Gg / 以Google sheets作為資料庫,透過Google Apps Script製作查詢頁面 修改版4 part1

時間過得好快

距離上一版「JS / 以Google sheets作為資料庫,透過Google Apps Script製作查詢頁面 修改版3」已經有1年之久了

這次的修改是要處理”資料還在增加的階段”,而且”要把所有資料都集中在同一個工作簿”

因為如果是分散在個別的工作簿,第一版就是這樣的設計

在後端程式中分解前端傳遞過來的課程代碼,再分別搜尋指定的工作簿取得對應的資料

經過彙整之後,再傳遞到前端網頁

前端網頁接收之後,經過處理、呈現在網頁上

如果要先把不同表單的資料集中在同一個工作簿,而且資料隨時會增加

這樣就必須先判斷是否有新的資料,如果有就更新原有的工作表

之後再搜尋資料表內是否有對應的資料

我的思路

1.1取得個別工作簿的版本紀錄,判斷是否更新

但是這個無法判斷是因為什麼原因產生新的版本紀錄

可能是修改其他欄位資料,不見得是新增資料筆數

1.2 取得個別工作簿的最後一筆資料所在列數,判斷是否增加

感覺這個方式比較準確,也比較直觀

2.更新有增加資料筆數的工作表

這邊用比較暴力的方式,直接刪除舊有工作表,複製新的工作表

為了資料的可讀性,新的工作表要放在舊有工作表原來的位置


現在說明各部分的內容

資料的介面,”資料紀錄”工作表

工作表名稱-課程代碼

fileID-表單的工作表ID

C、D、E欄紀錄當下執行所取得的修改時間、資料筆數,以及執行檢查的時間

F欄是紀錄前一次執行時所取得的修改時間、資料筆數是否跟當下執行所取得的資料,兩者比對的結果

G、H、I欄如果有新增資料就把原來的 C、D、E欄的資料移到這邊,C、D、E欄再寫入新的資料

 

主程式 getSheetInfo()

#2-4

取得”資料紀錄”工作表的最後一筆資料的列數,作為迴圈的依據

#7-47

有多少工作表就跑幾次迴圈

#8

取得A欄資料,工作表名稱

#11

取得B欄資料,工作簿的文件ID

#15-23

透過自訂函數listFileRevisions(),傳入文件ID,取得最近一次的修改紀錄時間

如果當前的修改紀錄時間不等於上一次檢查所取得的時間

將C欄資料移到G欄之後,C欄再寫入新的修改紀錄時間

在J欄寫入”lastModi !== sh.getRange(i, 3).getValue()”,目的是為了檢測程式碼是否確實執行

否則,在J欄寫入”lastModi == sh.getRange(i, 3).getValue()”,目的是為了檢測程式碼是否確實執行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function getSheetInfo() {
  var ss = SpreadsheetApp.openById("*****************************************");
  var sh = ss.getSheets()[1];
  var lastRow = sh.getLastRow();
  //console.log(lastRow);

  for (var i = 2; i < lastRow + 1; i++) {
    var sName = sh.getRange(i, 1).getValue();
    //console.log(sName);

    var sFileId = sh.getRange(i, 2).getValue();
    //console.log(sFileId);

    //取得最近一次的修改紀錄
    var lastModi = listFileRevisions(sFileId);
    //console.log(lastModi);
    if (lastModi !== sh.getRange(i, 3).getValue()) {
      sh.getRange(i, 7).setValue(sh.getRange(i, 3).getValue());
      sh.getRange(i, 3).setValue(lastModi);
      sh.getRange(i, 10).setValue("lastModi !== sh.getRange(i, 3).getValue()");
    }else{
      sh.getRange(i, 10).setValue("lastModi == sh.getRange(i, 3).getValue()");
    }
    //取得最新的表單回應
    var lastR = getSheetLastRow(sFileId) - 1; //取得表單回應

    if (lastR != sh.getRange(i, 4).getValue()) {
      sh.getRange(i, 7).setValue(sh.getRange(i, 3).getValue());
      sh.getRange(i, 8).setValue(sh.getRange(i, 4).getValue());
      sh.getRange(i, 9).setValue(sh.getRange(i, 5).getValue());
	  //
      sh.getRange(i, 4).setValue(lastR);
      sh.getRange(i, 5).setValue(Utilities.formatDate(new Date(), "GMT+8", "yyyy/MM/dd-hh:hh:ss"));
      sh.getRange(i, 6).setValue("");
      //
      delTable(sName);
      getTable(sFileId, sName);
      moveSheet(sName);
    } else {
      sh.getRange(i, 9).setValue(sh.getRange(i, 5).getValue());
      
	  sh.getRange(i, 5).setValue(Utilities.formatDate(new Date(), "GMT+8", "yyyy/MM/dd-hh:hh:ss"));
      
	  sh.getRange(i, 6).setValue("相同");
    }
  }
}

 

