Why are editor Apps Script executions different from trigger executions? - javascript

Have found nothing online on this:
I have built a script that repeatedly populates a sheet before copying it to a separate spreadsheet. The issue is my script behaves differently if executed from the editor versus when triggered from inside the spreadsheet.
When run from the Apps Script editor, the script runs as expected whereas when run from a custom menu item or a trigger in an embedded photo, my function populating my original sheet doesn't have a chance to finish execution before it is copied to a separate spreadsheet.
When executed from a trigger, my separate spreadsheet's copied sheet resembles my original sheet mid-execution. It gets copied before the function populating the original sheet is done executing. This is a problem since my code is meant for another person to use it directly from the spreadsheet.
edit (here's the code):
//loop thru supervisor, create new page
for (let i = 0; i < swConcise.length; i++){
let swName = swConcise[i];
//draw social worker report function
batchSocialWorker(swName);
//copy to alt ss
const sheet = baseSS.getSheetByName('SocialWorkersReport');
sheet.copyTo(newSS);
//rename sheet
newSS.getSheets()[i+1].setName(swName);
}
Edit (longer code if you need):
function batchSocialWorkers(){
/*
1. create new ss, set baseSS, newSS vars
2. create directors array
3. loop directors, copy sheet to newSS and rename
4. create link in new sheet
*/
const baseSS = SpreadsheetApp.getActiveSheet();
//create new ss titled date, sw Report
const today = date();
const title = "Social Workers Reports " + today;
const id = createSS (title);
const newSS = SpreadsheetApp.openById(id);
//scrape social workers names
const swAll = baseSS.getSheetByName('Relationships').getRange('F11:F').getValues();
//create concise s0cial workers array
const swConcise = cleanupArray(swAll);
//loop thru supervisor, create new page
for (let i = 0; i < swConcise.length; i++){
let swName = swConcise[i];
//draw social worker report function
batchSocialWorker(swName);
//copy to alt ss
const sheet = baseSS.getSheetByName('SocialWorkersReport');
sheet.copyTo(newSS);
//rename sheet
newSS.getSheets()[i+1].setName(swName);
}
//delete first 2 sheets
newSS.deleteSheet(newSS.getSheets()[0]);
newSS.deleteSheet(newSS.getSheets()[0]);
//provide link to new sheet
const link = 'https://docs.google.com/spreadsheets/d/' + id;
var ui = SpreadsheetApp.getUi();
ui.alert('Batch Social Workers Reports Created', link, ui.ButtonSet.OK);
}
function createSS (title) {
// This code uses the Sheets Advanced Service, but for most use cases
// the built-in method SpreadsheetApp.create() is more appropriate.
try {
let sheet = Sheets.newSpreadsheet();
sheet.properties = Sheets.newSpreadsheetProperties();
sheet.properties.title = title;
const spreadsheet = Sheets.Spreadsheets.create(sheet);
return spreadsheet.spreadsheetId;
} catch (err) {
// TODO (developer) - Handle exception
console.log('Failed with error %s', err.message);
}
}
function cleanupArray (array){
let newArray = new Array();
for (let i = 0; i < array.length; i++){
if (array[i][0] != '') newArray.push(array[i][0]);
}
return newArray;
}

This is partly a guess because you have not provided all of the code:
But try this. I still don't know which functions you are calling by a trigger. But I did see several errors in your code. I tried to repair what I can.
function batchSocialWorkers() {
const bss = SpreadsheetApp.getActive();
const title = "Social Workers Reports " + Utilities.formatDate(new Date(),bss.getSpreadsheetTimeZone(),"MM/dd/yyyy");//guess at the format
const ss = SpreadsheetApp.create(title);
const sh = bss.getSheetByName('Relationships')
const vs = sh.getRange('F11:F' + sh.getLastRow()).getValues();
for (let i = 0; i < vs.length; i++) {
let swName = vs[i];
batchSocialWorker(swName);
const sheet = bss.getSheetByName('SocialWorkersReport');
sheet.copyTo(ss);
ss.getSheets()[i + 1].setName(swName);
}
ss.deleteSheet(ss.getSheets()[0]);
ss.deleteSheet(ss.getSheets()[0]);
const link = 'https://docs.google.com/spreadsheets/d/' + id;
var ui = SpreadsheetApp.getUi();
ui.alert('Batch Social Workers Reports Created', link, ui.ButtonSet.OK);
}

Related

Triggering function from one master spreadsheet in other spreadsheet documents

I have a bunch of spreadsheets containing financial forecast. At the end of each month I make a copy to lock in the months forecast in every sheet. This works well. The problem is that I have to open all the sheets separately in order to activate the script in each sheet. So I was wondering if there is a way to trigger the script via a button from a master spreadsheet that looks something like this: https://docs.google.com/spreadsheets/d/1YA6twVP3_oSJttcLx5JRdm8TO01zKFr2k_D9lrlc1eE/edit#gid=0
The script I'd like to trigger looks like this:
function CopyMonth() {
const sh = SpreadsheetApp.getActive();
let ss = sh.duplicateActiveSheet();
let name = ss.getRange("a26").getDisplayValue();
ss.setName(name);
var sheet = SpreadsheetApp.getActiveSheet();
var rangeToCopy = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());
rangeToCopy.copyTo(sheet.getRange(1, 1), {contentsOnly: true});
}
The time and date I lock the forecast varies so it cannot be activated by a specific time or something like that.
The marked duplicate does not address my issue. The subject referenced to explains how to create a button to run a script. I'm asking how to run a script I've made in one document from an entirely different document. My problem is that I have 8 document I have to open and press a button today to run my script. I want to be able to run the scrip in all 8 documents from a master document instead.
Provided that all the spreadsheets you want to process are in the same folder, with no other spreadsheets in that folder or its subfolders, you can use something like this:
/**
* Makes a values only copy of the first tab in all
* spreadsheets in a folder and its subfolders.
*/
function insertValuesOnlyMonthTabInManySpreadsheets() {
// version 1.0, written by --Hyde, 14 December 2022
// - see https://stackoverflow.com/q/74801764/13045193
const folderName = '...put folder name here...';
const _action = (file) => {
const ss = getSpreadsheetFromFile_(file);
if (!ss) {
return;
}
const firstTab = ss.getSheets()[0];
const tabName = firstTab.getRange('A26').getDisplayValue();
let newTab;
try {
newTab = firstTab.copyTo(ss);
newTab.setName(tabName);
} catch (error) {
handleError_(error);
if (!newTab) {
return;
}
}
const rangeToCopyAsValuesOnly = firstTab.getDataRange();
rangeToCopyAsValuesOnly.copyTo(newTab.getRange(1, 1), { contentsOnly: true });
console.log(`Processed '${ss.getName()}'.`);
};
const folder = DriveApp.getFoldersByName(folderName).next();
processFilesInFolderRecursively_(folder, _action, handleError_);
}
For that to work, you will need to paste the getSpreadsheetFromFile_(), processFilesInFolderRecursively_() and handleError_() functions in the script project. You can get them from the processFilesInFolderRecursively_ script.

