I wish to run the next script, with a script.
When function "ConfigureTest1" has run, it should simply start "ConfigureTest2" and so on...
This is what I have, so far:**
function ConfigureTest1() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sh = ss.getSheetByName('TEST')
sh
.getRange('A2:A')
.createTextFinder('T')
.replaceAllWith('TEST')
ScriptApp.newTrigger('ConfigureTest2') // RUN function ConfigureTest2
.create();
}
function ConfigureTest2() {
let ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("AUTO")
let data = ss.getRange(1, 1, ss.getLastRow(), 1).getValues()
let initialData = data.map(function(r) {
return r[0];
})
let conde = ["TEST"]
let writea = ["TEST"]
let rowNumber = 2
for (i = 0; i <= ss.getLastRow() - 2; i++) {
for (j = 0; j <= conde.length - 1; j++) {
if (initialData[i].includes(conde[j])) {
ss.getRange("B" + rowNumber).setValue(writea[j])
break
}
ss.getRange("B" + rowNumber).setValue("TEST")
}
rowNumber++
}
}
You can just add ConfigureTest2() after creating the trigger like this:
function ConfigureTest1() {
//what you already have
ConfigureTest2()
}
I'm not sure what's your final purpose with the script, but as some additional advice, the TriggerBuilder documentation explains that you first need to specify what kind of trigger this is along with the ID of the document that you want to attach it to, and then specify the type of trigger, so your trigger creation should look more like this:
ScriptApp.newTrigger('ConfigureTest2')
.forSpreadsheet("<Your-spreadsheet-ID>")
.onOpen() //or edit, etc.
.create();
And also note that the includes method applies to an entire array, but you're trying to call it on just one element of it, so instead of this:
if (initialData[i].includes(conde[j])) {
You need to change it to this:
if (initialData.includes(conde[j])) {
This does copy some "TEST" data to a different column when you run ConfigureTest1, but again, I'm not sure what your goal is. At least this should solve the syntax errors so you can focus on the logic of the script.
Related
This problem is very annoying. So, I am making a scheduled trigger run every 24 hours. It simply gets items from one collection does some data processing then appends information to another collection. The functioning code works even when the function runs. But it will not let me save because there are "runtime" errors? Even though it was executed perfectly and returned.
Console Error
> result (JavaScript):
EJSON.parse('{"$undefined":true}')
I suppose this has something to do with returning. but when I return null I get this:
> result:
null
> result (JavaScript):
EJSON.parse('null')
when trying to save I get this at the top of the page:
runtime error during function validation
Function Code:
exports = async function() {
const usersCol = context.services.get("SchoologyDashCluster").db("SchoologyDashApp").collection("users");
const gradesCol = context.services.get("SchoologyDashCluster").db("SchoologyDashApp").collection("grades");
var usersCusor = await usersCol.find( ).toArray();
var gradesCusor = await gradesCol.find( ).toArray();
let insert = [];
for (let i = 0; i < usersCusor.length; i++) {
var user = usersCusor[i];
var userSavedGrades = gradesCusor[i].grades
var currentGrades = await getGrades(user.schoologyUID, user.consumerKey, user.secretKey);
var lastGraded = NaN;
let index = gradesCusor[i].grades.length - 1;
while (true) {
if (gradesCusor[i].grades[index].changed == 1) {
lastGraded = index;
break
}
index = index - 1;
}
console.log(lastGraded)
if (userSavedGrades[lastGraded].grades.ga == currentGrades.ga){
currentGrades = { changed : 0, time: new Date().getTime()};
} else {
currentGrades = {changed : 1, grades: currentGrades, time : new Date().getTime()};
}
gradesCol.updateOne(
{"user" : user._id},
{"$push" : {"grades" : currentGrades}}
)
}
// return usersCol.find( );
return null;
};
The answer was simple and now I feel ignorant. Instinctual I put the module imports at the top of the document. However this is incorrect and they need to be placed in the exports function, like so:
exports = function (x,y,z) {
const http = context.http;
return;
}
I am trying to create a class to import Google Sheets tables into a more maneuverable. The code:
class SheetData{
constructor(worksheet, spreadsheet=null) {
this.spreadsheet = spreadsheet ? spreadsheet : SpreadsheetApp.getActive();
this.sheet = this.spreadsheet.getSheetByName(worksheet);
this.values = this.sheet.getDataRange().getValues();
}
get records() {
let cols = this.values[0], temp = {}, out = [];
for (let row of this.values.slice(1)){
cols.forEach( (colName,idx) => temp[colName] = row[idx] );
out.push(temp);
temp = {};
}
}
How ever when I try to run it on a sheet Logger.log(new SheetData('Sheet1').values), I get an Unexpected identifier at the new. What am I doing wrong? I also am not getting any syntax highlighting in the editor, even though I have the V8 runtime enabled.
How about this modification?
Modification points:
If you are testing your script in your question, I think that in your script, } is required to be added at the last line (
} is one shortage.).
I think that this might be the reason of your issue.
And, new SheetData('Sheet1').records is run, undefined is returned. Because no value is returned.
When above points are reflected to your script, it becomes as follows.
Modified script:
class SheetData{
constructor(worksheet, spreadsheet=null) {
this.spreadsheet = spreadsheet ? spreadsheet : SpreadsheetApp.getActive();
this.sheet = this.spreadsheet.getSheetByName(worksheet);
this.values = this.sheet.getDataRange().getValues();
}
get records() {
let cols = this.values[0], temp = {}, out = [];
for (let row of this.values.slice(1)){
cols.forEach( (colName,idx) => temp[colName] = row[idx] );
out.push(temp);
temp = {};
}
return out; // <--- Added
}
} // <--- Added
// Please run this function.
function main() {
var s = new SheetData('Sheet1');
console.log(s.values); // or Logger.log(s.values);
console.log(s.records); // or Logger.log(s.records);
}
Note:
Please confirm whether V8 runtime is enabled again.
This question already has an answer here:
onEdit trigger doesn't catch current user
(1 answer)
Closed 3 months ago.
I have a Google spreadsheet with some data. I wrote script to track changes of some specific columns.
function onOpen() {
var ss = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Turn on', functionName: 'createSpreadsheetEditTrigger'}
];
ss.addMenu('Tracker', menuItems);
}
function changeTrack(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ui = SpreadsheetApp.getUi();
var ws = ss.getActiveSheet();
const headerRow = 4;
const editBodyCols = [2, 3, 4, 5];
const fResultCol = 6;
var range = ws.getActiveRange();
var row = range.getRow();
var col = range.getColumn();
let target1 = ws.getRange(row, fResultCol);
let target2 = ws.getRange(row, fResultCol + 1)
let activeUser = getCurrentUserEmail();
if(row > headerRow && editBodyCols.some(x => x === col) === true){
if(target1.getValue() !== ""){
target2.setValue(result(ss, ws, row, activeUser)[1]);
} else {
target1.setValue(result(ss, ws, row, activeUser)[0])
target2.setValue(result(ss, ws, row, activeUser)[1])
}
}
}
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('changeTrack')
.forSpreadsheet(ss).onEdit()
.create();
}
function date(){
return Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss");
}
function result(ss, ws, row, activeUser) {
const ssName = ss.getName();
let data = `Создал ${activeUser} ${date()}`;
let exp = `Файл ${ssName}, Лист ${ws.getName()}, изменил ${activeUser}, строка № ${row}, ${date()}`;
let adds = [];
adds.push([data],[exp]);
return adds;
}
function getCurrentUserEmail()
{
var email=Session.getActiveUser().getEmail();
return email;
}
My problem is to get active user's email. This script can get it but not all the time. Seems like random success. It means sometimes I can get expected value, sometimes not. I don't understand what is it depends from.
Where I'm wrong and how to fix it?
From the documentation on Session.getActiveUser():
Gets information about the current user. If security policies do not allow access to the user's identity, User.getEmail() returns a blank string. The circumstances in which the email address is available vary: for example, the user's email address is not available in any context that allows a script to run without that user's authorization, like a simple onOpen(e) or onEdit(e) trigger, a custom function in Google Sheets, or a web app deployed to "execute as me" (that is, authorized by the developer instead of the user).
So this seems pretty expected and there is no hard workaround you can make to retrieve the users mail. You should maybe just ask for it and see if they be willingly want to give it to you.
Although if you are the developer or the users are inside your organization this restrictions may be ignored:
However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same G Suite domain as the user.
Based on the comment by b-frid.
The flow:
create a custom menu and tell each user to run the function twice. First time for authorization, and the second time to actually run the code.
this will install the same trigger with the user's privileges and let the onEdit trigger get the email of an active user (author of the trigger)
video-instruction:
https://www.loom.com/share/30b11e4d012447f7a1efdd9a7eac4fca
gist:
https://gist.github.com/Max-Makhrov/e3e89e7fe0c6e86b68a4be1262e53629
Code:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('😎📬 run me 2x times')
.addItem('please let script to see your email', 'install')
.addToUi();
}
// function test() {
// var e = {
// range: SpreadsheetApp.getActive().getSheetByName('test_onEdit').getRange('B2')
// }
// edit_(e)
// }
function edit_(e) {
var targetcol = 1;
if (e.range.getSheet().getName() === 'Sheet1') {
var user = Session.getActiveUser().getEmail();
if (user !== '') {
var col = e.range.getColumn();
var rows = e.range.getHeight();
var ratgetrange = e.range.offset(
0,
targetcol - col,
rows,
1);
ratgetrange.setValue(user);
}
}
}
function install() {
setOnEditTrigger_(
SpreadsheetApp.getActive().getId(),
'edit_');
}
/**
* create onEdit trigger
*
* #param {string} spreadsheetId
* #param {string} functionName
*
*/
function setOnEditTrigger_(spreadsheetId, functionName) {
console.log('OnEdit trigger ' + functionName +
' for new file' +
spreadsheetId);
var trigger;
if (existsOnEditTrigger_(functionName, spreadsheetId)) {
console.log('stopped execution. Trigger exists.');
return;
}
trigger = ScriptApp
.newTrigger(functionName)
.forSpreadsheet(spreadsheetId)
.onEdit()
.create();
console.log('Created new trigger!')
return trigger.getUniqueId();
}
/**
* check if onEdit trigger exists
*
* #param {string} spreadsheetId
* #param {string} functionName
*
*/
function existsOnEditTrigger_(functionName, spreadsheetId) {
var triggers = ScriptApp.getProjectTriggers();
var trigger = {};
for (var i = 0; i < triggers.length; i++) {
trigger = triggers[i];
if (
trigger.getHandlerFunction() === functionName &&
trigger.getTriggerSourceId() === spreadsheetId &&
trigger.getEventType().toString() === 'ON_EDIT') return true;
}
return false;
}
Original comment:
The key is separate triggers for each user. Then I simply added code
to execute the update only if the Session.getActiveUser().getEmail()
call does not come back blank. Of course, because each's user's
trigger will run, the function will execute x times where x = the
number of users (i.e. triggers), but with the check for a blank return
value the logic only runs once (and so no overwrites). A bit clumsy
indeed, and perhaps not practical if you have more than a handful of
users, but workable in my case.
I have a form that my users enter data and I use onFormSubmit to trigger a script that creates a CSV based on the data inserted and after I create the CSV file I delete the data. Problem is that I am deleting the data before creating CSV file. Usually I would just make a promise call but I think it is not possible with Google Apps Script. Is there any alternative to it?
So my complete code is here:
More insight about what it does:
When I receive a new form entry, the "Avaliacao" sheet gets updated and will trigger testTrigger().
Then, it will write the email and the usercity in the LastUser city so it can lookup for some data that I will use to build my CSV file. But the saveAsCsv function is called before the sheet completed its VLOOKUP calls. So my CSV file is empty.
Another issue that I have with synchronization is that if I enable the sLastUser.clear(); line it will also delete before creating the CSV.
function testTrigger () {
//open the sheets
var SS = SpreadsheetApp.getActiveSpreadsheet();
var sAvaliacao = SS.getSheetByName("Avaliação");
var sPreCSV = SS.getSheetByName("PreCSV");
var sInput = SS.getSheetByName("Input");
var sLastUser = SS.getSheetByName("LastUser");
var dAvaliacao = sAvaliacao.getDataRange().getValues();
var dInput = sInput.getDataRange().getValues();
var avaliacaoLastRow = sAvaliacao.getLastRow()-1;
var userEmail = dAvaliacao[avaliacaoLastRow][2];
var userCity = dAvaliacao[avaliacaoLastRow][5];
var userId = dInput[3][52];
sLastUser.appendRow([userEmail, userCity]);
saveAsCSV(userId);
// sLastUser.clear(); <== this is the line where I can`t enable
}
function saveAsCSV(csvName) {
// Name
var fileName = String(csvName) + ".csv"
// Calls convertcsv
var csvFile = convertOutputToCsv_(fileName);
// create the file on my drive
DriveApp.createFile(fileName, csvFile);
}
function convertOutputToCsv_(csvFileName) {
// open sheets
var sPreCSV = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PreCSV");
var cont = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input").getDataRange().getValues()[3][50] + 1;
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PreCSV").getRange(1, 1, cont, 3);
try {
var data = ws.getValues();
var csvFile = undefined;
// Loop through the data in the range and build a string with the CSV data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// Join each row's columns
// Add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
Now with engine v8 Promise is defined object
Indeed, the JS engine used by Google Apps Script does not support promises (Logger.log(Promise); shows it's undefined).
To make sure that spreadsheet changes take effect before proceeding further, you can use SpreadsheetApp.flush().
Another relevant feature is event object passed by the trigger on Form Submit: it delivers the submitted data to the script without it having to fish it out of the spreadsheet.
There are no native Promises in GAS. With that said you can write your code in ES6/ESnext and make use of promises that way. To see how you can configure modules to expose the to the global scope see my comment here.
By doing this you can take advantage of promises. With that said depending on the size of your project this may be overkill. In such a case I'd advise using simple callbacks if possible.
I have minimal knowledge of Javascript, but would like to use this code to enhance my GMail experience. It works, but I also get errors. When I run the debugger in Google Spreadsheet, two functions appear to malfunction:
TypeError: Cannot call method "getThreads" of null. (line 59)
With the following Execution transcript
GmailApp.getUserLabelByName([FollowUp])
GmailApp.getUserLabelByName([FollowUp/1undefined])
Inserted comment: there is some information about the GMail API call getThreads (and others) here: https://developers.google.com/apps-script/class_gmaillabel#getThreads
What I don't get is why it is calling Followup/1undefined -> why is it undefined? It should be Followup/1days
And another error with another function:
Cannot find method moveThreadsToInbox(. (line 26)
With nothing in the Execution transcript
The entire code is:
// Adapted from:
// http://gmailblog.blogspot.com/2011/07/gmail-snooze-with-apps-script.html
//
// To setup:
// - From the |Run| menu select |setup|
// - if prompted to authorize, do so, and then repeat this step.
//
// - Verify the script is set to be triggered to run
// - |Triggers| menu |Current script's triggers...|
// - 3 triggers should exist to call e.g.
// - dailyUpdate, Time Driven, Daily
function getLabelName(i, labelSuffixString) {
return "FollowUp/" + i + labelSuffixString;
}
function setup() {
for (var i = 1; i <= 7; ++i) {
GmailApp.createLabel(getLabelName(i, "days"));
GmailApp.createLabel(getLabelName(i, "weeks"));
}
GmailApp.createLabel("FollowUp");
}
function moveToInbox(page) {
GmailApp.moveThreadsToInbox(page);
GmailApp.markThreadsImportant(page);
}
function cleanOldFollowUpLabels() {
var searchString = "-label:inbox label:FollowUp";
for (var i = 1; i <= 7; ++i) {
searchString += " -label:" + getLabelName(i, "days");
searchString += " -label:" + getLabelName(i, "weeks");
}
searchString = searchString.replace(RegExp("/", "g"), "-");
Logger.log("cleanOldFollowUpLabels() Search String:");
Logger.log(searchString);
var followUpLabel = GmailApp.getUserLabelByName("FollowUp");
var page = null;
// Get threads in "pages" of 100 at a time
while(!page || page.length == 100) {
page = GmailApp.search(searchString, 0, 100);
Logger.log("found: " + page.length);
if (page.length > 0)
followUpLabel.removeFromThreads(page);
}
}
function update(labelSuffixString) {
var oldLabel, newLabel, page;
var followUpLabel = GmailApp.getUserLabelByName("FollowUp");
for (var i = 1; i <= 7; ++i) {
newLabel = oldLabel;
oldLabel = GmailApp.getUserLabelByName(getLabelName(i, labelSuffixString));
page = null;
// Get threads in "pages" of 100 at a time
while(!page || page.length == 100) {
page = oldLabel.getThreads(0, 100);
if (page.length > 0) {
followUpLabel.addToThreads(page);
if (newLabel) {
// Move the threads into "today’s" label
newLabel.addToThreads(page);
} else {
moveToInbox(page);
}
// Move the threads out of "yesterday’s" label
oldLabel.removeFromThreads(page);
}
}
}
}
function dailyUpdate() {
update("days");
}
function weeklyUpdate() {
update("weeks");
}
Also here: http://pastie.org/4790086.js
mm.. with the help of a colleague, we found the answer to my own problem. I had three triggers running: A daily trigger, a weekly trigger, and the update trigger. Now the update trigger was not necessary, because it was called upon by the daily and weekly trigger, and without any input. That caused the error. Now I have to wait and see if there are any errors and if the script works.