使用 Apps 脚本将用户输入 Google 表格并处理查询

Taking user input to Google Sheets using Apps Script and process with queries

我创建了一个用于报告学生出勤率的分布sheet,其中包含 8 sheets(每个 sheet 命名为主题代码)。完成特定 class 后,我转到特定 sheet(主题)和 select 该特定日期的所有行,然后按按钮 AfterClass-->Process Data(排序,删除重复项和保护)使用 Google 应用程序 Script/Macros。一切正常。

现在我创建了一个 DASHBOARD,我希望老师可以在仪表板上做所有事情,而不是去个人 sheet(主题)。 S/he 可以从仪表板提供两个输入 - 主题(sheet 名称)和日期,然后自动处理 Sheet 的这些特定数据集(不是 sheet 的所有数据) .请注意日期在A列,主题代码在F列。我写的代码如下:

  function AfterClass() {
      var spreadsheet = SpreadsheetApp.getActive();
  //Sorting and removing duplicates
  var height = spreadsheet.getActiveSheet().getActiveRange().getHeight();
  spreadsheet.getCurrentCell().offset(0, 0, height, 6).activate()
  .sort({column: spreadsheet.getActiveRange().getColumn() + 2, ascending: true});
  spreadsheet.getActiveRange().removeDuplicates([spreadsheet.getActiveRange().getColumn() + 2]).activate();

  //Protecting data finally
  //var lastRow = spreadsheet.getLastRow();
  var timeZone = Session.getScriptTimeZone();
  var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
  var me = Session.getEffectiveUser();
  var description = 'Protected on ' + stringDate + ' by ' + me;
  var protection = SpreadsheetApp.getActiveSheet().getActiveRange().protect().setDescription(description);
  //protection.setDomainEdit(false);   
  protection.addEditor(me);
  protection.removeEditors(protection.getEditors());
  if (protection.canDomainEdit()) {
    protection.setDomainEdit(false);
  }      

//Removing blank spacess in between data
      var sheet = SpreadsheetApp.getActiveSheet();
      var rows = sheet.getDataRange();
      var numRows = rows.getNumRows();
      var values = rows.getValues();
      var rowsDeleted = 0;
      for (var i = 0; i <= numRows - 1; i++) {
        var row = values[i];
        if (row[1] == '') {
          sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
          rowsDeleted++;
        }
      }   

     //For Double periods in a class
          //var ss = SpreadsheetApp.getActiveSpreadsheet()
          //var database = SpreadsheetApp.openById("xxx");
          //var source = ss.getSheetByName('yyy');
          var dataToCopyRng = SpreadsheetApp.getActiveSheet().getActiveRange(); //Gets range object of all data on source sheet
          var dataToCopy = dataToCopyRng.getValues(); //Gets the values of the source range in a 2 dimensional array
          var copyToSheet = SpreadsheetApp.getActiveSheet();
          var copyData = copyToSheet.getRange(copyToSheet.getLastRow()+1,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy); 

      //Calculate class attendance and signed
      var height2 = spreadsheet.getActiveSheet().getActiveRange().getHeight();   
      SpreadsheetApp.getActiveSheet().getCurrentCell().offset(2*height2,1).activate();
      SpreadsheetApp.getActiveSheet().getCurrentCell().setRichTextValue(SpreadsheetApp.newRichTextValue()
      .setText(height2 + ' Students, SIGNED')
      .setTextStyle(0, 12, SpreadsheetApp.newTextStyle()
      .setBold(true)
      .build())
      .build());
      spreadsheet.getCurrentCell().offset(0, -1, 1, 6).activate();
      spreadsheet.getActiveRangeList().setBackground('#e6b8af');
      //.setBackground('#d9d9d9')            

    }    

  [dashboard][1]


  [1]: https://i.stack.imgur.com/cMtHC.png

如何在选择指定的 sheet 和时间

后从仪表板 运行 您的脚本
  • 修改您的函数,使其从 Dashboard
  • 的单元格 A2C2 以及 D2 中获取输入
  • 替换 getActiveSheet()getSheetByName(name) 的所有实例,其中名称是您从 A1
  • 输入的
  • 替换 getActiveRange()sheet.getRange() 的所有实例,从而将范围定义为检索日期所定义的子范围
  • 为了找到第一次出现的开始日期和最后一次出现的结束日期 - 使用方法 indexOf()lastIndexOf()
  • 确保仪表板中的日期符号与 sheet 中的日期符号相同,以实现正确的功能

样本:

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('After Class')
  .addItem('Process Data', 'AfterClass')
  .addToUi();
}

function AfterClass() {
  var spreadsheet = SpreadsheetApp.getActive();
  var dashboard = spreadsheet.getSheetByName("Dashboard");
  var sheetName = dashboard.getRange("A2").getValue();
  //retrieve the start date to use as desired
  var startDate = dashboard.getRange("C2").getDisplayValue();
  var endDate = dashboard.getRange("D2").getDisplayValue();
  var sheet = spreadsheet.getSheetByName(sheetName);
  //chose the range within the specified dates, for this first locate the date column
  var startRow = 2;
  var dateColumn = sheet.getRange(startRow,1,sheet.getLastRow(), 1);
  var dates = dateColumn.getDisplayValues().flat();
  var firstRow = dates.indexOf(startDate)+startRow;
  var lastRow = dates.lastIndexOf(endDate)+startRow;
  //now get the range between (and including) those rows
  var range = sheet.getRange(firstRow, 1, lastRow-firstRow+1, sheet.getLastColumn());
  //Sorting and removing duplicates
  // You need to specify by which column you want to sort your data, in this sample it it column 3 - that it column C
  var column = 3;
  range.sort({column: column, ascending:true});
  range.removeDuplicates([column]);
  //now delete empty rows if any
  for (var i = range.getHeight(); i >= 1; i--){
    if(range.getCell(i, 1).isBlank()){
      sheet.deleteRow(range.getCell(i, 1).getRow());
    }
  }
  //Protecting data 
  var timeZone = Session.getScriptTimeZone();
  var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
  var me = Session.getEffectiveUser();
  var description = 'Protected on ' + stringDate + ' by ' + me;
  var protection = range.protect().setDescription(description)
  //protection.setDomainEdit(false);   
  protection.addEditor(me);
  protection.removeEditors(protection.getEditors());
  if (protection.canDomainEdit()) {
    protection.setDomainEdit(false);
  }
}

重要

如果您的 sheet 按日期排序(升序)- 正如您在评论中指定的那样,上面的示例将起作用。但是,一旦数据按 C 列而不是日期排序,它可能无法按预期工作。

旁注:

从你的代码中我了解到你将它记录为一个宏而不是从头开始编写(通过 activate() 的(不必要的)调用可见)。

非常推荐大家花点时间学习roder中的Apps Script,深入理解自己的代码,并能够根据自己的需要进行修改和调整。

所有可用的 Apps 脚本方法都有 basic tutorial for Apps Script in general, samples and explanations for Google Sheets in specific and the references,其中大多数方法都有代码示例。