This script does not populate sheet after parsing retrieved data - google-apps-script

I hope this is well explained. First of all, sorry because my coding background is zero and I am just trying to "fix" a previously written script.
Problem The script does not populate sheet after parsing retrieved data if the function is triggered by timer and the sheet is not open in my browser .
The script works OK if run it manually while sheet is open.
Problem details:
When I open the sheet the cells are stuck showing "Loading" and after a short time, data is written.
Expected behavior is to get the data written no matter if I don't open the sheet.
Additional info: This is how I manually run the function
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [
{name: "Manual Push Report", functionName: "runTool"}
];
sheet.addMenu("PageSpeed Menu", entries);
}
Additional info: I set the triggers with Google Apps Script GUI See the trigger
Before posting the script code, you can see how the cells look in the sheet:
Script code
function runTool() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Results");
var rows = activeSheet.getLastRow();
for(var i=3; i <= rows; i++){
var workingCell = activeSheet.getRange(i, 2).getValue();
var stuff = "=runCheck"
if(workingCell != ""){
activeSheet.getRange(i, 3).setFormulaR1C1(stuff + "(R[0]C[-1])");
}
}
}
// URL check //
function runCheck(Url) {
var key = "XXXX Google PageSpeed API Key";
var strategy = "desktop"
var serviceUrl = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=" + Url + "&key=" + key + "&strategy=" + strategy +"";
var array = [];
var response = UrlFetchApp.fetch(serviceUrl);
if (response.getResponseCode() == 200) {
var content = JSON.parse(response.getContentText());
if ((content != null) && (content["lighthouseResult"] != null)) {
if (content["captchaResult"]) {
var score = content["lighthouseResult"]["categories"]["performance"]["score"];
} else {
var score = "An error occured";
}
}
array.push([score,"complete"]);
Utilities.sleep(1000);
return array;
}
}
You can try the code using the sheet below with a valid Pagespeed API key.
You only need to add a Trigger and wait for it's execution while the sheet is not open in your browser
https://docs.google.com/spreadsheets/d/1ED2u3bKpS0vaJdlCwsLOrZTp5U0_T8nZkmFHVluNvKY/copy

I suggest you to change your algorithm. Instead of using a custom function to call UrlFetchApp, do that call in the function called by a time-driven trigger.
You could keep your runCheck as is, just replace
activeSheet.getRange(i, 3).setFormulaR1C1(stuff + "(R[0]C[-1])");
by
activeSheet.getRange(i, 3, 1, 2).setValues(runCheck(url));
NOTE
Custom functions are calculated when the spreadsheet is opened and when its arguments changes while the spreadsheet is open.
Related
Cache custom function result between spreadsheet opens

Related

Google Scripts - View Log stuck at "Waiting for logs, please wait..."

