Please help make the script. As written below, I wrote a script that worked for some time. Now it does not work and I need help writing a new one.
The challenge is this: when you make changes in column F, K and O - occurs first check the availability of the text in the column to the - then if there is a text should start sorting first by F column - then sorting by a column O
There is a scheme of action sequences by the link:
https://drive.google.com/file/d/0B5qSx6LqB8U-d01URS1BcEVtNGs/view?usp=sharing
I will be happy if someone can help me.
In any case, thanks for your time and attention :) Have a nice day :)
29/03/17 I need help "Service error: spreadsheets"
A very recently worked script:
function onEdit() {
var ss = SpreadsheetApp.getActiveSheet();
var r = SpreadsheetApp.getActiveRange();
var cols = r.getColumn();
var rows = r.getRow();
var who = ss.getRange(rows,11).getValue();
if (who !== "") {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0]
sheet.sort(6);
sheet.sort(15);}
}
Today received an error:
Service error: spreadsheets
The script stopped working at all, help, please.
I'm not following how this completes your action sequence flow chart, but I believe this should get it running as before. I've seen other posts where people get this same Service Error message form previously working scripts, and I believe Google is trying to change how certain tasks are done to help their server loads.
function onEdit() {
var ss = SpreadsheetApp.getActive();
var r = SpreadsheetApp.getActiveRange();
var cols = r.getColumn();
var rows = r.getRow();
var who = ss.getActiveSheet().getRange(rows,11).getValue();
if (who !== "") {
var sheet = ss.getSheets()[0];
sheet.sort(6);
sheet.sort(15);}
}
I removed the duplicate definition of var ss and tweaked var ss, var who and var sheet to work without it.
Related
I've been getting problems trying to reference a dynamic range in google sheets to apply my function to: essentially replacing special characters and empty cells within a given data range with 0s, works perfectly if I gave it a static range but cant seem to do a dynamic one. Thanks alot for any help offered
Code thus far:
function replaceValues() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastRow = sheet1.getDataRange().getLastRow()
var firstRow = sheet1.getDataRange().getRow("B2")
var targetValue1 = '-'
var targetValue2 = ''
var targetValue3 = '$'
var subValue = 0
for(var i =0; i=targetValue1; i++); {
var valueRng = sheet1.getRange(firstRow,lastRow);
var newValues = valueRng.getValues()[i].setValues(subValue);
}
}
Select the range before going the the menu and executing the function or build a modeless dialog and you can make the selection and capture on the custom dialog with a button click.
Demo:
If you have a range, you can do search and replace inside it this way:
range.createTextFinder('aaa').replaceAllWith('b');
Or with RegExp:
range.createTextFinder('a+').useRegularExpression(true).replaceAllWith('b');
I still don't understand what exactly you're trying to gain. So here is a guess how your script might look like:
function replaceValues() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var range = sheet1.getDataRange(); // is the range dynamic enough?
// replace '$' and '-' with '0'
range.createTextFinder("[\$-]").useRegularExpression(true).replaceAllWith('0');
// replace '' with '0'
var data = range.getValues();
data.forEach((row,r) => row.forEach((cell,c) =>
data[r][c] = data[r][c].toString().replace(/^$/,'0')));
range.setValues(data);
}
The Email collection function built into the form is in the Text format. We have a lot of employees filling out this form, so it would be more convenient to choose from a drop-down list than to manually enter an email for each employee. The built-in function of collecting emails does not allow using the Dropdown list question type, so I had to disable it and send the response to the email using a script.
In addition, when you select one of the options (Transfer) in the form, you need to insert an additional correction row with data. This is also done by the script.
The script works fine, but unfortunately, its execution time reaches 45 seconds. I think this is abnormally large.
Updated code:
var FORM_ID = '#####';
var SHEET_NAME = 'Operations';
function sendFormToEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
console.time('section10'); // 25579ms
var sheet = ss.getSheetByName(SHEET_NAME);
console.timeEnd('section10');
// Open a form by ID and log the responses to each question.
var form = FormApp.openById(FORM_ID);
var formResponses = form.getResponses();
var i = formResponses.length - 1;
var formResponse = formResponses[i]; // Last item
var itemResponses = formResponse.getItemResponses();
var length = itemResponses.length;
Logger.log("length = " + length);
var emailTo = itemResponses[0].getResponse(); // Returns the email if given
var cp = itemResponses[1].getResponse(); // Counterparty
var subject = "Input form: "+ cp;
var datePay = itemResponses[2].getResponse(); // Date of the operation
var dateAccept = itemResponses[3].getResponse(); // Date of acceptance
var sum = itemResponses[4].getResponse() ; // Amount
var what = itemResponses[5].getResponse(); // Operation type
var comm = "Correction " + itemResponses[length - 1].getResponse(); // Last response
var sum_1 = parseFloat(sum.replace(/,/, '.')) * (-1); // Amount * (-1)
var timestamp = new Date();
var textBody = "Operation: " + timestamp + ";\n";
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
var resp = itemResponse.getResponse();
textBody += itemResponse.getItem().getTitle() + "=" + resp + ";\n";
}
if (what == 'Transfer') { // If the transfer between your accounts
var lr = sheet.getLastRow();
sheet.insertRowBefore(lr);
var values = [datePay, dateAccept, "", sum_1, "", "", "", "", comm, "", "", "", what, timestamp, "", "", ""];
Logger.log(values);
console.time('section12'); // 19449ms
sheet.getRange(lr, 2, 1, 17).setValues([values]);
console.timeEnd('section12')
}
if(emailTo !== undefined){
GmailApp.sendEmail(emailTo, subject, textBody);
}
}
At first I thought it was the large size of the acceptance sheet (about 13 thousand rows). I created a copy of the table, reduced it to several dozen rows, but the speed did not increase much.
Then I deleted the answers from the form (there were just under 9000 of them) - the same thing, I didn't get much performance gain.
Anyone have any ideas how to change the script algorithm to improve its performance?
===
Conclusions
Thanks to #TheMaster for the help with console.time(). Thanks to this tool I found bottlenecks in the code. More precisely, the code works well, but it's about the structure of the spreadsheets system with which the code interacts. The structure needs to be optimized.
There was also an idea addressed, probably, to Google developers. It would be great if there was a tool that visually (graphically) displays the relationships between spreadsheets and sheets that make up a single system. Perhaps with some kind of numerical characteristics that reflect, for example, the interaction time between its blocks. This would make it possible to quickly eliminate such bottlenecks in the system and improve it.
Investigation:
Pinpointing the issue is done using console.time() and console.timeEnd() of each section of code and calculating the time taken by reachl each section of code.
Issue:
As discussed in the question comment chain, This is due to a bloated spreadsheet with many sheets and import formulas. This caused more time to get the exact sheet and to use setValues:
console.time('section10'); // 25579ms
var sheet = ss.getSheetByName(SHEET_NAME);
console.timeEnd('section10');
//....
console.time('section12'); // 19449ms
sheet.getRange(lr, 2, 1, 17).setValues([values]);
console.timeEnd('section12')
Possible solutions:
Reduce number of sheets
Reduce interconnected spreadsheets (=import* formulas)
Delete empty rows on the bottom and empty columns on the right of each sheet.
I have 5 possible locations where a user can enter either: "Yes, No, or any URL". If a URL is entered, I need the value of that cell (the URL) to be entered into an equation on a corresponding cell in a separate sheet. Here is what I have:
//Locations of the cells where the user can enter the URL.
var graphic1_loc = 'A62';
var graphic2_loc = 'A63';
var graphic3_loc = 'A64';
var graphic4_loc = 'A65';
var graphic5_loc = 'A66';
//The corresponding locations on the "Briefing" sheet where the images would get inserted.
var graphic1_placement = 'B45';
var graphic2_placement = 'B46';
var graphic3_placement = 'B47';
var graphic4_placement = 'B48';
var graphic5_placement = 'B49';
//main_gen is the name of the sheet where the user enters the data. This just grabs the value
//the user entered and stores it in the corresponding variable.
var graphic1 = SpreadsheetApp.getActiveSheet().getRange('main_gen!'+graphic1_loc).getValue();
var graphic2 = SpreadsheetApp.getActiveSheet().getRange('main_gen!'+graphic2_loc).getValue();
var graphic3 = SpreadsheetApp.getActiveSheet().getRange('main_gen!'+graphic3_loc).getValue();
var graphic4 = SpreadsheetApp.getActiveSheet().getRange('main_gen!'+graphic4_loc).getValue();
var graphic5 = SpreadsheetApp.getActiveSheet().getRange('main_gen!'+graphic5_loc).getValue();
var graphics_placements = ["B45", "B46", "B47", "B48", "B49"]; //These are the corresponding cells where the image would be placed on a separate sheet.
//If any graphic is a URL, insert that image into it's corresponding cell in the Briefing tab.
for (var num = 0; num < 5; num ++) {
var graphicformula = '=IMAGE("' + graphic[num] + '",1)';
if (graphic[num].length > 5) {
SpreadsheetApp.getActiveSheet().getRange('Briefing!'+graphic[num]_placement).setFormula(graphicformula);
}
}
What I am trying to get the If statement within the For Loop to say is...If the length of the value of graphic1 is > 5 (if it isn't yes (3) or no (2) then it is assumed it is a URL) then find the corresponding cell on the "Briefing" sheet to insert the formula. The current way I have it written is not correct, but I wanted to attempt to write something to give you an idea of what I am after. Thanks for any help you can provide!
You want to run SpreadsheetApp.getActiveSheet().getRange('Briefing!'+graphic[num]_placement).setFormula(graphicformula); when the value of graphic# is URL.
For example, when the URL is in the cell A63 in the sheet main_gen, you want to put the formula of =IMAGE(URL,1) to the cell B46 in the sheet Briefing
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification points:
In your script, you are trying to use graphic[num] as a variable of graphic1, graphic2,,,. In this case, this cannot be used as the variable.
In your situation, the cells A62:A66 in the sheet main_gen are correspoinding to the cells B45:B49 in the sheet Briefing. I think that this can be used for modifying your script.
The values of the cells A62:A66 in the sheet main_gen can be retrieved by ss.getSheetByName("main_gen").getRange("A62:A66").getValues().
In order to confirm whether the value of cell is the URL, in this modification, I used test().
I think that when the destination sheet is declared out of the for loop, the process cost can be reduced.
When above points are reflected to your script, it becomes as follows.
Modified script:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheetByName("main_gen").getRange("A62:A66").getValues();
var dstSheet = ss.getSheetByName("Briefing");
var graphics_placements = ["B45", "B46", "B47", "B48", "B49"];
for (var num = 0; num < source.length; num ++) {
if (/https?:\/\//.test(source[num][0])) {
var graphicformula = '=IMAGE("' + source[num][0] + '",1)';
dstSheet.getRange(graphics_placements[num]).setFormula(graphicformula);
}
}
References:
test()
getValues()
If I misunderstood your question and this was not the direction you want, I apologize.
Could anyone please help me optimise this Google Sheets script?
It's painfully slow. It barely makes 300 copies in 24 hours (I need the Drive API to make the copies as "view only users can't copy/print/download")
function addCopies() {
var count = 10;
var input = DriveApp.getFileById('1GQI5m0g5XsCWi1dOTBWFs3VJkzW7e8N__09C3-2BEGI')
var results = DriveApp.getFileById('1gkOCTPJuqTQnNNlaE3jD2Hq2COU-lO02PmlO_xMepx0')
var indexSH = SpreadsheetApp.openById('1o6ZfweVylhnDXoVc9u_R8afKdVfXYgtdqtj_lSCdFEk').getSheetByName('Index')
for(var i=0;i<count;i++)
{
//make copies of both files (input & results)
var inputCopy = input.makeCopy(input.getName(), DriveApp.getFolderById('0B0go-fsdFA3ISG0xNGlhTDZoM0U'))
inputCopy.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.EDIT)
var inputID = inputCopy.getId()
//make results to be restricted using API
var resultsCopy = results.makeCopy(results.getName(), DriveApp.getFolderById('0B0go-fsdFA3ISG0xNGlhTDZoM0U'))
resultsCopy.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
var resultsID = resultsCopy.getId()
var apiFile = DriveAPI.Files.get(resultsID)
apiFile.labels.restricted = true
DriveAPI.Files.update(apiFile, resultsID)
//save IDs of both files to an index for later re-use
var lastRow = indexSH.getRange(1,1).getValue()
var addRng = indexSH.getRange(lastRow+1, 1,1,3)
addRng.setValues([['free',inputID,resultsID]])
}
}
Edit: techydesigner said "we don't optimise code on SO" . So please let me rephrase : am I using the wrong Drive services ? Is there a method to make those copies faster ?
I'm trying to create a drop down menu with contents based on a another cell in the same row. For example if A1 = 'yes' then the drop down in B2 gives you the options of 'yes' or 'no'. I can do this I have the list data set up and to code works. The problem is I need to do this 155 times in 4 different sheets. Is there a faster way to do this than right clicking and editing the data validation rules for each cell. Here's a link to the test sheet I'm working on :
https://docs.google.com/spreadsheets/d/1rd_Ig_wpof9R_L0IiA1aZ9syO7BWxb6jvBhPqG8Jmm4/edit?usp=sharing
You can set data validation rules with a script, as documented here. Here's a reference for starting with Apps scripts.
I wrote a function that does approximately what you described. It works with the range B3:B157 of the sheet '9th grade' in the current spreadsheet. For each of them, it sets the validation rule to be: a value in the same row, columns B and C of sheet 'List Data'. The line with
....... = listData.getRange(i+3, 2, 1, 2);
will need to be modified if the source range of validation is to be different. Here, the parameters are: starting row, starting column, number of rows, number of columns. So, 2 columns starting with the second, in row numbered i+3.
function setRules() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var grade = ss.getSheetByName('9th Grade');
var listData = ss.getSheetByName('List Data');
var range = grade.getRange('B3:B157');
var rules = range.getDataValidations();
for (var i = 0; i < rules.length; i++) {
var sourceRange = listData.getRange(i+3, 2, 1, 2);
rules[i][0] = SpreadsheetApp.newDataValidation().requireValueInRange(sourceRange).build();
}
range.setDataValidations(rules);
}
I land in this issue for a diferent reason: "Just mass DataValidation copy (or update) in one column". Thanks, to user3717023 that bring me a light.
I hope that helps someone this simplification.
function setRules() {
//select spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var leads = ss.getSheetByName('Leads');
//Select correct Datavalidation
var rangeNewDataValidation = leads.getRange('M2:M2');
var rule = rangeNewDataValidation.getDataValidations();
//Copy (or Update) Datavalidation in a specific (13 or 'M') column
var newRule = rule[0][0].copy();
Logger.log(leads.getMaxRows())
for( var i=3; i <= leads.getMaxRows(); i++){
var range = leads.getRange(i, 13);
range.setDataValidations([[newRule.build()]]);
}
}