BI

[ガラケー版(QRコード)]
アクセス記録[推移 / PV内訳(過去1日 / 過去1週間) / 外部アクセス元 (昨日 / 過去1週間) / ログイン論客足跡]
プロフィール私書(メール)
   /   /送済
評価(一覧   /)
投票   /共:   /
ファン登録
作品/情報/
DB構築()
ブログ
[書く]
攻略記事リンク集
My Play List
 作成日時分類記事タイトル
12015/03/16BIGoogle Apps ScriptでGoogle ..
22012/10/18BI統計::本日10/18は統計の日..
32012/10/14BI統計::日本の都道府県別人口分布(2011/10/0..
 反応日時来客名来客者の最近のメッセージ
12017/05/11uyam度々、すごーく、すいません。こんばんわ!uyamです。先日、..
22017/05/11オルタフォースお帰りなさいませ。そして、諸問題への対処お疲れさまでした。毎..
32017/04/20 非論客コメント
42017/02/25Merciこんばんは。サーバー移転後からだと思いますが、以前は見られた..
52017/02/17ねこじゃらしブログ投稿やコメントをしようとすると、たまにエラーになります..
その他最近のコメント
1.
2015/03/16 BI > Google Apps ScriptでGoogle AnalyticsとCalendarとBacklogの結果をまとめてKPI&PJ管理のデータ更新を自動化する」
[この書込みのみ表示(記事URL紹介用) / 編集 / 削除 / トラバ送信 / 共有分類に追加(タグ付け)]

1. 「Google Apps ScriptでGoogle AnalyticsとCalendarとBacklogの結果をまとめてKPI&PJ管理を自動化する」理由
2. 活用するAPIの簡単な説明
    1. Backlog API
        1. APIキーの取得
        2. プロジェクトIDの取得
        3. 見たいプロジェクトの$DATEを期限日としたタスクで、完了したものを得るAPI
        4. レポートの為のBacklogの運用上の注意
    2. Google Calendar
        1. レポートに合わせた運用方法
        2. カレンダーIDを得る方法
    3. Google AnalyticsのIDを得る方法
    4. Google Apps Scriptのマニュアル:「Spread Sheet」「Analytics」「Calendar」「Embedded Chart」「External API」
3. 上記の情報とAPIを活用して、Google Spread Sheetでサイト関連のベースとなるKPIを作るGoogle Apps Scriptを作成
    1. スプレッドシートを開いてからのGoogle Apps Scriptの追加方法
    2. 生成の為のScript内のカスタマイズ項目の説明
        1. PJに対応したAnalytics IDとBacklog Project ID
        2. APIキー等
        3. default_backlog_user
        4. 表示する期間の開始日
        5. holiday_is_holidayの真偽値
        6. holiday_list_only_for_youへ自分固有の休日の設定
    3. スクリプトの実行
    4. Google Apps Script for 「サイト実績+各人タスク実績自動更新統合簡易KPI」の例

1. 「Google Apps ScriptでGoogle AnalyticsとCalendarとBacklogの結果をまとめてKPI&PJ管理を自動化する」理由

事務作業はクリエィティヴな作業ではない。
自動化しないのなら事務作業をする時間が、クリエイターの本業を殺すだろう。

ということで、サイト制作関連のKPIをプロジェクト横断で自動更新できるようにする事を目的として、Google Spread Sheetに

IDデータ項目
1Google AnalyticsのUU
2Google AnalyticsのPV
3Google AnalyticsのPV/UU
4Google Analyticsの直帰率
5Google Analyticsのロード時間
6Backlogの完了チケット数
7Backlogの完了チケットタイトル
8Backlogの処理中チケット数
9Backlogの処理中チケットタイトル
10Google Calenderの関連イベント

を、PJ単位、PJ横断でユーザー単位で、Google Spread Sheet上にレポートを自動的に作れるGoogle Apps Scriptを作ってみた。
土日祝日の色付けや、チャート&トレンド曲線の表示にも対応。
2. 活用するAPIの簡単な説明


    1. Backlog API

https://developer.nulab-inc.com/ja/docs/backlog/api/2/get-issues
のAPI機能を使う。
      1. APIキーの取得

APIを利用させて貰うにはAPIキーの取得が必要ですが、「個人設定」ページの左下に「API」というメニューがあるので、そこで取得します。
      2. プロジェクトIDの取得

「プロジェクトの設定」をクリックした時のURLで確認する事が出来ます。
https://1stclass.backlog.jp/EditProject.action?project.id=3335

また、API経由だと
https://1stclass.backlog.jp/api/v2/projects?apiKey=$APIKEY
という形でリクエストを投げると、JSON形式で確認する事が出来ます。
      3. 見たいプロジェクトの$DATEを期限日としたタスクで、完了したものを得るAPI

例として2015-03-09 00:00:00 - 2015-03-10 00:00:00の間のものを得たい時
https://1stclass.backlog.jp/api/v2/issues?statusId[]=4&projectId[]=3335&dueDateSince=2015-03-09&dueDateUntil=2015-03-10&apiKey=$APIKEY
JSON形式で結果が返ってくるので、この結果をGoogle Apps ScriptのExternal APIを使って、取り込みます。
なお、表示数に上限があるので、offsetとかを活用して、ページめくりをしていく必要がプログラムの作成上ではあります。
      4. レポートの為のBacklogの運用上の注意

運用上では、Backlogには完了というステータスはあっても、完了日という記入欄はデフォルトではないので、完了または処理済みのステータスとなったチケットの期限日を、完了日として扱っている事になります。
なので、期限を過ぎて完了したチケットは、期限を完了した日付に合わせるように変更しておくように運用して下さい。
期限より前に終わった場合には、期限を実際の完了日に合わせて下さい。
    2. Google Calendar


      1. レポートに合わせた運用方法

プロジェクト名:○○○
と書いたものを、各PJに紐付いたイベント名として引っ張ってくるようになっているので、Google CalendarにはそのPJの特筆すべきリリース項目をそのような形で書いていって下さい。
該当PJの「リリース」の列に該当日にそれが表示されるようになります。
      2. カレンダーIDを得る方法

歯車のようなアイコンを押して使うカレンダーの設定画面に行き、そこからカレンダー名のリンクを押すと、
「カレンダーのアドレス」という項目の右に  (カレンダー ID: .................)
というのが表示されているので、それをコピー。
なお、Google Apps Scriptを実行するアカウントがそのカレンダーを閲覧する権限が与えられている必要がある。
    3. Google AnalyticsのIDを得る方法

Analyticsのレポート画面に入った時のURLの末尾の数字の部分を抽出する。

例:
https://www.google.com/analytics/web/?hl=ja&pli=1#report/visitors-overview/a103300w120308p96186554/

太字の部分
なお、Google Apps Scriptを実行するアカウントがそのレポートを閲覧する権限が与えられている必要がある。
    4. Google Apps Scriptのマニュアル:「Spread Sheet」「Analytics」「Calendar」「Embedded Chart」「External API」

Google Apps Scriptの関連API文章を参照しながら開発をする必要があります。

Analyticsは
https://developers.google.com/apps-script/advanced/analytics

Spread Sheetは
https://developers.google.com/apps-script/reference/spreadsheet/

Calendarは
https://developers.google.com/apps-script/advanced/calendar

Embedded Chartは
https://developers.google.com/apps-script/reference/spreadsheet/embedded-chart

External APIは
https://developers.google.com/apps-script/guides/services/external?hl=ja

Google Apps Scriptの初歩的な日本語での勉強としては、まず初めにはDotinstallとかが使えます。
http://dotinstall.com/lessons/basic_google_apps_script

なお、この中では、特にEmbedded Chartが公式文章にも載っていない方法が多くありました(手動で作ったチャートを公開して、そのJsonからリバースエンジニアリングして、使えそうなオプションを探していくという方法でその情報を得る事が出来ました)。
3. 上記の情報とAPIを活用して、Google Spread Sheetでサイト関連のベースとなるKPIを作るGoogle Apps Scriptを作成


    1. スプレッドシートを開いてからのGoogle Apps Scriptの追加方法

スプレッドシートを開き、メニューのツールから、スクリプトエディターを選んで、Google Spread SheetのCustom Functionを作るを選んで、そこに作ったGoogle Apps Scriptを保存&実行する。
    2. 生成の為のScript内のカスタマイズ項目の説明


      1. PJに対応したAnalytics IDとBacklog Project ID

pj_settings = [{...}, {...}]
の中に、Google AnalyticsとBacklogのPJ情報を記述します。

Google Analyticsはそれぞれに対応したViewがあるので、この例ではそれぞれのIDを入れていますが、Backlogは無料版では一つしかPJが作れないので、今回のこの例では無料版を使っているので、allの所にだけBacklogのIDを入れています。
Backlogの有料版を使っていれば、複数のPJが使えるので、それぞれ他のPJにも対応したものを入れて、逆にallの方にはbacklogのPJ IDは入れないでしょうが。

逆にBacklogの方だけあって、Google Analyticsの対応するViewが無いという場合には、Google AnalyticsのIDを空にして、BacklogのPJ IDの方だけ入れて下さい。
      2. APIキー等

backlog_url_part
backlog_apikey
calendarId
の値をセットして下さい。
backlogを利用していないのならば、backlog系の値は空にしておいて大丈夫です。
      3. default_backlog_user

backlogで実行したユーザー名が空の時セットされるユーザー名をセットして下さい。
空にしておけば、Unknownというユーザーにそのチケットは割り当てられます。
      4. 表示する期間の開始日

各PJの開始の日付(start_date_str)には、自分の必要な必要な計測開始日を入れて下さい。
当然昔になればなるほど、データ更新に時間は当然かかります。
共通ので良ければ、common_start_date の値が使われるので、それを調整して下さい。
      5. holiday_is_holidayの真偽値

holiday_is_holidayはもし土日祝が休日かつその日にはチケットが完了する事がない場合には、trueをセットして下さい。
各人の完了チケットまとめで、土日祝がスキップされます。
      6. holiday_list_only_for_youへ自分固有の休日の設定

自分固有の休日(=絶対に働かない日)がある場合には、そこに値を定義して下さい。
holiday_is_holidayの値がtrueならば、Workersシートにてその日付が休日としてスキップされます。
    3. スクリプトの実行

まず最初にスクリプトエディターでセレクトボックスから選ぶ実行する関数でonOpenを選び、実行して下さい。
これにより、各シートのメニューに「Custom Menu」という項目が追加されます。

あとは、追加された「Custom Menu」の中の「Update Data」をクリックすれば、自動的にAnalyticsとBacklogとCalendarを活用した、PJとメンバー横断の簡易サイトKPI・タスク完了状況・リリース情報が自動更新されます。

または、run()という関数をスクリプトエディターから実行します。
トリガーというボタンがありますが、そこでrun()という関数を1時間毎に更新するという設定にすれば、cron的に1時間毎に更新してくれます。

最終的な実行には
https://code.google.com/apis/console
で、Google Calendar APIとAnalytics APIの実行の許可を得ておく必要があります。

なお、Google Apps Scriptの実行で問題が起きる場合には、Logger.log(.....);という記述で、変数の中身を実行ログに記録できるので、それで原因を追っていくと良いでしょう。
Google Script Editorでは、メニューの「表示」→「ログ」で、実行が終わった後、確認する事が出来ます。
    4. Google Apps Script for 「サイト実績+各人タスク実績自動更新統合簡易KPI」の例

  /*
  Script was made at http://sakuhindb.com/pj/6_B4C9CDFDBFCDA4B5A4F3/20150316.html by Hajime Kurita
  
  This will make a report combining the data of 
  - google analytics, 
  - your google calendar
  - google Japanese holiday calendar
  - backlog's done and doing tickets
  with charts which have trend line.
  You can see each member's task all over the projects.
  
  To make this apps script available, you have to do the following steps.
  
  Step 1. Make spread sheets named as PJ and "Dones" and "Doings"
  Step 2. Adjust parameters in the customizable section. You also have to make API availeble for the account.
  Step 3. Run function onMenu() and add new menu to the menu bar
  Step 4. Choose "Custom Menu" and click "Update Data"
  
  */

//////////////////// Start part for customization ///////////////////
  backlog_url_part = '1stclass';
  backlog_apikey = 'exampleeeeeeee_backlogapikey'; // Just Example. Please modify or make it blank
  calendarId = 'exampleeeeeeee@gmail.com';// Just example. Please modify it.
  default_backlog_user = 'hajimekurita'; // Just example. Please modify it
  holiday_google_calender_id = "ja.japanese#holiday@group.v.calendar.google.com"; // If you are not Japanese, please modify it.

  common_start_date = "2015/01/01"; // Just examle. Plese modify it.
  holiday_is_holiday = false; // if holiday is holiday for workers, please make it true. Then holidays will be skipped in Workers' sheet.
  holiday_list_only_for_you = {
  /* "2015/10/01":1,
  "2015/09/01":1,
  */
  }; /* Please set holidays only for you to skip in Workers' report */

pj_settings = [
  {pj:'all',analytics_profile_id:96186554, backlog_project_id:3335},
  {pj:'sakuhindb',analytics_profile_id:71818, backlog_project_id:''},
  {pj:'ensakuhindb',analytics_profile_id:96188472, backlog_project_id:''},
  {pj:'minakoe',analytics_profile_id:96199604, backlog_project_id:''},
  {pj:'find',analytics_profile_id:96177576, backlog_project_id:''},
  {pj:'ryokoukeikaku',analytics_profile_id:106873884, backlog_project_id:''}
];


//////////////////// End part for customization ///////////////////
  date2backlog_user_done_jobs = {};
  tanto2backlog_user_doing_jobs = {};
  date2release_events = {};
  date2holiday_events = {};
  date2jp_holidays = {};

  function run(){
    Logger.log("Start: Run: "+toLocaleDate(new Date()));
    
    /////////////////////////// Get Data /////////////////////////
    Logger.log("Start: Getting Calendar 1: "+toLocaleDate(new Date()));
    date2release_events = get_events(calendarId, common_start_date);  
    Logger.log("End: Getting Calendar 1: "+toLocaleDate(new Date())+"\n");

    /////////////////////////// Get Data /////////////////////////
    Logger.log("Start: Getting Calendar 2: "+toLocaleDate(new Date()));
    date2holiday_events = get_events(holiday_google_calender_id, common_start_date);
    Logger.log("End: Getting Calendar 2: "+toLocaleDate(new Date())+"\n");    
    date2jp_holidays = extract_events(date2holiday_events, "");
    
    for(pj_i=0;pj_i < pj_settings.length;pj_i++){
      create_report(pj_settings[pj_i]);
    }
    create_report({pj:'Dones',analytics_profile_id:'', backlog_project_id:''});
    create_report({pj:'Doings',analytics_profile_id:'', backlog_project_id:''});
  
    Logger.log("End: Run: "+toLocaleDate(new Date()));
  }

  function onOpen() {
    return showMenu();
  }

  showMenu = function() {
    menu = [
      {
        name: "Update Data",
        functionName: "run"
      }
    ];
    return SpreadsheetApp.getActiveSpreadsheet().addMenu("Custom Menu", menu);
  };

  create_report = function(obj) {
    pj = obj.pj;
    
    Logger.log("###########"+"\n"+"PJ: "+pj+" / "+toLocaleDate(new Date()));
    
    profileId = obj.analytics_profile_id;
    backlog_project_id = obj.backlog_project_id;
    if(obj.start_date){
      start_date_str = obj.start_date;
    }
    else{
      start_date_str = common_start_date;
    }
    event_match_str = pj;
    hiduke=new Date(); 
    year = hiduke.getFullYear();
    month = hiduke.getMonth()+1;
    week = hiduke.getDay();
    day = hiduke.getDate();
    if(month < 10){
      month = '0'+month;
    }
    if(day < 10){
      day = '0' + day;
    }
    end_date_str = year+'/'+month+'/'+day;

    start_time_str = start_date_str + ' 00:00:00';
    start_date = new Date(start_time_str);
    
    date2events = extract_events(date2release_events, event_match_str);
    Logger.log(date2events);
    
    end_time_str = end_date_str + ' 23:59:59';
    end_date = new Date(end_time_str);
    
    date_cursor = end_date;
    sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(pj);
    
    if(!sheet){
      SpreadsheetApp.getActiveSpreadsheet().insertSheet(pj);
      sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(pj);
    }
    line_num = 0;
    line_num++;
    item_names = [];
    if(pj == 'Dones'){
      item_names[0] = ["Date", "Day"];
      for(in_user_name in date2backlog_user_done_jobs){
        item_names[0].push(in_user_name);
        item_names[0].push(" ");
      }
    }
    else if(pj == 'Doings'){
      for(in_user_name in tanto2backlog_user_doing_jobs){
        item_names[0] = [in_user_name, " "];
      }
      if(!item_names[0]){
         item_names[0] = [" ", " "];
      }
    }
    else{
      item_names[0] = ["Date", "Day", "UU", "PV", "PV/UU", "Bounce Ratio", "Load Time", "Release", "Done Tickets", "Tickets"];
    }
    if(item_names[0]){
      last_column = item_names[0].length;
    }
    tableId = 'ga:' + profileId;
    metric = 'ga:users,ga:pageviews,ga:bounceRate,ga:avgPageLoadTime';
    options = {
      'dimensions': 'ga:date',
      'sort': '-ga:date'
    };
    sheet.getRange(line_num, 1, item_names.length, last_column).setValues(item_names).setBackgroundColor('#000000').setFontColor('#FFFFFF');
    
    in_startDate = Utilities.formatDate(date_cursor, Session.getTimeZone(), start_date.getFullYear() + '-' + toDoubleDigits(start_date.getMonth() + 1) + "-" + toDoubleDigits(start_date.getDate()));

    backlog_end_date = end_date;
    backlog_end_date.getDate()+1;
    backlog_end_date.setDate(backlog_end_date.getDate()+1);
    backlog_end_date_str = Utilities.formatDate(backlog_end_date, Session.getTimeZone(), "yyyy-MM-dd");
    backlog_end_date_str2 = Utilities.formatDate(backlog_end_date, Session.getTimeZone(), "yyyy/MM/dd");
    
    end_date_str = Utilities.formatDate(end_date, Session.getTimeZone(), "yyyy-MM-dd");

    line_num++;
    if(profileId){
      /////////////////////////// Get GA Data /////////////////////////
      Logger.log("Start: Getting GA: "+toLocaleDate(new Date()));
      report = Analytics.Data.Ga.get(tableId, in_startDate, end_date_str, metric, options);
      Logger.log("End: Getting GA: "+toLocaleDate(new Date())+"\n");
    }
    

    pgsz = 100;
    date2backlog_finished_cnt = new Array();
    date2backlog_finished_tickets = new Array();
    if(backlog_project_id){
      // Get Done tickets
      offset = 0;
      while (1 === 1) {
        backlog_url = 'https://' + backlog_url_part + '.backlog.jp/api/v2/issues?offset=' + offset + '&statusId[]=3&statusId[]=4&count=' + pgsz + '&projectId[]=' + backlog_project_id + '&dueDateSince=' + in_startDate + '&dueDateUntil=' + backlog_end_date_str + '&apiKey=' + backlog_apikey;
        Logger.log(backlog_url);

        /////////////////////////// Get Data /////////////////////////
        Logger.log("Start: Getting Backlog1: "+toLocaleDate(new Date()));
        response = UrlFetchApp.fetch(backlog_url);
        Logger.log("End: Getting Backlog1: "+toLocaleDate(new Date())+"\n");

        json = response.getContentText();
        data = JSON.parse(json);
        for (i = 0; i < data.length; i++) {
          in_data = data[i];
          in_date_str = in_data.dueDate.replace(/T.*/, "");
          in_updated_str = in_data.updated.replace(/T.*/, "");
          // If updated is older than due date, use updated as finished date
          if(in_updated_str.replace(/\-/g, '') < in_date_str.replace(/\-/g, '')){
            in_date_str = in_updated_str;
          }
          
          tanto = '';
          if(in_data.assignee){
            if(in_data.assignee.name){
              tanto = in_data.assignee.name;
            }
          }
          title = in_data.summary;
          
          if(!tanto && default_backlog_user){
            tanto = default_backlog_user;
          }
          if (date2backlog_finished_cnt[in_date_str]) {
            date2backlog_finished_cnt[in_date_str]++;
            if (in_data.summary) {
              date2backlog_finished_tickets[in_date_str] += '\n' + tanto + ': ' + title;
            }
          } else {
            date2backlog_finished_cnt[in_date_str] = 1;
            if (in_data.summary) {
              date2backlog_finished_tickets[in_date_str] = tanto + ': ' + title;
            }
          }
          
          if(!date2backlog_user_done_jobs[tanto]) {
            date2backlog_user_done_jobs[tanto] = {};
          }
          
          title = pj + ':' + title;
          if(!date2backlog_user_done_jobs[tanto][in_date_str]) {
            date2backlog_user_done_jobs[tanto][in_date_str]=[title];
          }
          else{
            date2backlog_user_done_jobs[tanto][in_date_str].push(title);
          }
        }
        if (data.length < (offset + pgsz)) {
          break;
        }
        offset += pgsz;
      }
      
      // Get Doing tickets
      offset = 0;
      while (1 === 1) {
        backlog_url = 'https://' + backlog_url_part + '.backlog.jp/api/v2/issues?offset=' + offset + '&statusId[]=2&count=' + pgsz + '&projectId[]=' + backlog_project_id + '&dueDateSince=' + in_startDate + '&dueDateUntil=' + backlog_end_date_str + '&apiKey=' + backlog_apikey;
        Logger.log(backlog_url);
        ///////////////// Get Data ///////////////////
        Logger.log("Start: Getting Backlog2: "+toLocaleDate(new Date()));
        response = UrlFetchApp.fetch(backlog_url);
        Logger.log("End: Getting Backlog2: "+toLocaleDate(new Date())+"\n");
        
        json = response.getContentText();
        data = JSON.parse(json);
        for (i = 0; i < data.length; i++) {
          in_data = data[i];
          tanto = '';
          if(in_data.assignee){
            if(in_data.assignee.name){
              tanto = in_data.assignee.name;
            }
          }
          title = in_data.summary;
          
          if(!tanto && default_backlog_user){
            tanto = default_backlog_user;
          }
          
          if(!tanto2backlog_user_doing_jobs[tanto]) {
            tanto2backlog_user_doing_jobs[tanto] = [];
          }
          title = pj + ':' + title;
          if(!tanto2backlog_user_doing_jobs[tanto]) {
            tanto2backlog_user_doing_jobs[tanto]=[title];
          }
          else{
            tanto2backlog_user_doing_jobs[tanto].push(title);
          }
        }
        if (data.length < (offset + pgsz)) {
          break;
        }
        offset += pgsz;
      }
    }
    

    if(!profileId){
      Logger.log("Start: Making GA Data: "+toLocaleDate(new Date()));
      Report = function () {
        this.rows = [];
      };
  
      report = new Report();
      date_start_date = start_date;
      date_end_date = end_date;
      cursor_date_obj = date_end_date;
      i = 0;
      while(Utilities.formatDate(cursor_date_obj, Session.getTimeZone(), "yyyyMMdd") >= Utilities.formatDate(date_start_date, Session.getTimeZone(), "yyyyMMdd")){
        report.rows[i] = [];
        report.rows[i][0] = Utilities.formatDate(cursor_date_obj, Session.getTimeZone(), "yyyyMMdd");
        cursor_date_obj.setDate(cursor_date_obj.getDate()-1);
        i++;
      }
      Logger.log("End: Making GA Data: "+toLocaleDate(new Date())+"\n");
    }
    
    line_datas = [];
    last_loc = 0;
      
    if(pj == 'Doings'){
      Logger.log("Start: Drawing Doing Data of backlog: "+toLocaleDate(new Date()));
      loc_num = 0;
      for(in_user_name in tanto2backlog_user_doing_jobs){
        if(tanto2backlog_user_doing_jobs[in_user_name]){
          loc_num++;
          sheet.getRange(line_num, loc_num).setValue(tanto2backlog_user_doing_jobs[in_user_name].length);
          loc_num++;
          sheet.getRange(line_num, loc_num).setValue(tanto2backlog_user_doing_jobs[in_user_name].join("\n"));
        }
        else{
          loc_num++;
          sheet.getRange(line_num, loc_num).setValue(0);
          
          loc_num++;
          sheet.getRange(line_num, loc_num).setValue(" ");
        }
      }
      
      loc_num++;
      while(sheet.getRange(line_num, loc_num).getValue()){
        sheet.getRange(line_num, loc_num).setValue("");
        loc_num++;
      }
      Logger.log("End: Drawing Doing Data of backlog: "+toLocaleDate(new Date())+"\n");
    }
    else if (report && report.rows) {
      Logger.log("Start: Drawing Data: "+toLocaleDate(new Date())+ " / length = "+report.rows.length);
      start_line_num = line_num;
      in_i = 0;
      for (i = 0; i< report.rows.length; i++) {
        line_datas[in_i] = [];
        loc_num = 0;
        in_date_str = report.rows[i][0];
        in_year = in_date_str.substr(0, 4);
        in_month = in_date_str.substr(4, 2);
        in_mday = in_date_str.substr(6, 2)
        show_date = in_year + '/' + in_month + '/' + in_mday;
        bar_show_date = in_year + '-' + in_month + '-' + in_mday;
        date = new Date(in_year+"/"+in_month+"/"+in_mday);
        
        if(show_date == backlog_end_date_str2){
          continue;
        }

        wday = '';
        if(date.getDay() == 0){
          if(pj == 'Dones' && holiday_is_holiday){
            continue;
          }
          sheet.getRange(line_num, 1, 1, last_column).setBackgroundColor('#FFAAAA');
          sheet.getRange(line_num, 2).setFontColor('#FF0000');
          wday = '日';
        }
        else if(date.getDay() == 6){
          if(pj == 'Dones' && holiday_is_holiday){
            continue;
          }
          sheet.getRange(line_num, 1, 1, last_column).setBackgroundColor('#AAAAFF');
          sheet.getRange(line_num, 2).setFontColor('#0000FF');
          wday = '土';
        }
        else if(date2jp_holidays[show_date]){
          if(pj == 'Dones' && holiday_is_holiday){
            continue;
          }
          sheet.getRange(line_num, 1, 1, last_column).setBackgroundColor('#FFAAAA');
          sheet.getRange(line_num, 2).setFontColor('#FF0000');
          wday = '祝';
        }
        else if(holiday_list_only_for_you[show_date]){
          if(pj == 'Dones' && holiday_is_holiday){
            continue;
          }
          sheet.getRange(line_num, 1, 1, last_column).setBackgroundColor('#FFFFAA');
          sheet.getRange(line_num, 2).setFontColor('#888800');
          wday = '休';
        }
        else{
          if(date.getDay()  == 1){
            wday = '月';
          }
          else if(date.getDay()  == 2){
            wday = '火';
          }
          else if(date.getDay()  == 3){
            wday = '水';
          }
          else if(date.getDay()  == 4){
            wday = '木';
          }
          else if(date.getDay()  == 5){
            wday = '金';
          }
          sheet.getRange(line_num, 1, 1, last_column).setBackgroundColor('#FFFFFF');
          sheet.getRange(line_num, 2).setFontColor('#000000');
        }
        
        loc_num++;
        line_datas[in_i].push(show_date);
        
        loc_num++;
        line_datas[in_i].push(wday);
        
        if(pj == 'Dones'){
          for(in_user_name in date2backlog_user_done_jobs){
           if(date2backlog_user_done_jobs[in_user_name] && date2backlog_user_done_jobs[in_user_name][bar_show_date]){
             loc_num++;
             line_datas[in_i].push(date2backlog_user_done_jobs[in_user_name][bar_show_date].length);
              
             loc_num++;
             line_datas[in_i].push(date2backlog_user_done_jobs[in_user_name][bar_show_date].join("\n"));
           }
            else{
              loc_num++;
              line_datas[in_i].push(0);
              
              loc_num++;
              line_datas[in_i].push(" ");
            }
          }
        }
        else{
          uu = report.rows[i][1];
          pv = report.rows[i][2];
          bounce_rate = report.rows[i][3];
          avg_load_time = report.rows[i][4];
          pv_uu = 0;
          if (uu > 0 && pv > 0) {
            pv_uu = (pv / uu).toFixed(2);
          }
          if(pv_uu == 'NaN'){
            pv_uu = 0;
          }
          events_str = '';
          if(date2events && date2events[show_date]){
            events_str = date2events[show_date];
          }
          done_cnt = 0;
          if (date2backlog_finished_cnt[bar_show_date]) {
            done_cnt = date2backlog_finished_cnt[bar_show_date];
          }
          tickets = '';
          if(date2backlog_finished_tickets[bar_show_date]) {
            tickets = date2backlog_finished_tickets[bar_show_date];
          }
          
          loc_num++;
          line_datas[in_i].push(uu);
        
          loc_num++;
          line_datas[in_i].push(pv);
        
          loc_num++;
          line_datas[in_i].push(pv_uu);
        
          loc_num++;
          line_datas[in_i].push(Math.floor( bounce_rate * 100)/100);
        
          loc_num++;
          line_datas[in_i].push(Math.floor( avg_load_time * 100)/100);
        
          loc_num++;
          line_datas[in_i].push(events_str);
        
          loc_num++;
          line_datas[in_i].push(done_cnt);
          
          loc_num++;
          line_datas[in_i].push(tickets);
        }
        last_loc = loc_num;
        line_num++;
        in_i++;
      }

      Logger.log(start_line_num+", "+1+", "+line_datas.length+", "+line_datas[0].length);
      sheet.getRange(start_line_num, 1, line_datas.length, line_datas[0].length).setValues(line_datas);
      
      Logger.log("End: Drawing Data: "+toLocaleDate(new Date())+"\n");
      
      line_num--;
      
      chart_x = last_loc+1;
      chart_y = 3;
      chart_x_diff = 0;
      chart_y_diff = 16;
      chart_num = 0;
      
      
      
      if(pj != 'Dones' && pj != 'Doings'){
        Logger.log("End: Drawing Chart: "+toLocaleDate(new Date()));
        
        sheet.getRange(2, last_loc+1).setValue("※各種グラフは↓下方向にスクロールして確認");
        range1 = sheet.getRange("A2:A"+line_num);
        chart_list = {
          'C':'UU',
          'D':'PV',
          'E':'PV/UU',
          'F':'Bounce Ratio',
          'G':'Loaded time',
          'I':'Done Tickets'
        };
        
          
        for(chart_alph in chart_list){
          range = sheet.getRange(chart_alph+'2:'+chart_alph+line_num);
          graph_title = chart_list[chart_alph];
          chart = sheet.getCharts()[chart_num];
          if(!chart){
            chart = sheet.newChart()
            .asLineChart()
            .addRange(range1)
            .addRange(range)
            .setOption('useFirstColumnAsDomain', true)
            .setOption('title', graph_title)
            .setOption('animation.duration', 500)
            .setOption('usefirstcolumnasdomain', true)
            .setOption("trendlines", {"0":{"pointSize":0,"showR2":false,"labelInLegend":"Trend","degree":10,"visibleInLegend":true,"type":"polynomial","opacity":0.4,"lineWidth":2}})
            .setXAxisTitle("Date")
            .setPosition(chart_y,chart_x,0,0)
            .build();
            sheet.insertChart(chart);
          }
          else{
            chart = chart.modify()
            .asLineChart()
            .addRange(range1)
            .addRange(range)
            .setOption('useFirstColumnAsDomain', true)
            .setOption('title', graph_title)
            .setOption('animation.duration', 500)
            .setOption('usefirstcolumnasdomain', true)
            .setOption("trendlines", {"0":{"pointSize":0,"showR2":false,"labelInLegend":"Trend","degree":10,"visibleInLegend":true,"type":"polynomial","opacity":0.4,"lineWidth":2}})
            .setXAxisTitle("Date")
            .setPosition(chart_y,chart_x,0,0)
            .build();
            sheet.updateChart(chart);
          }
          chart_num++;
          chart_x += chart_x_diff;
          chart_y += chart_y_diff;
        }
        Logger.log("End: Drawing Chart: "+toLocaleDate(new Date()));
      }
    }
  };


  get_events = function(calendarId, start_time, match_str) {
    start_time_obj = new Date(start_time);
    events = Calendar.Events.list(calendarId, {
      timeMin: start_time_obj.toISOString(),
      singleEvents: true,
      orderBy: 'startTime',
      maxResults: 2500 /* max */
    });
    return events;
  };

  extract_events = function(events, match_str) {
    if (events.items && events.items.length > 0) {
      date2events = {};
      for (i = 0; i < events.items.length; i++) {
        event = events.items[i];
        if(!event.summary){
          continue;
        }
        if (event.start.date || event.start.dateTime) {
          type = 'date';
          start = parseDate(event.start.date);
          if(!start || start == "Invalid Date"){
            start = parseDate(event.start.dateTime);
            type = 'time';
          }
          
          start_obj = new Date(start);
          end = parseDate(event.end.date);
          if(!end || end == "Invalid Date"){
            end = parseDate(event.end.dateTime);
          }
          end_obj = new Date(end);
          
          cursor_obj = start_obj;

          if (match_str == "" || event.summary.indexOf(match_str+":") >= 0) {
            cursor_time = cursor_obj.getTime();
            while ( (type == 'date' && cursor_time < end_obj.getTime() )
              ||
                 (type == 'time' && cursor_time <= end_obj.getTime() )
              ) {
              if(match_str){
                title = event.summary.replace(new RegExp(match_str+':', ''), "");
              }
              else{
                title = event.summary;
              }
              date2events[toLocaleDate(new Date(cursor_obj))] = title;
              cursor_obj.setTime(cursor_obj.getTime() + 60 * 60 * 24 * 1000);
              cursor_time = cursor_obj.getTime();
            }
          }
        }
        else{
          Logger.log(event);
          return;
        }
      }
      return date2events;
    }
  };

  parseDate = function(string) {
    if(!string){
      return;
    }
    var parts;
    parts = string.split('T');
    parts[0] = parts[0].replace(/-/g, '/');
    return new Date(parts[0]+" 00:00:00");
  };

  toLocaleDate = function(date) {
    var mday, mon, year;
    year = date.getFullYear();
    mon = date.getMonth() + 1;
    mday = date.getDate() + '';
    mon += '';
    if (mon.length < 2) {
      mon = '0' + mon;
    }
    if (mday.length < 2) {
      mday = '0' + mday;
    }
    return year + '/' + mon + '/' + mday;
  };


  toDoubleDigits = function(num) {
    num += "";
    if (num.length === 1) {
      num = "0" + num;
    }
    return num;
  };


コメントする

2.
2012/10/18 BI > 統計 > 本日10/18は統計の日」
[この書込みのみ表示(記事URL紹介用) / 編集 / 削除 / トラバ送信 / 共有分類に追加(タグ付け)]拍手:3個

1. 10/18は統計の日
2. 世界の人口

    1. 10/18は統計の日


本日10/18は統計の日

本日知って、前の日本の都道府県別人口に関する記事は折角なら今日書けば良かった(> <)と思ったけど、新たに記事書いてみました。

何故本日10/18が統計の日なのかと言えば、
1973年(昭和48年)7月3日の閣議了解によって10月18日に制定された。

明治3年9月24日(グレゴリオ暦1870年10月18日)に、日本で初めての近代的生産統計である府県物産表に関する太政官布告が公布されたことが由来となっており、国民の統計の重要性に対する関心と理解を深め、統計調査への一層の協力を得ることを目的としている。

出典元: Wikipedia
との事です。
なるほど、日本国の近代化の過程の一つを、記念&意識高めの為に制定された日という事ですね。

とこで、日本の統計情報というのはどこから得れば良いものでしょうか?
統計情報は色々な所にありますが(例えばこのサイト作品データベースの作品の評価やランキングも統計の結果)、日本の統計情報の集積サービスとして見逃せないのは、やはり総務省統計局のサイトになります。

http://www.stat.go.jp/

国勢調査の結果をはじめとする日本の各種統計だけでなく、世界の統計も紹介されており、幅が広いです。
社会の統計数値については、小学校や中学の時に主に社会科で習っているので、ちょっと懐かしい気分にもなれますが、時代が経てばその統計値も変わっているのは当然ですね。
改めて、昔習った統計がどうなっているのか今見てみると、記憶力が良い人は、おお、自分の時代とはこう変わっているのか、とか感慨に浸れるかなと思います。
    2. 世界の人口

前の記事では、日本の都道府県の人口について見てみましたが、今回はちょっと世界の人口について見てみましょう。



中国は人口多いですけど、一人っ子政策とかもありますし、インドが抜くのもそう遠くはないですね。
日本は、東北大震災とかがあったとはいえ、高齢化&少子化の影響が大きく、前年度から人口は既に減る時代になっており、アジアでもフィリピン、ベトナムとかに人口数で抜かれるのも、時間の問題なのかなという状況になっていると思います。

何だかんだいってこのグラフを見て思うのは、中国とインド、やっぱ規模違うな、ってのが大きな印象ですね。
日本としては今回騒ぎになったように中国でのビジネスには色々な問題や難しさがありますが、それでも中国にビジネスとして進出を検討する企業が多くあるのは、ある意味国際化を考える上では、どうしても候補として上がってしまうだけの圧倒的な規模があるからですね。
日本のどこでビジネスをするのかで各都道府県の人口数を参考するのと同様に、どこの国に優先的に進出するのかというのでは当然国別人口数にも着目しなければいけないので、結果的にどうしても目は離せない国になります。
まあ、とはいえ一人あたりの稼ぎが国と国の間ではより大きく異なるので、GNPとか金額での指標の方が、海外進出ではより重要な指標として使う事にはなりますが。

コメントする3個

3.
2012/10/14 BI > 統計 > 日本の都道府県別人口分布(2011/10/01時点)」
[この書込みのみ表示(記事URL紹介用) / 編集 / 削除 / トラバ送信 / 共有分類に追加(タグ付け)]拍手:5個

1. 2011/10/01時点での日本の各都道府県別人口分布
2. 数値から見て取った事

1. 2011/10/01時点での日本の各都道府県別人口分布

2011/10/01時点での日本の各都道府県別人口分布です。
数値の所は x 1000人として見て下さい。

使い方は色々ですが(店舗展開ビジネス等)、こうした基本となる数値は頭の中に把握しながら、色々物事は考えていかなくては行けません。
ソースは総務省統計局より。

全体での比率が4%以上ある都道府県については赤色にさせて頂きました。
8都道府県が到達しています。
福岡県はギリギリ4%に届かず。

都道府県全体0~14歳15~6465歳以上内75歳以上全体0~14歳15~6465歳以上内75歳以上
合計142,50716,70581,34229,75214,708     
北海道6,1826503,4551,3826954.34%3.89%4.25%4.65%4.73%
青森県1,5491688403551861.09%1.01%1.03%1.19%1.26%
岩手県1,5101657913581961.06%0.99%0.97%1.20%1.33%
宮城県2,5973031,5035202711.82%1.81%1.85%1.75%1.84%
秋田県1,2541216343191800.88%0.72%0.78%1.07%1.22%
山形県1,3451476933211840.94%0.88%0.85%1.08%1.25%
福島県2,2682631,2255022781.59%1.57%1.51%1.69%1.89%
茨城県3,2843941,8886763262.30%2.36%2.32%2.27%2.22%
栃木県2,2242671,2864472241.56%1.60%1.58%1.50%1.52%
群馬県2,2422721,2504792411.57%1.63%1.54%1.61%1.64%
埼玉県7,8349494,7521,5066275.50%5.68%5.84%5.06%4.26%
千葉県6,8108014,0431,3705964.78%4.79%4.97%4.60%4.05%
東京都14,4911,4918,9922,7131,29510.17%8.93%11.05%9.12%8.80%
神奈川県9,8961,1846,0091,8658386.94%7.09%7.39%6.27%5.70%
新潟県2,7062971,4416243441.90%1.78%1.77%2.10%2.34%
富山県1,2401406612871520.87%0.84%0.81%0.96%1.03%
石川県1,3121587302791450.92%0.95%0.90%0.94%0.99%
福井県9151114902021120.64%0.66%0.60%0.68%0.76%
山梨県9711135322131130.68%0.68%0.65%0.72%0.77%
長野県2,4532921,2795713111.72%1.75%1.57%1.92%2.11%
岐阜県2,3232871,2805042521.63%1.72%1.57%1.69%1.71%
静岡県4,1945072,3399034452.94%3.04%2.88%3.04%3.03%
愛知県8,1081,0634,8241,5306915.69%6.36%5.93%5.14%4.70%
三重県2,0772511,1454512301.46%1.50%1.41%1.52%1.56%
滋賀県1,5612119082951471.10%1.26%1.12%0.99%1.00%
京都府2,9353331,6746243042.06%1.99%2.06%2.10%2.07%
大阪府9,7511,1635,6852,0128916.84%6.96%6.99%6.76%6.06%
兵庫県6,2127573,5211,3046304.36%4.53%4.33%4.38%4.28%
奈良県1,5581828743401621.09%1.09%1.07%1.14%1.10%
和歌山県1,1391265952741440.80%0.75%0.73%0.92%0.98%
鳥取県67377353155880.47%0.46%0.43%0.52%0.60%
島根県833914142071210.58%0.54%0.51%0.70%0.82%
岡山県2,2002631,1854932591.54%1.57%1.46%1.66%1.76%
広島県3,2063861,7766933512.25%2.31%2.18%2.33%2.39%
山口県1,6581828534072161.16%1.09%1.05%1.37%1.47%
徳島県898954732121180.63%0.57%0.58%0.71%0.80%
香川県1,1311316022581400.79%0.78%0.74%0.87%0.95%
愛媛県1,6301848573822071.14%1.10%1.05%1.28%1.41%
高知県881914472201230.62%0.54%0.55%0.74%0.84%
福岡県5,6556873,2481,1445763.97%4.11%3.99%3.85%3.92%
佐賀県9631235152091160.68%0.74%0.63%0.70%0.79%
長崎県1,6211918553712041.14%1.14%1.05%1.25%1.39%
熊本県2,0752491,0974672621.46%1.49%1.35%1.57%1.78%
大分県1,3661557183191740.96%0.93%0.88%1.07%1.18%
宮崎県1,2921576812931610.91%0.94%0.84%0.98%1.09%
鹿児島県1,9552321,0164502571.37%1.39%1.25%1.51%1.75%
沖縄県1,5282479122421271.07%1.48%1.12%0.81%0.86%

2. 数値から見て取った事

東京、大阪、愛知とその周辺の都道府県が人口が多い都道府県になるのは想定通りですね。
北海道と兵庫県についてはそんなにいるとは意識していませんでしたが。

東京は他都道府県に比べて、もっと人数いるかと思っていましたが(Webのリーチとかの指標を最近見てる事が多かったので)、住所ベースの統計で見てみるとそこまでの差を周辺都道府県につけれているわけでもないですね。
Web人口と住所ベースの人口の差は、勿論地域判別力の不正確さによる部分もありますが、やはり東京は通勤や通学で昼人口が多いというのが反映されているのかもしれません。
昼人口(平日人口)、夜人口(休日人口)はそれぞれ異なるものですが、そうした「状態」というのが大数的な統計値であっても、具現化している可能性を意識して数値は見なくてはいけないなとそのギャップを見ていて気付かされました。

また、やはり大都市圏より、そうでない都道府県の方が高齢化が進んではいますが、人数で言えばベースの大きさがそのまま効いて、序列としては大都市圏が一番高齢者数が多い都道府県である事には、全く変わりなかったのも印象的でした。

コメントする5個


管理人さん さんのコメント (2012/10/15) [編集/削除(書込み者/所有者が可能)]
こんにちは、非論客さん。

福岡県は惜しいですね!
どこかで聞かせて頂いていたどなたかの恋バナのようにあと一歩!
しかし届かず...
一体あの恋バナはどうなってしまったのでしょうか...



... 失礼しましたm(_ _)m
たらこさん、お元気そうで何よりです。
たらこさんにサイト名が分からんと言われていたのを思い出し(?)、
やっとこサイト名とドメイン名を合わせてsakuhindb.comになりました。

OBとして(既にOB扱い!?)、たまにはレインさんの充実青春ライフでも見に来て頂ければと(^^
ではでは、また。
非論客 プロバイダ: 8403 ホスト:8370 ブラウザ: 4783
なんか・・・・嫌な表があるぞぉ(笑)
(・・・・もともと専門が社会科だったりします(汗))

福岡おしい!
(だからど~したぁ)

この高齢者割合というのは・・・・人数に対するものなんですね。
いっしゅん、あれ?と思いました。
(ちゃんと説明文を読もう!)

~一句~
いつの日か わたしもきみも こうれいしゃ

・・・・
(汗)
・・・・
(じと・・・・)

・・・・
(逃)

PS
DBおめでとうございます!!
私も活動開始して何年になるのでしょう・・・・。
最近は完全に空気ですが・・・・
・・・・いろいろ騒いだ時代もありました(冷汗)。
懐かしいですね。

このまま発展して、目指すは世界一!!
(おい(笑))
RSS購読
RSS
ブログ表示スタイル
リスト/携帯(QRコード)
画像/動画/音声/リンク
表示開始年月
分類
全て
1.このサイトについて
2.作品DB開発/運用
3.ホームページ制作技術
4.Perl
5.C言語 / C++
6.検索エンジン&SEO
7.サッカー
8.自分のこと
9.Linux
10.旅行
11.思ったこと
12.パソコン
13.Berkeley DB
14.その他技術系
15.企画
16.スマートフォン
17.鑑賞
18.皆声.jpニュース
19.インターネット業界
20.運用マニュアル(自分用)
21.技術系以外実用書
22.料理
23.ALEXA
24.アニメ
25.会計
26.漫画
27.設計書
28.色々サイト作成
29.サーバー
30.自分専用
31.生活
32.OP/ED/PV
33.ゲーム
34.DB整備
35.新規開始作品紹介
36.英語圏の話題
37.大道芸
38.映画
39.PHP
40.ダイエット
41.Mac
42.JavaScript
43.MySQL
44.介護
45.作品DB作品追加作業
46BI
47.Web API
48.パフォーマンス
49.インターネットの活用方法
50.Riak
51.Androidアプリ開発
52.Cassandra
53.スパム
54.写真
55.iOSアプリ開発
56.AWS
57.マーケティング
58.Web漫画
59.法律
60.mongodb
61.開発環境整備
62.Google Apps Script
63.meteor
64.Pentaho
65.Ansible
66.VPS
67.技術書メモ
68.Vagrant
69.Docker
70.dokuwiki
71.Apple Watch
72.Webサービス
73.セキュリティ
74.Elastic Search
75.Wordpress
76.クラウド
77.英語
78.MVNO
79.シンガポール
80.マレーシア
81.海外生活
日記の主な内容
サイト運営/開発
検索エンジン情報
・技術ネタ(Berkeley DB,
Linux, Perl, サイト作成)等

サイト管理
全まとめ
サーバー管理
定期処理状況
開発予定
削除提案
作品追加依頼
OP/ED追加依頼
OP/ED not found
作品提案承認欄

格言 fromスクライド
この世の理は即ち速さ
20年かければ馬鹿でも
傑作小説を書ける

助けられたら助け返す
それが俺のルール

強くなるには
一番弱い考えをする事だ
そしてその考えに反逆する




右側に何か入れてみるテスト


仕事でのサイト
介護DB
Helpyou
Doctor career
Nurse career
上へ ↑上へ 最速検索作品DB皆声