I'm trying to run a script for my Google Sheet on Scripts, and a function isn't working properly. I have some loggers in place to check why this is happening, but anytime I try to open the Logs tab, I get this:
... and it's just stuck there forever.
Has anyone ever had this problem? Any potential fixes? Thanks
EDIT: My executions window looks like so:
EDIT 2: Here is the code I'm trying to run, with segment = 1. SPREADSHEETS is just a variable that I'm unfortunately not able to share, but it just contains some import segment information that directs to either 1 or 2.
function CopyPasteAllSheets(segment) {
for (x in SPREADSHEETS) {
if (SPREADSHEETS[x].IMPORTSEGMENT != segment) {
// DRR added app which is redundant to intakeSpreadhseet, but keeps logic more readable
app.toast('running loop')
console.log("ID: " + SPREADSHEETS[x].SOURCE.ID + "NO MATCH");
} else {
// Logger.log("x: "+ x) // keep commented out
var intakeSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].INTAKE.ID);
var intakeSheet = intakeSpreadsheet.getSheetByName(SPREADSHEETS[x].INTAKE.SHEET); //confirm formatting conventions
// This is functionally equivlent to the above, except we don't have a reference to intakeSpreadsheet anymore
// Access the Spreadsheet and sheet you want to copy the data TO
console.log("ID: "+ SPREADSHEETS[x].SOURCE.ID)
var sourceSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].SOURCE.ID);
var sourceSheet = sourceSpreadsheet.getSheetByName(SPREADSHEETS[x].SOURCE.SHEET);
var sourceStartRow = SPREADSHEETS[x].SOURCE.STARTROW;
var sourceStartCol = SPREADSHEETS[x].SOURCE.STARTCOL;
var sourceRangeCol = SPREADSHEETS[x].SOURCE.ENDCOL - SPREADSHEETS[x].SOURCE.STARTCOL + 1;
// Get the range of the data you want and the range where you want the data to go
var rowsToCopy = sourceSheet.getLastRow()-sourceStartRow+1; // is +1 too conservative, check...
var rangeToCopy = sourceSheet.getRange(sourceStartRow,sourceStartCol,rowsToCopy, sourceRangeCol);
var dataToCopy = rangeToCopy.getValues();
var numRows = rowsToCopy;
var numColumns = sourceRangeCol;
var intakeStartRow = SPREADSHEETS[x].INTAKE.STARTROW;
var intakeStartCol = SPREADSHEETS[x].INTAKE.STARTCOL;
var rangeToPaste = intakeSheet.getRange(intakeStartRow,intakeStartCol, numRows,numColumns); // WAS FORMERLY 1,20, ..,.. ~DRR 7/14
rangeToPaste.setValues(dataToCopy);
}
}
}

Pushing a simple Log string from a Google Ad Script to a Google Sheet

I am trying to set up a script which can push data from an App Script into a Google Sheet.
I have the script successfully logging what I want, which goes in the following format Account budget is 12344, but now I want to push this into a Google Sheet. I have set up a variable containing the URL and another variable containing the sheet name, and also a clear method to delete anything already there.
Find the code I have below:
// - The link to the URL
var SPREADSHEET_URL = 'abcdefghijkl'
// - The name of the sheet to write the data
var SHEET_NAME = 'Google';
// No to be changed
function main() {
var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = spreadsheet.getSheetByName(SHEET_NAME);
sheet.clearContents();
}
function getActiveBudgetOrder() {
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
Logger.log("Budget Order Amount " + budgetOrder.getSpendingLimit());
}
}
Assuming you want to clear the entire Sheet every time you extract the data this should work for you. You will need to set the url and shtName variables.
function getActiveBudgetOrder() {
var url = 'https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxx/';
var shtName = 'Sheet1';
var arr = [];
var sht = SpreadsheetApp.openByUrl(url).getSheetByName(shtName);
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
arr.push(["Budget Order Amount " + budgetOrder.getSpendingLimit()]);
}
sht.clearContents();
sht.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}

Send email when cell contains a specific value

