旧集計システムからの移行

私が作った旧検温集計シート(スタッフ体温集計ver1.2.4)を既にお使いで、今からは「検温くん」に移行できないという方用に従来のシートを使用してGoogleフォームからの送信と「検温くん」からの送信どちらも対応可能にする方法を解説します

最初から「検温くん」で始めた方は、この作業はやらないでください。この作業は「検温くん」より前の旧システムからの移行の際のものです

検温くんシートver1.4.0までの対応でサポート終了しました。今後は検温くんのシートを利用ください

プログラム的な作業になります。自信がない場合は詳しい人にお願いするか諦めた方が無難です

必ずシートのオーナー権限がある方(編集権限ではありません)が、この操作を行ってください

事前準備

まずは「検温くん」の概要を把握しておいてください(このページで説明を重複させない為)

旧集計シートが入っているフォルダ

旧集計シートが入っているフォルダを覗き、シートのオーナー(上図の矢印部分)が「自分」になっているか確認してください。

違う人の名前になっている場合は権限がある人が操作を行うか、オーナー権限がある人からオーナ権限を移譲してもらってください

DATAシートを作成

既存のシートを開き左下にある「+」を押してシートを追加します

新しくできたシートのタブにある▼をおして、サブメニューから「名前を変更」をクリック

英字半角で「DATA」(すべて大文字)を入力、エンターを押して確定させます

DATAシートには何も書かなくても良いですが、気になるならば上図のように記入してください。

  1. 「TIMESTAMP」 ※英字半角で全て大文字で
  2. 「部署」
  3. 「名前」
  4. 「体温」

記入する場合は正確に入力してください

ライブラリ登録

「ツール」→「スクリプトエディタ」を開きます

スクリプトエディタにて「リソース」→「ライブラリ」を押します

このような小画面が出てくるので(A)の空欄に下記のライブラリーのKEY(正確にはスクリプトID)を入力します

検温くんスクリプトID:
1Xz_fgc1_935ujnrIqZNeApydWv81SpUEfMcKocAGp_cqDTz3xYWzUXo6

コピペで入れたら「追加」を押します

すると「検温SYSTEM」というライブラリが現れます。
まずは「バージョン」の所にある▼を押してバージョンリストを表示させます

リストの中から一番、数字の大きな物を選びます(上図の場合「9 不具合修正公開版」)

次にIdentifierの「検温SYSTEM」という所を「Kenon」と書き換えます

大文字と小文字は正確に入れてください
最初の文字(K)は大文字、あとは小文字です

このようにできたら「保存」を押して終了です

スクリプトをごっそり入れ替える

次からがかなり慎重になってほしいのですが、コード部分にカーソルを合わせ、Ctrrl+A(macだとCommand+A)で全選択してDELキーで全部消しちゃいます

すっからかーん!かなり不安ですよね

この無くなったコードの部分に新しいコードをコピペします

下の「+コードを表示する」を押して、コードを表示させて、さらにコード上にカーソルがあると表示される右上の「COPY」を押してコピー状態にします

/*
 このスクリプトはキングが作成しました
 使用ライブラリ:moment.js / Underscore / 検温くん
 version 2.0.0
 Update : 2020/7/30
*/

