ビデオハングアウトを社内の人がどれくらい使っているかを調べる

Admin SDK の Reports API が [GAS からでも使えるようになりました](http://googleappsdeveloper.blogspot.jp/2013/12/more-google-services-available-in-apps.html)。
そこで、Google+ビデオハングアウトのドメイン内ユーザーの利用状況を、GASで取得するものを作ってみました。

# 何故作ったか

Google+ビデオハングアウトで、手軽にビデオ会議ができますよね。
ビデオハングアウトは会社でも結構使われています。
会社の出張旅費などを削減できている筈です。
しかし、今まではビデオハングアウトの利用状況のログを取れなかったので、どれくらいの人が使っているのかの、まったくわかりませんでした。

## 参考にしたサイト
▼Customer Usage の Google+ Parameters 
[Reports API: Customer Usage – Google+ Parameters](https://developers.google.com/admin-sdk/reports/v1/reference/usage-ref-appendix-a/customers-gplus?hl=ja)

▼ user usage report のサンプルコード を参考にして（ほとんどコピペ）、Customer Usage reports 用に書き換えました。
[Generate user usage report](https://developers.google.com/apps-script/advanced/admin-sdk-reports#generate_user_usage_report)

## サンプルの実行方法

1. 以下のサンプルコードをスクリプトエディタにコピペして保存します。
1. APIを許可してください。

▼  APIを許可する方法は、以下の通りです。  

1. スクリプトエディタで、[リソース]  > [Advanced Google services....] を選択。
1. 表示されたダイアログで、[Admin Reports API] を [ON] にする。
1. ダイアログ画面の下にある、[Google Developers Console.] のリンクをクリック。
1. 新しく開いたコンソール画面で、[Admin DSK]  の横にある ON/OFF スイッチをクリック。
1. スクリプトエディタに戻り、ダイアログで [OK] をクリック。有効になった Advanced Google services.が自動補完で利用できるようになる。

* ご参考： [Enabling advanced services](https://developers.google.com/apps-script/guides/services/advanced#enabling_advanced_services)

* スクリプトを実行すると、Googleドライブのマイドライブに 「Google+ Customer Usage Report(yyyy-MM-dd_yyyy-MM-dd) 」 という名前のスプレッドシートが作成されます。
* スプレッドシートには、過去90日間(PST)のGoogle+ビデオハングアウトの利用状況と、Google+のアクティブユーザー数が記録されます。

## サンプルコード

```code.gs
/*
Google+ビデオハングアウト 利用状況取得スクリプト

Admin SDK Reports Service
https://developers.google.com/apps-script/advanced/admin-sdk-reports

Google+パラメータ
https://developers.google.com/admin-sdk/reports/v1/reference/usage-ref-appendix-a/customers-gplus?hl=ja
*/
function generateCustomerUsageReport_Gplus() {
  var reportRows = [];
  var reportData = [];
  var today = new Date();
  var n = 4;  // 4日前からの情報を取得、3日前だとエラーになる場合がある
  var kikan = 90 + n;
  var timezone = Session.getTimeZone();
  var fourDaysAgo = new Date(today.getTime() - n * 24 * 60 * 60 * 1000);
  for (n; n <= kikan; n++) {
    var nDaysAgo = new Date(today.getTime() - n * 24 * 60 * 60 * 1000);
    var date = Utilities.formatDate(nDaysAgo, timezone, 'yyyy-MM-dd');
    var reportRows = getReport(date);
    if (reportRows.length > 0) {
      for (var i = 0; i < reportRows.length; i++) {
        reportData.push(reportRows[i]);
      }
    }
  }

  if (reportRows.length > 0) {
    var startDate = Utilities.formatDate(fourDaysAgo, timezone, 'yyyy-MM-dd');
    var spreadsheet = SpreadsheetApp.create('Google+ Customer Usage Report(' + startDate + '_' + date + ')');
    var sheet = spreadsheet.getActiveSheet();

    // Append the headers.
    var headers = ['日付(PST)',
                   'ビデオハングアウト開催数',
                   '参加者数合計',
                   '参加者数平均',
                   '会議時間合計(分)',
                   '会議時間平均(分)',
                   'G+:1日のアクティブユーザー数',
                   'G+:7日間のアクティブユーザー数',
                   'G+:30日間のアクティブユーザー数'];
    sheet.appendRow(headers);

    // Append the results.
    sheet.getRange(2, 1, reportData.length, headers.length).setValues(reportData);
    sheet.setColumnWidth(1, 80);
    sheet.setColumnWidth(2, 90);
    sheet.setColumnWidth(3, 90);
    sheet.setColumnWidth(4, 90);
    sheet.setColumnWidth(5, 90);
    sheet.setColumnWidth(6, 90);
    sheet.setColumnWidth(7, 90);
    sheet.setColumnWidth(8, 90);
    sheet.setColumnWidth(9, 90);
    sheet.getRange('A1:I1').setFontWeight('bold').setBackground('#ddddee').setVerticalAlignment('top');
    sheet.setFrozenRows(1);
    sheet.setFrozenColumns(1);

    Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
  } else {
    Logger.log('No results returned.');
  }
}

function getReport(date) {
  var parameters = [
    'gplus:num_video_conferences',
    'gplus:num_video_calls',
    'gplus:total_video_call_minutes',
    'gplus:num_1day_active_users',
    'gplus:num_7day_active_users',
    'gplus:num_30day_active_users'

  ];
  var rows = [];
  var pageToken, page;
  do {
    page = AdminReports.CustomerUsageReports.get(date, {
      parameters: parameters.join(','),
      maxResults: 500,
      pageToken: pageToken
    });
    var reports = page.usageReports;
    if (reports) {
      for (var i = 0; i < reports.length; i++) {
        var report = reports[i];
        var parameterValues = getParameterValues(report.parameters);
        var row = [report.date];
        if (isFinite(parameterValues['gplus:num_video_conferences'])) {
          row.push(parameterValues['gplus:num_video_conferences']);
          row.push(parameterValues['gplus:num_video_calls']);
          row.push(Math.ceil(parameterValues['gplus:num_video_calls'] / parameterValues['gplus:num_video_conferences']));
          row.push(parameterValues['gplus:total_video_call_minutes']);
          row.push(Math.ceil(parameterValues['gplus:total_video_call_minutes'] / parameterValues['gplus:num_video_calls']));
        } else {
          row.push(null);
          row.push(null);
          row.push(null);
          row.push(null);
          row.push(null);
        }
        if (isFinite(parameterValues['gplus:num_1day_active_users'])) {
          row.push(parameterValues['gplus:num_1day_active_users']);
        } else {
          row.push(null);
        }
        if (isFinite(parameterValues['gplus:num_7day_active_users'])) {
          row.push(parameterValues['gplus:num_7day_active_users']);
        } else {
          row.push(null);
        }
        if (isFinite(parameterValues['gplus:num_30day_active_users'])) {
          row.push(parameterValues['gplus:num_30day_active_users']);
        } else {
          row.push(null);
        }

        rows.push(row);
      }
    } else {
      rows.push([date,null,null,null,null,null,null,null,null]);
    }
    pageToken = page.nextPageToken;
  } while (pageToken);

  return rows; 
}

/**
 * Gets a map of parameter names to values from an array of parameter objects.
 * @param {Array} parameters An array of parameter objects.
 * @return {Object} A map from parameter names to their values.
 */
function getParameterValues(parameters) {
  return parameters.reduce(function(result, parameter) {
    var name = parameter.name;
    var value;
    if (parameter.intValue !== undefined) {
      value = parameter.intValue;
    } else if (parameter.stringValue !== undefined) {
      value = parameter.stringValue;
    } else if (parameter.datetimeValue !== undefined) {
      value = new Date(parameter.datetimeValue);
    } else if (parameter.boolValue !== undefined) {
      value = parameter.boolValue;
    }
    result[name] = value;
    return result;
  }, {});
}
```