使用 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
的单元格 A2
和 C2
以及 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,其中大多数方法都有代码示例。
我创建了一个用于报告学生出勤率的分布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
的单元格 - 替换
getActiveSheet()
到getSheetByName(name)
的所有实例,其中名称是您从A1
输入的
- 替换
getActiveRange()
到sheet.getRange()
的所有实例,从而将范围定义为检索日期所定义的子范围 - 为了找到第一次出现的开始日期和最后一次出现的结束日期 - 使用方法
indexOf()
和lastIndexOf()
- 确保仪表板中的日期符号与 sheet 中的日期符号相同,以实现正确的功能
A2
和 C2
以及 D2
中获取输入
样本:
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,其中大多数方法都有代码示例。