function input_Taion(e){
  
  console.info("入力内容>"+"TIME:"+e["values"][0]+" Name:"+e["values"][1]+" Temperature:"+e["values"][2]); //送信内容のログ
  
  /* 定数の宣言 */
  const DATA_TIME_COL = 1; //データシート上でタイムスタンプが入っている列
  const DATA_NAME_COL = 2; //データシート上で名前が入っている列
  const DATA_TAION_COL = 3;//データシート上で体温が入っている列
  const DATA_START_ROW = 2; //データシート上でデータ開始行
  const SHUKEI_BUSHO_COL = 1; //集計シート上で部署の列
  const SHUKEI_NAME_COL = 2; //集計シート上で名前の列
  const SHUKEI_HEINETSU = 3; //集計シート上で平熱の列
  const SHUKEI_DATE_COL = 4; //集計シート上で日付の開始列
  const SHUKEI_DATE_ROW = 1; //集計シート上で日付入力の行
  const SHUKEI_DN_ROW = 2; //集計シート上で朝昼夜を書く行
  const SHUKEI_START_ROW = 3; //集計シート上でデータ入力開始行
  const ASAYORU = [["朝",3,12],["昼",12,18],["夜",18,3]]; //送信時間の区分[ネーム,範囲開始,範囲終了(未満)]
  const WARN_TEMP = 0.5; //平熱からこの温度上がった場合 注意の色を付ける
  const DANGER_TEMP = 1.0; //平熱からこの温度上がった場合 危険の色を付ける
  
  /* ドキュメントのロック */
  var docLock = LockService.getDocumentLock(); //作業中はドキュメントをロックする定義
  var nowtime; //現在時間を入れる設定 
  
  /* ロック失敗時に遅延して繰り返す */
  var retry_count = 0;
  while(true){ 
  
    if (docLock.tryLock(10000)){ // ドキュメントに対してLockを試みる
      /* シートを定義 */
      var sheet_shukei = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("集計");
      var sheet_data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("フォームDATA");
      
      /* 変数定義 */
      var databox = sheet_shukei.getDataRange().getValues(); //集計シートのデータをすべて配列に格納
      
      var new_date = Moment.moment(e["values"][0]);//送信されたタイムスタンプ ※Momentオブジェクトを使用
      var new_name = rep_spc(e["values"][1]); //送信された名前
      var new_taion = Number(e["values"][2]); //送信された体温
      var last_row = databox.length; //集計シートの最終行を取得
      
      var target_row = 0; //送信された名前が置いてある行を入れる変数
      var last_day; //記録最終日を入れる変数 ※Momentオブジェクトを使用
      var array_instemp =[]; //データ挿入時の一時的な配列
      var jikan = new_date.hour(); //送信された時間の整数値
      var tekiyo = 0; //ASAYORUのどれに当てはまるか
      var maenohi = false; //深夜の場合は前日扱いにするためチェックが入る
      var tmp_range; //一時的なレンジオブジェクト格納変数
      var i; //テンポラリー変数
      var tmp_n; //テンポラリー数値変数
      var send_row; //データシートの代入された行数
      
      /* 「フォームDATA」シートのチェックをする */
      send_row = check_sh_data(sheet_data,e["values"][0],e["values"][1],new_taion);
      if( send_row == -1){
        console.error("ERROR:データシートにアクセスできませんでした");
      } else {
        tmp_range = sheet_data.getRange(send_row, 1,1,3);
        tmp_range.setBackground("#ebb907"); //送信されたところをオレンジに塗る
      }
     
      
      /* 送信された「名前」を検索 */
      for(i = SHUKEI_START_ROW-1; i < databox.length; i++){
        if(databox[i][SHUKEI_NAME_COL-1] == new_name){
          target_row = i + 1;
          break;
        }
      }
      
      /* 新規の名前の場合は新たに追加する */
      if( target_row === 0){
        sheet_shukei.insertRowBefore(last_row+1); //罫線形式など引き継ぐために行を挿入
        sheet_shukei.getRange(last_row+1, SHUKEI_NAME_COL).setValue(new_name);
        target_row = last_row + 1;
        last_row++;
      }
      
      /* 適用範囲を調査 */
      for(i = 0; i <= ASAYORU.length; i++){
        if(i == ASAYORU.length){ //該当なしの場合
          tekiyo = -1;
          break;
        }
        if(jikan >= ASAYORU[i][1] && jikan < ASAYORU[i][2]){ //通常範囲の場合
          tekiyo = i;
          break;
        } else if(ASAYORU[i][1] > ASAYORU[i][2] && jikan >= ASAYORU[i][1]){ //範囲が日をまたぐ場合で同日
          tekiyo = i;
          break;
        } else if(ASAYORU[i][1] > ASAYORU[i][2] && jikan < ASAYORU[i][1]){ //範囲が日をまたぐ場合で前日扱い
          tekiyo = i;
          maenohi = true;
          break;
        }
      }
      if(maenohi == true){ //前の日扱いの場合は送信データから1日減算する
        new_date.subtract(1,'d');
      }
      
      
      /* 送信された日時が最終日時として登録されていいなければ追加する */
      last_day = databox[SHUKEI_DATE_ROW-1][SHUKEI_DATE_COL-1] != undefined ? Moment.moment(databox[SHUKEI_DATE_ROW-1][SHUKEI_DATE_COL-1]) : Moment.moment('2000-1-1'); //最終日取得
      if(!new_date.isSame(last_day, 'day')){ //送信された日付と最終更新日が同じ日付かどうか判断
        for(i = 0; i < ASAYORU.length; i++){ //新規日付や朝夜のデータをセット
          if(i == 0){
            array_instemp.push([new_date.format("M/D")]);
            array_instemp.push([ASAYORU[ASAYORU.length - 1][0]]);
          } else {
            array_instemp[0].push('');
            array_instemp[1].push(ASAYORU[ASAYORU.length - 1 - i][0]);
          }
        }
        sheet_shukei.insertColumnsBefore(SHUKEI_DATE_COL,ASAYORU.length); //列を必要分新規挿入
        sheet_shukei.getRange(SHUKEI_DATE_ROW, SHUKEI_DATE_COL,2,ASAYORU.length).setValues(array_instemp); //一気に挿入
        sheet_shukei.getRange(SHUKEI_DATE_ROW, SHUKEI_DATE_COL,1,ASAYORU.length).merge(); //日付の部分のセルを結合
        sheet_shukei.setColumnWidths(SHUKEI_DATE_COL, ASAYORU.length, 45); //列幅調整
        sheet_shukei.getRange(SHUKEI_START_ROW, SHUKEI_DATE_COL,last_row - SHUKEI_START_ROW + 1,ASAYORU.length).setBackground(null);//新規追加した部分は背景色をなしにする
        
      } 
      
      /* 体温を合致した場所に代入 */
      if(tekiyo == -1){
        tmp_range = sheet_shukei.getRange(target_row, SHUKEI_DATE_COL);
        tmp_range.setValue("ERROR");
        tmp_range.setFontColor("red");
      } else {
        tmp_range = sheet_shukei.getRange(target_row, SHUKEI_DATE_COL + ASAYORU.length - 1 - tekiyo);
        tmp_range.setValue(new_taion);
        tmp_range.setFontColor("black");
      }
      
      /* 平熱の欄を更新 */
      sheet_shukei.getRange(target_row, SHUKEI_HEINETSU).setValue('=AVERAGE(offset($A$1,row()-1,'+(SHUKEI_DATE_COL-1)+',1,100))');
      tmp_n = Math.round(sheet_shukei.getRange(target_row, SHUKEI_HEINETSU).getValue() * 10) / 10  ; //計算された数値を小数第1位で四捨五入して取得(もしかしたらタイミング的に間に合っていないかも)
      if(tmp_n != undefined && tmp_n != ""){
        if(new_taion >= tmp_n + DANGER_TEMP){ //体温が平熱より危険温度以上の場合背景色を赤にする
          tmp_range.setBackground("red");
        } else if(new_taion >= tmp_n + WARN_TEMP) { //体温が平熱より注意温度以上の場合背景色をオレンジにする
          tmp_range.setBackground("#ebb907");
        } else { //その他の場合は背景色を白にする
          tmp_range.setBackground("white");
        }
      } else { //なんかの理由で平熱の値が取得できなかった場合も背景を白くする
        tmp_range.setBackground("white");
      }
      
      /* ロックの解除 */
      docLock.releaseLock();
      
      break; //リトライから脱出して終了
      
    } else { //既にロックされていた場合
      nowtime = Moment.moment();
      console.warn("ロックされていたため入力に失敗しました:"+ nowtime.format('YYYY/M/D H:mm:ss'));
      Utilities.sleep(1000); //1秒待機
      retry_count++;
      if(retry_count >= 10){ //10回繰り返したら諦める
        console.error("数回リトライしましたが成功しませんでした");
        break;
      }
    }
  }
}


