Я реализовал 2 триггера в сценарии приложений. Цель этих триггеров — выполняться только 15-го и последнего дня каждого месяца. Текущая проблема: триггеры пытаются выполниться несколько раз в день, что приводит к исключению ошибки: в этом сценарии слишком много триггеров. Триггеры необходимо удалить из сценария, прежде чем можно будет добавить новые.
Триггер 1 (база данных копирования): Ожидаемые результаты: триггер копирует указанный диапазон из исходного листа (индекс) в целевой лист (все рабочие задания) 15-го числа каждого месяца и в последний день каждого месяца (с 23:00 до полуночи).
/// TRigger
ScriptApp.newTrigger("copyDatabase")
.timeBased()
.atHour(23)
.everyDays(1)
.create();
function copyDatabase() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Index");
var desinationsheet = ss.getSheetByName("All Work Orders");
var startRow = 2;
var numRows = sheet.getLastRow() - startRow + 1;
var startCol = 23;
var numCols = 14;
var startColTarget = 1;
var dataRange = sheet.getRange(startRow, startCol, numRows, numCols);
var data = dataRange.getValues();
var Copy = "Copy";
var firstEmptyRow = sheet.getRange("A2:P").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
var dataToCopy = data.filter(row => row[2] !== Copy);
desinationsheet.getRange(firstEmptyRow, startColTarget, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
}
Триггер 2 (exportTriggerFunction): Ожидаемые результаты: триггер выполняет функцию и экспортирует в указанные листы/вкладки в Excel 15-го числа (с 23:00 до полуночи) и в последний день каждого месяца (с 23:00 до полуночи).
///////////////
/// TRigger
ScriptApp.newTrigger("exportTriggerFunction")
.timeBased()
.atHour(23)
.everyDays(1)
.create();
function exportTriggerFunction()
{
var today = new Date();
var lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);
if (today.getDate() == lastDayOfMonth.getDate() )
{
var exportSheetIds =
["560568812","1440952465","439649389","513481136",
"1088500659","133657514","1618608630","802444630",
"1834450484","657423004","682313931","1980296394","635144452"]; // Please set the sheet IDs you want to export.
const ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var name = sheet.getRange('Reference!M2').getDisplayValue();
var destination = DriveApp.getFolderById("1HRchNqQ5_0LYzfULw1hWN_ALNuijsy2q"); // Google Drive Folder Location
// 1. Copy the active Spreadsheet as a tempora Spreadsheet.
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet().copy('tmp');
const sheets = spreadsheet.getSheets();
const check = sheets.filter(s => exportSheetIds.includes(s.getSheetId().toString()));
if (check.length == 0) {
throw new Error("No export sheets.");
}
// 2. Convert the formulas to the texts.
sheets.forEach(sheet => {
if (exportSheetIds.includes(sheet.getSheetId().toString())) {
const targetRange = sheet.getDataRange();
targetRange.copyTo(targetRange, { contentsOnly: true });
}
});
// 3. Delete/Exclude sheets: Index and Reference
sheets.forEach(sheet => {
if (!exportSheetIds.includes(sheet.getSheetId().toString())) {
spreadsheet.deleteSheet(sheet);
}
});
SpreadsheetApp.flush();
// 4. Retrieve the blob from the export URL.
const id = spreadsheet.getId();
const xlsxBlob = UrlFetchApp.fetch(`https://docs.google.com/spreadsheets/export?id=${id}&exportFormat=xlsx`, { headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` } }).getBlob();
// 5. Create the blob as a file.
destination.createFile(xlsxBlob.setName(`${name}.xlsx`));
// 6. Delete the temporate Spreadsheet.
DriveApp.getFileById(id).setTrashed(true);
}
}
Изначально моя стратегия выполнения 15-го числа заключалась в настройке параметров каждого триггера.
Выберите тип триггера по времени: Месячный таймер. Выберите день месяца: 15 число.
А для выполнения в последний день месяца я попытался отредактировать сценарий кода, но эта комбинация в настоящее время вызывает исключение ошибки.
Во-первых, если вы ищете триггер, который срабатывает только 15-го и последнего дня каждого месяца, вы настраиваете его неправильно. Вам следует использовать триггер timeBased, который использует синтаксис onMonthDay.
Во-вторых, я не знаю, где в вашем коде вы настроили возможность программного добавления этих триггеров, но вы запускали скрипт столько раз, что было добавлено много копий. Я бы это почистил.
Если вам не нужно добавлять триггеры программно, я бы просто установил их вручную, используя onMonthDay. Я бы также использовал 1-й день месяца, полночь-1 час ночи, вместо последнего дня месяца (потому что вы не можете вручную установить последний день месяца напрямую, просто число).
Я считаю, что ваша цель состоит в следующем.
copyDatabase
и exportTriggerFunction
.copyDatabase
и exportTriggerFunction
только один раз с 23:00:00 до 00:00:00 15-го числа и в последний день каждого месяца.В этом случае как насчет следующей модификации?
ScriptApp.newTrigger("copyDatabase")...
и ScriptApp.newTrigger("exportTriggerFunction")...
используются как глобальные. В этом случае при запуске copyDatabase
и exportTriggerFunction
устанавливаются 2 триггера, управляемых по времени. И триггеры не удаляются. Я предположил, что ваше изображение может указывать на это.Как насчет следующего потока, чтобы достичь своей цели?
copyDatabase
и exportTriggerFunction
. И устанавливается следующий триггер, управляемый временем.
copyDatabase
и exportTriggerFunction
используются путем помещения в функцию типа wrapFunction
. Судя по вашему сценарию, я предположил, что обе функции можно выполнить за 6 минут.Когда этот поток отражается в вашем сценарии, он становится следующим.
Скопируйте и вставьте следующий сценарий в редактор сценариев. И, пожалуйста, запустите функцию вручную installTriggers
. При этом устанавливается 1-й триггер, управляемый по времени. После этого функции copyDatabase
и exportTriggerFunction
запускаются автоматически с помощью автоматически установленного триггера, управляемого по времени.
В этой модификации используется только один триггер, управляемый по времени.
// --- I added the below script.
function installTriggers() {
// These values are from your question.
const time = 23;
const dates = [15, 0];
const extraTime = 60; // seconds
const functionName = "wrapFunction";
ScriptApp.getProjectTriggers().forEach(t => {
if (t.getHandlerFunction() == functionName) {
ScriptApp.deleteTrigger(t);
}
});
const now = Date.now();
const triggerDate = dates.map(e => {
const d = new Date();
d.setMonth(d.getMonth() + (e > 0 ? 0 : 1), e);
d.setHours(time, 0, 0);
if (d.getTime() < now + (extraTime * 1000)) {
d.setMonth(d.getMonth() + 1);
}
return d;
}).sort((a, b) => a.getTime() > b.getTime() ? 1 : -1)[0];
ScriptApp.newTrigger(functionName).timeBased().at(triggerDate).create();
}
function wrapFunction() {
installTriggers();
copyDatabase();
SpreadsheetApp.flush();
exportTriggerFunction();
}
// ---
// --- Below script is your showing script.
function copyDatabase() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Index");
var desinationsheet = ss.getSheetByName("All Work Orders");
var startRow = 2;
var numRows = sheet.getLastRow() - startRow + 1;
var startCol = 23;
var numCols = 14;
var startColTarget = 1;
var dataRange = sheet.getRange(startRow, startCol, numRows, numCols);
var data = dataRange.getValues();
var Copy = "Copy";
var firstEmptyRow = sheet.getRange("A2:P").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
var dataToCopy = data.filter(row => row[2] !== Copy);
desinationsheet.getRange(firstEmptyRow, startColTarget, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
}
function exportTriggerFunction()
{
var today = new Date();
var lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);
if (today.getDate() == lastDayOfMonth.getDate() )
{
var exportSheetIds =
["560568812","1440952465","439649389","513481136",
"1088500659","133657514","1618608630","802444630",
"1834450484","657423004","682313931","1980296394","635144452"]; // Please set the sheet IDs you want to export.
const ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var name = sheet.getRange('Reference!M2').getDisplayValue();
var destination = DriveApp.getFolderById("1HRchNqQ5_0LYzfULw1hWN_ALNuijsy2q"); // Google Drive Folder Location
// 1. Copy the active Spreadsheet as a tempora Spreadsheet.
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet().copy('tmp');
const sheets = spreadsheet.getSheets();
const check = sheets.filter(s => exportSheetIds.includes(s.getSheetId().toString()));
if (check.length == 0) {
throw new Error("No export sheets.");
}
// 2. Convert the formulas to the texts.
sheets.forEach(sheet => {
if (exportSheetIds.includes(sheet.getSheetId().toString())) {
const targetRange = sheet.getDataRange();
targetRange.copyTo(targetRange, { contentsOnly: true });
}
});
// 3. Delete/Exclude sheets: Index and Reference
sheets.forEach(sheet => {
if (!exportSheetIds.includes(sheet.getSheetId().toString())) {
spreadsheet.deleteSheet(sheet);
}
});
SpreadsheetApp.flush();
// 4. Retrieve the blob from the export URL.
const id = spreadsheet.getId();
const xlsxBlob = UrlFetchApp.fetch(`https://docs.google.com/spreadsheets/export?id=${id}&exportFormat=xlsx`, { headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` } }).getBlob();
// 5. Create the blob as a file.
destination.createFile(xlsxBlob.setName(`${name}.xlsx`));
// 6. Delete the temporate Spreadsheet.
DriveApp.getFileById(id).setTrashed(true);
}
}
ScriptApp.newTrigger("copyDatabase")...
и ScriptApp.newTrigger("exportTriggerFunction")...
удалены. Если их не удалить, этот скрипт не сможет корректно работать. Пожалуйста, будьте осторожны с этим.спасибо за очень подробное и понятное руководство. Изучив ваши комментарии, я заново собрал скрипты, и они работают как положено, вполне достаточно. Также спасибо за дополнительные ссылки для обзора.
@Джарвис Дэвис Спасибо за ответ и тестирование. Я рад, что ваша проблема была решена. И тебе спасибо.
Сосредоточьте вопрос на одном из триггеров