實作抗體鑑定細胞資料庫(2)-撰寫 Javascript 腳本

撰寫 Javascript 腳本

執行 App script 之前,都會跳出安全警示,確認使用者是否相信所執行的腳本。由於是自己寫的內容,當然可以相信,並忽略警告。

搜尋血球資料

搜尋流程大致可分為: 讀取搜尋條件 > 搜尋血球資料庫 > 顯示搜尋結果

function search() {

  var sp = SpreadsheetApp.getActiveSpreadsheet();
  var s = sp.getSheetByName("Search");

  // 定位搜尋條件的範圍
  var titleRow = getKeywordPos(s, "POS").getRow()-1;
  var posRow =  getKeywordPos(s, "POS").getRow();
  var negRow = getKeywordPos(s, "POS").getRow()+1;
  var startCol = getKeywordPos(s, "POS").getColumn()+1;
  var lastColumn = s.getLastColumn();

  var keys = s.getRange(titleRow,1,1, lastColumn).getValues()[0];

  // 讀取條件
  var filter={};

  filter["positive"] = [];
  filter["negative"] = [];

  for(var col=startCol; col<= lastColumn; col++) {
    //陽性條件
    if(s.getRange(posRow, col).getValue()) {
      filter["positive"].push(keys[col-1]);
    }
    //陰性條件
    if(s.getRange(negRow, col).getValue()) {
      filter["negative"].push(keys[col-1]);
    }
  }

  // 是否搜尋用盡之血球
  var showEmpty = getKeywordPos(s, "Show empty").offset(0,1).getValue();

  // 定位填入資料位置
  var startCol = getKeywordPos(s, "Select").getColumn();
  var titleRow = getKeywordPos(s, "Select").getRow();

  //讀取所有血球頁籤
  var cellSheets = getCellSheets();
  for(i in cellSheets) {
    var data = getData(cellSheets[i], filter, showEmpty);
    var startRow = s.getLastRow()+1;
    var startPos = s.getRange(startRow, startCol);
    filldata(s, titleRow, startPos,data);
  }
}

search() 裡面自訂的 function 如下:

getKeywordPos()

/*
回傳 Keyword 位置
*/
function getKeywordPos(sheet, keyword) {
  var search = sheet.createTextFinder(keyword);
  var range = search.findNext();
  return range;
}

主要是拿來定位用。找出特定關鍵定的位置。例如在 Search 頁籤中,找到 POS 的位置,就可以推算出抗原表題、陽性條件跟陰性條件的相對位置。

getCellsheets()

/*
 讀取細胞頁籤
*/
function getCellSheets() {

  var sp = SpreadsheetApp.getActiveSpreadsheet();
  var sheets = sp.getSheets();
  var cellSheets=[];

  // 讀取名為 "H-hide" 的頁籤,裡面紀錄非血球資料的頁籤
  var hideSheet = sp.getSheetByName("H-hide");
  var toHide =hideSheet.getRange(1, 1, hideSheet.getLastRow()).getValues();

  for(i in sheets) {
    var skip = false;
    var sheetName =sheets[i].getName();

    if(sheetName.indexOf("H-") ==0) continue;  //略過所有開頭為 "H-" 的頁籤

    // 略過非血球資料的頁籤
    for(i2 in toHide) {
      if(sheetName == toHide[i2][0]) skip=true;
    }

    if(skip) continue;

    cellSheets.push(sheets[i]);
  }

  return cellSheets;
}

getData()

/* 
試取細胞 sheet 內,血球表現型的資料。
filter: 抗原篩選器
includeEmpty: 是否搜尋已用盡之細胞試劑 
*/ 
function getData(sheet, filter, includeEmpty) {

  var targetPos = getKeywordPos(sheet, "cell#");
  var titleRow = targetPos.getRow();
  var lastColumn = sheet.getLastColumn();
  var lastRow=sheet.getLastRow();
  var keys = sheet.getRange(titleRow,1,1, lastColumn).getValues()[0];
  var res=[]; //最後的搜到的血球

  for(var row=titleRow+1; row<=lastRow;row++){
    var data = {}; // 每一筆血球之表現型資料
    for(var col=1; col<=lastColumn; col++){
      var cellValue = sheet.getRange(row, col).getValue();
      data[keys[col-1]] = cellValue;
    }

    // 判斷是否搜尋用盡的血球
    if(!includeEmpty && data["Out"]==true){continue;}

    var toAdd = true;

    // 過濾陽性血球,非陽性排除
    for(pos in filter["positive"]){
      if(data[filter["positive"][pos]] != "+" && data[filter["positive"][pos]] != "+s" && data[filter["positive"][pos]] != "w")  {

        toAdd=false;
        break;
      }
    }

    // 過濾陰性血球,非陰性排除
    for(neg in filter["negative"]) {
      if(data[filter["negative"][neg]] != "0")  {
        toAdd=false;
        break;
      }
    }

    if(toAdd) res.push(data);
  }

  return res;
}