/* 空白を除去する関数 */
function rep_spc(target){
  if (target == null || target == undefined){
    return "";
  }
  return target.replace(/\s+/g, "");
}

/* 次の関数は今回新しくするのでコメントアウト
/* トリガー設置 
function bt_kyoka(){
    var ssid = SpreadsheetApp.getActiveSpreadsheet(); //スプレッドシートのSSIDを取得
    var triggers = ScriptApp.getProjectTriggers(); //トリガー情報を取得

    if(triggers.length == 0){ //トリガーが0の場合は初回とみなして設置する
        var onFormSubmitTrigger = ScriptApp.newTrigger("input_Taion").forSpreadsheet(ssid).onFormSubmit().create();
        console.info("トリガーが登録されました");
        Browser.msgBox("設定完了\\n正確に権限の許可作業ができていれば自動入力されるようになりました");
    } else {
        console.warn("トリガー登録ボタンが押されましたが既に登録済みでした");
    }
}
*/

/* フォームDATAシートチェックする関数 */
function check_sh_data(e_sheet,e_date,e_name,e_taion){
  var databox = e_sheet.getDataRange().getValues(); //集計シートのデータをすべて配列に格納
  var _ = Underscore.load(); //underscoreライブラリのロード
  var i; //一時変数
  
  var start_row = -1;
  var end_row = -1;
  
  for(i = 0; i < databox.length ; i++){
    if( Moment.moment(databox[i][0]).diff(e_date,'seconds') <= 2 ){ //フォームからのトリガー送信と自動データコピーでタイムスタンプに1秒ほど差が出ることがあるので2秒は誤差の範囲として処理
      if( start_row == -1) start_row = i;
      end_row = i;
    }
  }
  
  if(start_row == -1){
    console.error("SEARCH ERROR: 日時が合うものが見つからない");
    return -1; //日時データが合わないのならば-1を返す
  } else {
    
    if(start_row == end_row){ //1行しかなかった場合
      if( databox[start_row][1] == e_name && databox[start_row][2] == e_taion){
        return start_row + 1;
      } else {
        console.error("SEARCH ERROR: 1行見つかったが内容が違う");
        return -1;
      }
      
    } else { //複数行あった場合
      var tmp_array = _.first(databox,end_row + 1); //必要ない部分を切り取る
      var databox_tr = _.zip.apply(_, tmp_array); //行列変換
      
      start_row = databox_tr[1].indexOf(e_name,start_row); //名前を検索
      if(start_row == -1){
        console.error("SEARCH ERROR: 時間範囲内に同じ名前を見つけられない");
        return -1; //名前がなかったら-1を返す
      } else {
        end_row = databox_tr[1].lastIndexOf(e_name);
        tmp_array = _.first(databox,end_row + 1); //必要ない部分を切り取る
        databox_tr = _.zip.apply(_, tmp_array); //行列変換
        
        start_row = databox_tr[2].indexOf(e_taion,start_row); //体温を検索
        if(start_row == -1){
          console.error("SEARCH ERROR: 時間範囲内・同じ名前の中で同じ体温を見つけられない");
          return -1; //名前がなかったら-1を返す
        } else {
          return databox_tr[2].lastIndexOf(e_taion) + 1; //見つかった場合は最後の物を返す
        }
      }
    }   
  }
}