自訂函數

1.取得最後修改紀錄 listFileRevisions(fileId)

接收fileId-文件ID(B欄)為參數

必須引用drive api,再使用Drive.Revisions.list(fileId)取得修訂紀錄

修訂紀錄為物件(object)的資料型態,所以要用物件的方式來取得資料

只需要取得最新一筆紀錄,其中modifiedDate是修改時間

使用內建函數Utilities.formatDate()調整時區與呈現格式之後,將資料傳回

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function listFileRevisions(fileId) {
  var response = Drive.Revisions.list(fileId);

  var lastRec = response.items.length - 1;

  var lastModiDate = response.items[lastRec].modifiedDate;

  var fmDate = Utilities.formatDate(new Date(lastModiDate), "GMT+8", "yyyy/MM/dd-hh:hh:ss");

  return fmDate;
}

 

drive api的引用方式

新增”服務”

選擇  drive api

選擇V2版本

2.取得工作表的最後一筆位置 getSheetLastRow( fileId)

接收 fileId-文件ID(B欄)為參數

取得第一個工作表的最後一筆資料所在的列數,傳回

1
2
3
4
5
6
function getSheetLastRow(fileId) {
  var sId = fileId;
  var ss = SpreadsheetApp.openById(sId);
  var daTaNum = ss.getSheets()[0].getLastRow();
  return daTaNum;
}

 

3.刪除資料工作表 delTable(fileId)

接收 fileId-文件ID為參數

使用內建函數deleteSheet()來刪除工作表

這邊要留意deleteSheet()接收的參數是工作表物件

1
2
3
4
5
6
function delTable(fileId) {
  console.log(fileId);
  var ss = SpreadsheetApp.openById("*****************************************");
  var sOne = ss.getSheetByName(fileId);
  ss.deleteSheet(sOne);
}

 

4.取得工作表 getTable(fileId, fileName)

接收 fileId-文件ID(B欄)、fileName-工作表名稱-課程代碼(A欄)為參數

s1是”資料紀錄”所在的工作簿

s2是要取得資料的來源工作簿

s2One是來源工作簿的第一個工作表

將s2One複製到s1,並用課程代碼來命名

1
2
3
4
5
6
7
8
9
function getTable(fileId, fileName) {
  var sId = fileId;
  var sName = fileName;
  var s1 = SpreadsheetApp.openById("*****************************************");
  var s2 = SpreadsheetApp.openById(sId);

  var s2One = s2.getSheets()[0];
  s2One.copyTo(s1).setName(sName);
}

 

5.移動工作表 moveSheet(fileName)

接收 fileName-工作表名稱-課程代碼(A欄)為參數

取得當前工作表在指定順序(arr2)的序號,再移動位置

適用只移動一個工作表

#8-9

內建的工作簿函數moveActiveSheet()接收的參數

是要移動到的工作表位數(從1開始)

工作表必須先activate(),才能使用工作簿函數moveActiveSheet()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function moveSheet(fileName) {
  var sName = fileName;
  var ss = SpreadsheetApp.openById("1KCNDlSA_ZgOrCJHfjJehc-REA6Dbquvs98dI0smQufg");
  var sOne = ss.getSheetByName(sName);
  var arr2 = ["c0101", "c0102", "c02", "ch01", "ch02", "en01", "en02", "ma01", "ma02"];
  var r = arr2.indexOf(sName);

  sOne.activate();
  ss.moveActiveSheet(r + 3);
}

 

接下來就是整合到原有的流程之中了