Pushing a simple Log string from a Google Ad Script to a Google Sheet - google-apps-script

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);
}

Related

Using Logger.Log to log different value

I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>

This script does not populate sheet after parsing retrieved data

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

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);
}
}
}

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.

get form URL from a spreadsheet bound Form

In a spreadsheet script I want to send mail to users that will point them to the URL of a form that will let them enter data. I have tried:
function test1(){
var formID = FormApp.getActiveForm();
var formUrl = DriveApp.getUrl(formID);
sendMail(formUrl);
return
}
This fails because the value of formID is allways NULL.
If there is one form linked to spreadsheet
Use
var ss = SpreadsheetApp.getActiveSpreadsheet(); // or openById, etc
var formUrl = ss.getFormUrl();
to get its Url. If needed, FormApp.openByUrl(formUrl); returns a pointer to the form, which allows any other methods.
Multiple forms linked to spreadsheet
There is no built-in method to return the list of forms linked to a given spreadsheet; this is an open issue in Apps Script issues tracker. A workaround is to search all forms (as Cyrus Loree did), get the destination of each, and return the list of those where the destination is the spreadsheet of interest. This is how:
function linkedForms() {
var ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
var formList = [];
var files = DriveApp.getFilesByType(MimeType.GOOGLE_FORMS);
while (files.hasNext()) {
var form = FormApp.openByUrl(files.next().getUrl());
try {
if (form.getDestinationId() == ssId) {
formList.push(form.getPublishedUrl());
}
}
catch(e) {
}
}
return formList;
}
Remarks:
I put form.getDestinationId() in a try block because this method throws an error what the form's destination spreadsheet happens to be deleted (instead of just returning null)
To get the list of form Ids instead of Urls, use form.getId() in the function.
Because you are working in the Spreadsheet, you need to get the associated form from the Spreadsheet object (e.g. SpreadsheetApp.getActiveSpreadsheet.getFormUrl() ).
You will also need to send the mail message with the htmlBody optional parameter.
Here is a code sniplet:
function sendNotice(recipient){
try{
// either hardcode the folder id below
var formStorageFolderId = '';
// or programmatically get the folder from the spreadsheet parent
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssFolder = DriveApp.getFileById(ss.getId()).getParents();
if(ssFolder.hasNext()){
// assume there is only one parent folder
formStorageFolderId = ssFolder.next().getId();
}
var formFolder = DriveApp.getFolderById(formStorageFolderId);
var files = DriveApp.getFilesByType(MimeType.GOOGLE_FORMS);
var formId = '';
while(files.hasNext()){
// search for the form (is it the same name as the spreadsheet?)
var file = files.next();
var fileName = file.getName();
var sheetName = ss.getName();
if(fileName == sheetName){
// matched names
formId = file.getId();
break;
}
}
if(formId){
var actualForm = FormApp.openById(formId);
var formName = actualForm.getTitle();
var formURL = actualForm.getPublishedUrl();
var subject = "Please fill out form";
// html mail message (needed for the link
var mailBody = '<div><p>Please fill out the attached form<p>';
mailBody += '<p>' + formName + '';
MailApp.sendEmail(recipient, subject, '',{htmlBody:mailBody});
}
}catch(err){
Logger.log(err.lineNumber + ' - ' + err);
}
}
I know that it's an old question, but I'll still add an answer to whom it may concern. #user3717023's answer works fine, but for most use-cases, there's a better way.
Generally speaking, Form is connected to Sheet, not to Spreadsheet itself. So, one way to get all of the Spreadsheet forms is to go through the Sheets of the Spreadsheets, and get their Form URL, like that:
function getFormsOfSpreadsheet(spreadsheetId) {
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheets = ss.getSheets();
const formsUrls = [];
for (const sheet of sheets) {
const formUrl = sheet.getFormUrl();
// getFormUrl() returns null if no form connected
if (formUrl) {
formsUrls.push(formUrl);
}
}
return formsUrls;
}
Docs
If this way is not optimal for you (e.g. you have way too many Sheets in your Spreadsheet and only a few of them are linked to Forms), there's still a possibility that the Forms you need to capture are stored in the same Folder. In this case, there's no need to search through the whole Drive to capture them, you can use the same method, but with Folder:
function linkedForms() {
var ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
var formList = [];
var folder = DriveApp.getFolderById(yourFolderId);
var files = folder.getFilesByType(MimeType.GOOGLE_FORMS);
while (files.hasNext()) {
var form = FormApp.openByUrl(files.next().getUrl());
try {
if (form.getDestinationId() == ssId) {
formList.push(form.getPublishedUrl());
}
}
catch(e) {
}
}
return formList;
}
Docs