fillData()

/**
 * sheet: 填入的頁籤
 * titleRow: 表填入格之表頭列
 * startPos: 填入位置
 * data: 欲填入之血球資料
 */

function filldata(sheet, titleRow, startPos, data) {
  var s = sheet
  var startCol = startPos.getColumn();
  var lastColumn = s.getLastColumn();
  var startRow = startPos.getRow();
  var keys = s.getRange(titleRow,1,1, lastColumn).getValues()[0];

  for(var row in data) {    

    // 計算填入的列
    var targetRow = parseInt(startRow) + parseInt(row);

    // 略過未選擇之血球
    if(data[row]["Select"] != null && data[row]["Select"] !=true) {
      startRow -=1;
      continue;
    }

    for(var col= startCol;col<=lastColumn;col++) {
      // 當表頭無名稱時停止
      if(keys[col-1] == "") {
        break;
      }

      // 在 select 欄位加上 checkbox
      if(keys[col-1] == "Select") {
         if(!data[row]["Out"]) {
           s.getRange(targetRow, col).insertCheckboxes();
          } else{
            s.getRange(targetRow, col).setValue("X"); // 當血球用完時顯示"X"
          }
         continue;
      }

      // 填入資料
      var value=data[row][keys[col-1]];
      if(value==null) {
        value ="/";  //當無表現型資料時顯示"/"
      }
      s.getRange(targetRow, col).setValue(value);
    }
  }
}

匯出選取血球為 Panel 表

匯出流程:建立空白 Panel 表 > 讀取搜尋結果 > 將血球資料填入空白 Panel 表中

function exportCells() {
  // 建立空白 Panel 表
  var outSheet = createEmptyPanel("H-emptySelect");
  var sp = SpreadsheetApp.getActiveSpreadsheet();

  var sourceSheet = sp.getSheetByName("Search")

  sortSearchData(); // 排序血球之巨集

  var data = getData(sourceSheet, {}, false);

  data = cleanData(data);
  var targetRange = getKeywordPos(outSheet, "LOT");
  var titleRow = targetRange.getRow();
  var startPos = targetRange.offset(1,0);

  filldata(outSheet, titleRow, startPos , data);
}

createEmptyPanel()

/*
 * 複製空白 Panel 表
 * emptySheetName: 空白 Panel 表樣版頁籤
 **/
function createEmptyPanel(emptySheetName) {
  var sp = SpreadsheetApp.getActiveSpreadsheet();
  var outSheet = sp.getSheetByName("Out");
  if (outSheet!=null)  sp.deleteSheet(outSheet);
  var tempSheet = sp.getSheetByName(emptySheetName);
  outSheet = tempSheet.copyTo(sp);
  outSheet.setName("Out");
  sp.setActiveSheet(outSheet);
  sp.moveActiveSheet(2);
  return outSheet;
}

sortSearchData()

/*
 * 將已選擇的血球置頂,並依日期排序
 * 基本上是用巨集產生的
 */
function sortSearchData() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Search");
  var row = getKeywordPos(spreadsheet, "Select").getRow();
  var range = spreadsheet.getRange(row, 1, 100, 35 );
  range.offset(1, 0, range.getNumRows() - 1).sort([{column: 1, ascending: false},{column: 4, ascending: false}, {column: 2, ascending: true}]);
};

cleanData()

/*
 * 修改格式
 * */
function cleanData(dataArray) {

  for(var idx in dataArray) {
    dataArray[idx]["Expiration date"] =Utilities.formatDate(dataArray[idx]["Expiration date"], "GMT+8", "yyyyMMdd");
    dataArray[idx]["LOT-cell#"] = dataArray[idx]["LOT"].toString() +"-"+ dataArray[idx]["cell#"].toString();
  }
  return dataArray;
}

待續….

關鍵字: 抗體鑑定, 血庫, Javascript, Google sheets, Apps script

留言

這個網誌中的熱門文章

Terminal 預設換行

血庫人的夢魘 - 令人崩潰的抗癌新藥

Thomsen‐Friedenreich antigen (T 抗原) 活化病人之輸血策略