How to run a function based on an answer

I am trying to figure out how to make a script to run a function based on an answer provided, at the moment my script creates and populates a word doc with answers provided from a google form. I want to streamline it so that I can use one google form to create different documents based on an answer provided rather than creating multiple google forms. I am very new to JavaScript and I am pretty sure I need an if statement at the beginning of everything, but I don't know what I should write or where it should go. This is my script:
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs(){
const googleDocTemplate = DriveApp.getFileById('google file goes here');
const destinationFolder = DriveApp.getFolderById('google folder goes here');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Response');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row,index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[5]} document name goes here`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
body.replaceText('{{Name}}', row[5]);
body.replaceText('{{To}}', row[6]);
body.replaceText('{{Items}}', row[7]);
body.replaceText('{{Reasoning}}', row[8])
body.replaceText('{{Submitter}}', row[1]);
body.replaceText('{{Role}}', row[2]);
body.replaceText('{{Time}}', row[3]);
body.replaceText('{{Discord}}', row[4]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}

This Variation code doesn't work with my code in doStuff Function

I'm trying to fetch the data from the sheet cell B1 and do the math with that data but it doesn't work when I this line
var source = SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue();
But when i remove the above line, it works all fine.can i know the reasone? here is my code,
document.getElementById("btn").addEventListener("click", doStuff);
function doStuff() {
var source = SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue(); //Separate spreadsheet book
const rate = 0.155;
const vprice = document.getElementById("price").value;
const downPayment = document.getElementById("DownPay").value;
const borrow = vprice-downPayment;
const period = 12;// add motnhs here
const opp = vprice*0.5;
var nf = new Intl.NumberFormat(); //number format
const subL = Math.round((vprice*source)/2);
Unfortunately, in the current stage, at HTML&Javascript side (client side), Google Apps Script cannot be directly used. In your case, I think that google.script.run can be used for retrieving the values from Google Apps Script. So please modify as follows.
HTML&Javascript side:
Please modify doStuff() as follows.
function doStuff() {
google.script.run.withSuccessHandler(source => {
const rate = 0.155;
const vprice = document.getElementById("price").value;
const downPayment = document.getElementById("DownPay").value;
const borrow = vprice-downPayment;
const period = 12;// add motnhs here
const opp = vprice*0.5;
var nf = new Intl.NumberFormat(); //number format
const subL = Math.round((vprice*source)/2);
}).getValues();
}
Google Apps Script side:
Please add the following function.
function getValues() {
return SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue();
}
In this modification, when doStuff() is run, getValues() at Google Apps Script is run by google.script.run. And the returned value from getValues() can be retrieved at Javascript side using withSuccessHandler.
References:
Class google.script.run

How to pass Google/sheetsAPI data to website?

Sooooo,
I want to get data from a google sheet to a website which appends the passed data dynamically to the website so the amount of itme's on the website is equal to the amount of rows. I pretty much got everything done separately but I just cant seem to figure out how to combine them.
I set up a node.js project which uses my Api key to retrieve the data, manipulate and push it into an array.
also maybe there's a more efficient way to push the data but i couldn't figure anything else out but saving it to a file and then reading it back. I then pumped that into a AWS api gateway+Lambda funtion which didn't really work because I couldn't get Lambda to require google/api but i think i could sort that out.
I also already did all the HTML / JS on the website to push the data to the page I just cant seem to figure out how to get the data from my AWS-http-link into my website. No matter if I use async or promise or none of both It just can't get the data.
'ISSUES: using google/api inside Lambda, getting data from Api to array, maybe diffrent way to get the data from google api'
Thank you in advance!
AWS-link: https://po7bu16g2i.execute-api.eu-central-1.amazonaws.com/live/celldata
//JS code on website
var receivearray =[];
function createEntry(data){
for(i=0;i<data.length;i++){
var outdt = document.createElement('div');
outdt.className='outwrap';
var pers_name = document.createElement('div');
pers_name.className='name';
var pers_name_txt= document.createElement('h3');
pers_name_txt.className='name_head';
pers_name_txt.innerHTML=data[i][0];
outdt.append(pers_name);
pers_name.append(pers_name_txt);
for(k=1;k<data[i].length;k++){
var top =document.createElement('div');
top.className='text_wrap';
var q = document.createElement('p');
q.className='frage';
q.innerHTML=data[i][k][0];
var a= document.createElement('p');
a.innerHTML=data[i][k][1];
outdt.append(top);
top.append(q,a);
}
document.body.appendChild(outdt);
}
};
createEntry(receivearray);
//Node.js Code
//requirements
var GoogleSpreadsheet = require('google-spreadsheet');
var crds = require('./credt.json');
const fs = require('fs');
//updates the data-tfile
function update(sheetid){
//google api shapens
var doc = new GoogleSpreadsheet(sheetid);
doc.useServiceAccountAuth(crds,
function (err) {
doc.getRows(1,
function (err, rows) {
//saves all retrieved data into textfile
fs.writeFile('ting.txt', JSON.stringify(rows),
function (err) {
//checks if file was saved succesfully
if (err) throw err;
console.log('updated')
}
)
}
)
}
)
}
function prepdata(){
//define variables
var text = fs.readFileSync("ting.txt", "utf8");
var findata =[];
var hold=[];
//singleout all row-elements
for(x=0;x<text.length;x++){
if(text.slice(x,x+7)=='"name":'){
hold.push(text.slice(x+7,x+120));
}
}
//split data by , into sets
for(x=0;x<hold.length;x++){
findata.push(hold[x].split(','));
}
//push sets into arrays
for (var k = 0; k < findata.length; k++){
for (var i = 0; i < findata[k].length; i++){
findata[k][i] = findata[k][i].replace(/"/g, " ");
findata[k][i] = findata[k][i].split(':');
}
}
return findata;
}
update("1W6tVuj0krrwI7PyTRJha2ZOX72kGGfFAI8eqXOirWHo");
console.log(prepdata());

Google Apps Script: Script function not found: FormatCWR

I am trying to build a function that formats certain columns of a tab (a sheet within the main sheet) that is titled "Responses - DO NOT EDIT." However, every time I run the script from the menu, I receive the error message: "Script function not found: FormatCWR."
Here's the code, that I've frankensteined...
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{name:"FormatCWR", functionName:"FormatCWR"}];
ss.addMenu("Scripts", entries);
FormatCRW()
}
function FormatCRW() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetCWR = ss.getSheetByName("Responses - DO NOT EDIT");
var data = sheetCWR.getDataRange().getValues();
var newData = new Array();
for(i in data){
sheetCWR.getRange('H2:H').setNumberFormat("mm/dd/yyyy");
sheetCWR.getRange('J2:J').setNumberFormat('$0.00');
sheetCWR.getRange('K2:K').setNumberFormat('$0.00');
sheetCWR.getRange('R2:R').setNumberFormat('$0.00');
sheetCWR.getRange('BD2:BD').setNumberFormat('$0.00');
sheetCWR.getRange('BG2:BG').setNumberFormat('$0.00');
sheetCWR.getRange('BJ2:BJ').setNumberFormat('$0.00');
sheetCWR.getRange('S2:S').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('T2:T').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('BO2:BO').setNumberFormat('[h]:[m]:[s]');
break;
}
};
I assume the function is in a ".gs" file and that it really is there. So it's possible that something is wrong somewhere else. I know a lot people make their menus in a manner that is similar to what you're doing. However, I like doing it this way.
SpreadsheetApp.getUi().createMenu('Scripts')
.addItem('Format','FormatCRW')
.addToUi();
Maybe this will help...Maybe not.
I'm probably missing something here but this function makes no sense to me.
function FormatCRW() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetCWR = ss.getSheetByName("Responses - DO NOT EDIT");
var data = sheetCWR.getDataRange().getValues();
var newData = new Array();//Not used at all
for(i in data){//Why are you looping on every row in the data range when all of the ranges cover all of the rows?
sheetCWR.getRange('H2:H').setNumberFormat("mm/dd/yyyy");
sheetCWR.getRange('J2:J').setNumberFormat('$0.00');
sheetCWR.getRange('K2:K').setNumberFormat('$0.00');
sheetCWR.getRange('R2:R').setNumberFormat('$0.00');
sheetCWR.getRange('BD2:BD').setNumberFormat('$0.00');
sheetCWR.getRange('BG2:BG').setNumberFormat('$0.00');
sheetCWR.getRange('BJ2:BJ').setNumberFormat('$0.00');
sheetCWR.getRange('S2:S').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('T2:T').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('BO2:BO').setNumberFormat('[h]:[m]:[s]');
break;//Why have a loop if your going to break out the very first time?
}
};
It would make a little more sense this way
function FormatCRW() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetCWR = ss.getSheetByName("Responses - DO NOT EDIT");
sheetCWR.getRange('H2:H').setNumberFormat("mm/dd/yyyy");
sheetCWR.getRange('J2:J').setNumberFormat('$0.00');
sheetCWR.getRange('K2:K').setNumberFormat('$0.00');
sheetCWR.getRange('R2:R').setNumberFormat('$0.00');
sheetCWR.getRange('BD2:BD').setNumberFormat('$0.00');
sheetCWR.getRange('BG2:BG').setNumberFormat('$0.00');
sheetCWR.getRange('BJ2:BJ').setNumberFormat('$0.00');
sheetCWR.getRange('S2:S').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('T2:T').setNumberFormat('[h]:[m]:[s]');
sheetCWR.getRange('BO2:BO').setNumberFormat('[h]:[m]:[s]');
}

Categories

Resources