I am an absolute beginner and using some code from the documentation, I am trying to pass a user interface response to a variable and then place it on a sheet.
The response gets into the logger OK but only "Response from user" appears in the cell.
The piece of code is:
if (response.getSelectedButton() == ui.Button.YES) {
Logger.log('The user\'s name is %s.', response.getResponseText());
var name=response;
var target1 = tr.getRange(8, 25).activate(); // a test cell in sheet
target1.setValue(name);
} else if (response.getSelectedButton() == ui.Button.NO) {
Here's an example:
function gettingAResponse() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var resp=SpreadsheetApp.getUi().prompt("Are you ten feet tall?", SpreadsheetApp.getUi().ButtonSet.YES_NO_CANCEL);
if(resp.getSelectedButton()==SpreadsheetApp.getUi().Button.YES) {
SpreadsheetApp.getUi().alert('You pressed yes');
sh.getRange("E5:E6").setValues([["Your response is:"],[resp.getResponseText()]]);
}
if(resp.getSelectedButton()==SpreadsheetApp.getUi().Button.NO) {
SpreadsheetApp.getUi().alert("You pressed no.");
sh.getRange("E5:E6").setValues([["Your response is:"],[resp.getResponseText()]]);
}
if(resp.getSelectedButton()==SpreadsheetApp.getUi().Button.CANCEL) {
SpreadsheetApp.getUi().alert("You pressed cancel");
sh.getRange('E5:E6').clearContent();
}
}
Another Example
In your code, you are doing the following:
var name=response;
var target1 = tr.getRange(8, 25).activate(); // a test cell in sheet
target1.setValue(name);
However, if what you want, is to set the actual response value in your cell, you have to do it as follows (notice the difference in the first line):
var name=response.getResponseText();
var target1 = tr.getRange(8, 25).activate(); // a test cell in sheet
target1.setValue(name);
Further to that, I suggest that you evaluate whether you need the activate() function call, and that you check out the troubleshooting page in the documentation (it can save you lots of time!).
Reference
https://developers.google.com/apps-script/reference/base/prompt-response#getResponseText()
Related
I create a spreadsheet with 3 sheets, "Links", "Valid Links" and "Invalid links", then use the following code to check each row in the "Links" sheet, as below:
function myFunction() {
var rows = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Links").getDataRange().getValues();
rows.forEach(function(row, index)
{
if (index !== 0)
{
var url = row[1];
var page = UrlFetchApp.fetch(url).getContentText();
var number = page.match("sample.com");
if (!number)
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Valid Links").appendRow(url);
else
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Invalid Links").appendRow(url);
}
});
}
However, when I debug to UrlFetchApp.fetch(url), the script suddenly aborts. No error or exception are shown. Why?
I have asked this question on Google Script Community, but no one answers. So I have to ask it here. I don't know how to get the specific URL for my question on Google Script Community, so I have to copy & paste the question. Sorry about that.
Update
With the help of Tanaike, I fix my bug:
I think row[1] refers to column A, but it actually to column B, which is an undefined value, so cause the bug.
!number should be changed to number, as if (number) means a match so the result is valid link.
I believe your goal as follows.
You want to put the value to Valid Links and Invalid Links sheets by checking the URL using UrlFetchApp.fetch.
The URLs for checking are put in the column "B" in Links sheet.
Modification points:
In this case, how about using muteHttpExceptions as the option of UrlFetchApp.fetch? By this, the response value can be retrieved even when the request is failed.
The default value of muteHttpExceptions is false. In this case, when the request occurs error, the script is stopped. It seems that this is the current specification. But when muteHttpExceptions is true, the script is not stopped even when the request occurs error.
In your script, appendRow(url) is used and var url = row[1]; is the value from the column "B". In this case, url is required to be [url].
In the case of var number = page.match("sample.com");, when sample.com is included in page, url is put in Invalid Links sheet. When sample.com is NOT included in page, url is put in Valid Links sheet. I'm not sure whether this might be the result you expect. But please be careful this.
I think that when var ss = SpreadsheetApp.getActiveSpreadsheet(); is used, the process cost will be able to be reduced a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Added
var rows = ss.getSheetByName("Links").getDataRange().getValues(); // Added
rows.forEach(function(row, index) {
if (index !== 0) {
var url = row[1];
var page = UrlFetchApp.fetch(url, {muteHttpExceptions: true}).getContentText(); // Modified
var number = page.match("sample.com");
if (!number) {
ss.getSheetByName("Valid Links").appendRow([url]); // Added
} else {
ss.getSheetByName("Invalid Links").appendRow([url]); // Added
}
}
});
}
Note:
When fetchAll is used, the process cost might be able to be reduced more. But I'm not sure about the number of URLs. So I modified your script like above without using fetchAll method.
References:
fetch(url, params)
appendRow(rowContents)
Added:
About the following new question,
One more question, when url is wisesoft.co.uk, I get error SSL Error wisesoft.co.uk (line 10, file "Code") and the script also abort. I use Chrome and find https has error but the site can be visited via http version, how to ignore such an error and continue fetch the contents?
in this case, how about using try...catch as follows?
Modified script:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var rows = ss.getSheetByName("Links").getDataRange().getValues();
rows.forEach(function(row, index) {
if (index !== 0) {
var url = row[1];
try {
var page = UrlFetchApp.fetch(url, {muteHttpExceptions: true}).getContentText();
var number = page.match("sample.com");
if (!number) {
ss.getSheetByName("Valid Links").appendRow([url]);
} else {
ss.getSheetByName("Invalid Links").appendRow([url]);
}
} catch(e) {
ss.getSheetByName("Invalid Links").appendRow([url]);
}
}
});
}
try...catch
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
I'm having some trouble with my script. Basically, that works getting data from one course and inputting the values to one Sheet. That's working perfectly. But when one of my students input 'enter' command at that course, I have trouble to read it in Excel.
SO, I have to find (it's a regular Expression: \r\n|\n|\r) and replace the enter at Google Spreadsheet and change it for "; ". Works perfectly doing it manually, but I can't do it by script. Here the piece:
// 1. Enter sheet name where data is to be written below
var SHEET_NAME = "DATA";
// 2. Run > setup
//
// 3. Publish > Deploy as web app
// - enter Project Version name and click 'Save New Version'
// - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
//
// 4. Copy the 'Current web app URL' and post this in your form/script action
//
// 5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
return handleResponse(e);
}
function doPost(e){
return handleResponse(e);
}
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("CHANGED BY SECURITY REASON"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("1Un5A61M8CJDBGDAB-Tx-lYgKYaVB2RSfn9QAQ5Q-sZs", doc.getId());
}
I tried this and worked well, but needs a trigger that's play only minute per minute. Not ok.. And change just the first value from the cell. Sometimes there's more than one at same cell:
function Replace() {
var redeletar = new RegExp("\r\n|\n|\r");
var range = SpreadsheetApp.getActiveSheet()
.getRange('A1:CH');
range.setValues(range.getValues()
.map(function (r) {
return r.map(function (c) {
//Replace string
return c.toString().replace(redeletar, ";");
});
}));
}
Any idea to play this script at same time after the Sheet receive de data from my course AND play it recursively?
Thanks so mucho
I think all you need is the global flag as shown below:
function Replace()
{
var re = /(\r\n|\n|\r")/g;
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
range.setValues(range.getValues().map(function (r){return r.map(function (c){return c.toString().replace(re, ";");});}));
}
I think this will make all of the replacements rather than just the first ones;
So, I made a Google Spreadsheet for a group of people to use to keep track of weekly "counts" for a large group of people on a Reddit sub. The things I'm trying to automate are two things. The one I'm having problems with is the one I thought would be the easiest, just copying the values from one set (G2:G200) to overwrite the values in another (E2:E200). I'm having some other issues as well, but I'd be more interested in an explanation for what I'm doing wrong there than just an answer. The biggest one is that this is supposed to be making a custom menu on the sheet, and I can't seem to get that working, even though I basically copied the script from the Google Tutorial for that. I've tried the script for this two ways, one using the same script as Excel printed out when recording the same basic thing:
function UpdateLore_() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;
} else {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
This returns an arror on the "Selection.PasteSpecial" line. The other way I tried it was using what I could find online for this:
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValue = inputRange.getValue();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200");
}
The top part of the code looks like this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Weekly Update')
.addItem('Update for Lore', 'UpdateLore')
.addItem('Update for XP Master', 'UpdateMaster')
}
I feel like I'm missing something very obvious, especially with the whole "doesn't seem to change the sheet in anyway" part. Thanks for any help
Got some answers and now it works, thanks for all the help:
Got it, thanks for all the help. New code looks like this:
function UpdateLore() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
}
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
if (result ==ui.Button.NO) {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
To add data to a sheet you need to use:
setValue()
setValues()
appendRow()
You've got a function inside of the if body:
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
. . . .
}
}
If you want to call another function at that point, you could use:
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
};
function copyFunction () {
. . .
};
You need to set the values from the inputRange to the outputRange. Use the .setValues() on your outputRange to do this.
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
None of this is valid apps script code:
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;
I have to check weather a certain property exists before I can run the function listed in the add-on menu for a google spreadsheet. Rather than creating a copy of the same check for each function I would like to create a single function that I can pass the function to run as a parameter. How can I do this?
Below is my non functioning test code, but you may get the idea.
function testRun(){
//in practicality this would be an add-on menu
test1Check('test1()');
}
function test1(){
Logger.log("Function Ran");
}
function test1Check(functionToRun){
var labels = PropertiesService.getDocumentProperties().getProperty('labels');
var ui = SpreadsheetApp.getUi(); // Same variations.
if (!labels) {
var result = ui.alert(
'You have not yet set up the page for gClassFolders',
'Would you like to do this now?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
setupGCF();
}
}
else {
functionToRun;
}
}
I had to remove the () from the parameter sent, and add () to the variable in the function.
function testRun(){
test1Check(test1);
}
function test1(){
Logger.log("Function Ran");
}
function test1Check(functionToRun){
var labels = PropertiesService.getDocumentProperties().getProperty('labels');
var ui = SpreadsheetApp.getUi(); // Same variations.
if (!labels) {
var result = ui.alert(
'You have not yet set up the page for gClassFolders',
'Would you like to do this now?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
setupGCF();
}
}
else {
functionToRun();
}
}