I am working on a Google Form that allows our employees to submit an in-field inspection of their equipment. I have a script that takes the form responses and creates a new sheet based on the date and the specific unit number of the equipment. The user goes through a checklist and selects either "Good" or "Needs Repair" for each item on the list. They can also add comments and upload pictures of any issues.
I am trying to have the script automatically send an email if "Needs Repair" is selected for any of the checks, as well as if the user adds a comment or a picture. This way we do not have to open every submitted sheet to know if any repairs are required. What I have is just not sending emails and I cannot figure out why. Any help is greatly appreciated!
Here is my current script:
function onFormSubmit() {
// onFormSubmit
// get submitted data and set variables
var ss = SpreadsheetApp.openById("*Spreadsheet Link*");
var sheet = ss.getSheetByName("Submissions");
var row = sheet.getLastRow();
var Col = sheet.getLastColumn();
var headings = sheet.getRange(1,1,1,Col).getValues();
var lastRow = sheet.getRange(row, 1, 1, Col);
var UnitNumber = sheet.getRange(row,3).getValue();
var newSheet = sheet.getRange(row,4,Col).getValue();
var fileExist = false;
var drillSheet = null;
var folder = DriveApp.getFoldersByName("Fraser Drill Inspections").next();
var files = folder.getFilesByName(UnitNumber);
var file = null;
var employee = sheet.getRange(row,2);
var checks = sheet.getRange(row, Col, 1, 20);
// check if Drill has sheet
while (files.hasNext())
{
fileExist = true;
file = files.next();
break;
}
if (fileExist) //If spreadsheet exists, insert new sheet
{
drillSheet = SpreadsheetApp.openById(file.getId());
drillSheet.insertSheet("" + newSheet);
}
else //create new spreadsheet if one doesn't exist
{
drillSheet = SpreadsheetApp.create(UnitNumber);
var ssID = drillSheet.getId();
file = DriveApp.getFileById(ssID);
file = file.makeCopy(UnitNumber, folder);
DriveApp.getFileById(ssID).setTrashed(true);
drillSheet = SpreadsheetApp.openById(file.getId());
drillSheet.renameActiveSheet(newSheet);
}
// copy submitted data to Drill sheet
drillSheet.getSheetByName(newSheet).getRange(1,1,1,Col).setValues(headings);
drillSheet.appendRow(lastRow.getValues()[0]);
drillSheet.appendRow(['=CONCATENATE(B6," ",B5)']);
drillSheet.appendRow(['=TRANSPOSE(B1:2)']);
//Hide top rows with raw data
var hiderange = drillSheet.getRange("A1:A3");
drillSheet.hideRow(hiderange);
//Widen columns
drillSheet.setColumnWidth(1,390);
drillSheet.setColumnWidth(2,700);
//Send email if there are any comments or if anything needs repair
if(lastRow.getValues() == "Needs Repair") {
function SendEmail() {
var ui = SpreadsheetApp.getUi();
MailApp.sendEmail("email#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.")
}
}
}
The function to send an email is:
GmailApp.sendEmail(email, subject, body);
Try changing
if(lastRow.getValues() == "Needs Repair") {
function SendEmail() {
var ui = SpreadsheetApp.getUi();
MailApp.sendEmail("email#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.")
}
}
to just the following:
if(lastRow.getValues() == "Needs Repair") {
GmailApp.sendEmail("youremail#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.");
}
It looks like you've still got some additional work to do too, e.g. to make it send to the email address from the form submission instead of a hardcoded one.

Uploading Google Spreadsheet to a Google Site