/*以後「検温くん」で動く部分で追加されたもの*/
/*
  このスクリプトはライトニングが作成しました
  使用ライブラリ:検温くんSYSTEM(1Xz_fgc1_935ujnrIqZNeApydWv81SpUEfMcKocAGp_cqDTz3xYWzUXo6)
  不具合が出た場合は公式サイト(https://stafftaion.dcf7.com/)にご連絡ください
*/

var sheet_ver = "1.4.0"; //バージョン情報

//データ受信した時の処理
function doPost(e){
  console.log(JSON.stringify(e));
  
  //後に変更する可能性のある設定数値を
  const DEFAULT_OBJ = {
    SHEET_NAME_DATA : "DATA", //受信したデータ保管シートの名前
    SHEET_NAME_SHUKEI : "集計", //集計シートの名前
    SHEET_ID: SpreadsheetApp.getActiveSpreadsheet().getId(), //シートのID
    SHEET_NAME_SET : "設定", //設定シートの名前
    DATA_TIME_COL : 1, //データシート上でタイムスタンプが入っている列
    DATA_JOB_COL : 2, //データシート上で部署が入っている列
    DATA_NAME_COL : 3, //データシート上で名前が入っている列
    DATA_TAION_COL : 4,//データシート上で体温が入っている列
    DATA_START_ROW : 2, //データシート上でデータ開始行
    SHUKEI_BUSHO_COL : 1, //集計シート上で部署の列
    SHUKEI_NAME_COL : 2, //集計シート上で名前の列
    SHUKEI_HEINETSU : 3, //集計シート上で平熱の列
    SHUKEI_DATE_COL : 4, //集計シート上で日付の開始列
    SHUKEI_DATE_ROW : 1, //集計シート上で日付入力の行
    SHUKEI_DN_ROW : 2, //集計シート上で朝昼夜を書く行
    SHUKEI_START_ROW : 3, //集計シート上でデータ入力開始行
    ASAYORU : [["朝",3,12],["昼",12,18],["夜",18,3]], //送信時間の区分[ネーム,範囲開始,範囲終了(未満)]
    WARN_TEMP : 0.5, //平熱からこの温度上がった場合 注意の色を付ける
    DANGER_TEMP : 1.0, //平熱からこの温度上がった場合 危険の色を付ける
    KEY_NAME_TIME : "TIMESTAMP", //POSTのKEY名の設定
    KEY_NAME_JOB : "部署", //POSTのKEY名の設定
    KEY_NAME_NAME : "名前", //POSTのKEY名の設定
    KEY_NAME_BT : "体温", //POSTのKEY名の設定
  }
  var cache = CacheService.getScriptCache();//キャッシュを取得
  Kenon.kenon_post(DEFAULT_OBJ,e,cache); //ライブラリで処理
  
  var version = {
    lib: Kenon.ver,
    sheet: sheet_ver,
  }
  /* 戻り値としてバージョン情報を送信 */
  var version = {
    lib: Kenon.ver,
    sheet: sheet_ver,
  }
  var res = ContentService.createTextOutput();
  res = res.setMimeType(ContentService.MimeType.JAVASCRIPT);
  res = res.setContent(JSON.stringify(version));
  
  return res;
}

/* キュートリガー */
function timeDrivenFunction(){
  //cacheを取得
  var cache = CacheService.getScriptCache();
  var data = cache.get("key");

  //cacheの読み書きの競合が怖いのでなるべく早く消しておく
  cache.remove("key");
  
  Kenon.kenon_timeDrivenFunction(data,cache);
}


/* トリガー設置 */
function bt_kyoka(){
  Kenon.kenon_init_trigger(sheet_ver);
  
}

/* テスト デバッグ用 */
function test(){
  var d_now = new Date
  var post_data = {
    parameter : {
      'TIMESTAMP' : Utilities.formatDate( d_now, 'Asia/Tokyo', 'yyyy/MM/dd hh:mm:ss'),
      '部署' : "GAS-TEST",
      '名前' : "テスト",
      '体温' : 36.7,
    },
  };
  doPost(post_data);
}

それを先程の空のスクリプトエディタにペーストする

うまくペースト出来たら一度「保存」してください

ウェブアプリケーションとして公開

ここからは本来のシートと同じなので下記リンクの記事を参考にウェブアプリケーションとして公開→承認をすれば、「Googleフォーム」「検温くん」どちらも機能するようになります。

タイトルとURLをコピーしました