I'd like to begin by stating that the end goal is to display our company directory (a list of our employees names/job title/extension#/office location/email), which is in a Google Sheet, on a page in one of our Google Sites.
I tried to use Google's embed function, and it works... but it is very clunky, does not have a "Sort" function, and it just looks weird.
I pulled a Google Apps Script from somewhere online like 3 months ago and it actually did pull in a way that made me happy:
(This is as it appears currently on the Google Sites page. So in this screenshot, the embedded Sheet is at the top. The Sheet when, imported via the script, is below. Yes, they are both on the same page. I'm in testing!)
This is the code I used (I THINK - I don't remember how I implemented it):
function myFunction() {
}
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(ScriptProperties.getProperty("page url") == null){
ss.addMenu("List page", [{name: "Create list", functionName: "create_list"},null,
{name: "Fetch list items", functionName: "fetch_items"}]);
}
else{
ss.addMenu("List page", [{name: "Push Items", functionName: "push_items"}]);
}
}
function create_list() {
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var parent_page = Browser.inputBox("URL of the parent page:");
var title = Browser.inputBox("Choose a name for your list page:");
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(parent_page).createListPage(title, title.split(' ').join(''), '', data[0]);
ScriptProperties.setProperty("page url", list.getUrl());
onOpen();
push_items();
}
function push_items(){
var done = false;
while(!done){
try{
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(ScriptProperties.getProperty("page url"));
var list_items = list.getListItems();
for(i in list_items){
list_items[i].deleteListItem();
}
for(var i = 1; i < data.length; i++){
var item = list.addListItem(data[i]);
}
done = true;
}
catch(e){
}
}
SpreadsheetApp.getActiveSpreadsheet().toast(ScriptProperties.getProperty("page url"), "List page updated", 10);
}
function fetch_items(){
var url = Browser.inputBox("URL of your list page:");
var col_number = Browser.inputBox("Number of columns in the list:");
var data = new Array();
var list_items = SitesApp.getPageByUrl(url).getListItems();
for(i in list_items){
var row = new Array();
for(j = 0; j < col_number; j++){
row.push(list_items[i].getValueByIndex(j));
}
data.push(row);
}
SpreadsheetApp.getActiveSheet().getRange(1, 1, data.length, data[0].length).setValues(data);
}
[I do not take credit for writing this!]
So I would like to ask (since this ceases to make much sense to me) is if this is viable code for a Google Apps Script, and if so, how do I implement it to output Sheet data similarly in the same type of format as in the screenshot?
Alternatively, is there a better way to display this Sheet data in Google Sheets?
A totally different alternative would be to use Romain Vialard's "awesome tables" gadget. It works... awesome, and it is really easy to use. Besides, it admits filters, ...

Getting TypeError in onFormSubmit trigger?

I used a Google Forms tutorial to tweak Form data to merge into a PDF and then send to an email. I am getting the following error message when I try to run the script:
TypeError: Cannot read property "values" from undefined. (line 11, file "Code")
I do not know how to fix the problem. I have searched the web for an answer. Here is a copy of the script. I marked the 2 lines where the script is giving an error:
var docTemplate = "1ZSqmId2BBjtz6PmgQEmusjnkHGsFKD1CBSq0rrQk6Kc";
var docName = "TestCertificate";
// When Form Gets submitted
function onFormSubmit(e) {
//Get information from form and set our variables
var email_address = "EMAIL#example.com";
//**(THIS IS WHERE THE ERROR IS OCCURRING ON THESE 2 LINES BELOW!)**
var full_name = e.values[2];
var Activity = e.values[3];
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName+' for '+full_name)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys,in our google doc template
copyBody.replaceText('keyFullName', full_name);
copyBody.replaceText('keyActivity', Activity);
// Save and close the temporary document
copyDoc.saveAndClose();
// Convert document to PDF
var pdf = DocsList.getFileById(copyId).getAs("application/pdf");
// Attach PDF and send the email
var subject = "Report";
var body = "Here is the form for " + full_name + "";
MailApp.sendEmail(email_address, subject, body, {htmlBody: body, attachments: pdf});
// Delete temp file
DocsList.getFileById(copyId).setTrashed(true);
}
Here are links to the form and certificate I was testing.
Form/Spreadsheet
Document Template
The error you're seeing is because you're running a trigger function in the Script Editor. When you do this, the Event Parameter e is not defined - that's what the error message is saying.
For more background, see How can I test a trigger function in GAS?
Here's a test function that will run your onFormSubmit() function multiple times, with the data that's already in your spreadsheet. It reads each row of the sheet, generates an object to simulate the Event you would get when a form was submitted, then calls the trigger function. If you place breakpoints inside onFormSubmit(), or rely on Logger.log(), this technique will allow you to test your trigger function.
function test_onFormSubmit() {
var dataRange = SpreadsheetApp.getActiveSheet().getDataRange()
var data = dataRange.getValues();
var headers = data[0];
// Start at row 1, skipping headers in row 0
for (var row=1; row < data.length; row++) {
var e = {};
e.values = data[row];
e.range = dataRange.offset(row,0,1,data[0].length);
e.namedValues = {};
// Loop through headers to create namedValues object
for (var col=0; col<headers.length; col++) {
e.namedValues[headers[col]] = e.values[col];
}
// Pass the simulated event to onFormSubmit
onFormSubmit(e);
}
}
I've done no other debugging of your original function... but this gets rid of that error message, so you can continue testing.
I use the below code to test my triggers
//
//
function onFormSumbit(e) {
//Timestamp 1 Status Remarks 3 Expected Completion Date 4 TRB Number
var resp = e.source.getActiveSheet().getRange(e.range.rowStart,1, e.range.rowStart,5 ).getValues();
/*
function test(){
var ss=SpreadsheetApp.getActive();
var respsht=ss.getSheetByName("Form responses 1");
var resp = respsht.getRange("A119:P119").getValues();
*/
I will comment out the onformsubmit portion and run the test portion.
Once the function is working, I will comment out the test function and activate the onformsubmit function.
Simple solution :)