Is there a way to determine when a spreadsheet is done calculating? - google-apps-script

I've a spreadsheet with a lot of imports and formulas based on the imports. Sometimes it takes a long time to update -- there is now a progress bar in the upper right that pops ups the message "calculating formulas" when you hover over it. It there a programmatic way to detect when the sheet is done recalculating?
I'd like to popup a box (toast?) saying the sheet is busy and then disappear the box when the sheet is done recalculating.

Not really.
Volatile functions like Importrange are the enemies of spreadsheet stability. Sometimes they get stuck recalculating and never finish; that is for nor particular reason as creating a copy usually creates a version that finishes calculating (eventually).
If you work with many importrange formulas and formulas based on them it is generally advisable to replace the import formula by a scheduled import via script.

This seems to work so far:
SourceWB = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/185XVLyN4fFLds8Z7JIgkL6mGl1CC82maBmmVAhYP71g/");
SourceRange = SourceWB.getRangeByName("CoreElementExport");
SouceValues = SourceRange.getValues();
var sourceRows = SourceRange.getNumRows();
var sourceColumns = SourceRange.getNumColumns();
DestWB = SpreadsheetApp.getActive();
DestSheet = DestWB.getSheetByName("TaxonomyImport");
DestSheet.getDataRange().offset(0, 0, sourceRows, sourceColumns).setValues(SouceValues);
something after, just trying to use this very complicated input tech...

Related

Efficiently making Cell Notes from another Cell

I'm making a Google Sheet for a TTRPG I play with some friends and I'm trying to find the best way to make automated cell notes. I found a solution here that works and modified it to match my sheet but I'm VERY new to Google Scripts and am sure I'm doing things inefficiently.
Right now the solution is:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Copy of Digital');
var range = sheet.getRange("R31:T59");
range.clearNote();
for (var x=1;x<30;x++) {
range.getCell(x, 1).setNote(range.getCell(x, 3).getValue());
}
}
This is a workable solution for my needs but I always want to take things farther than just "workable" :). I know a small amount of C# but JavaScript is entirely new to me. The issues I see with my current implementation and possible areas of improvement are:
Because of formatting requirements on another section of the sheet, every other row where I am running this solution will ALWAYS be empty, but the solution is going to check the empty rows for notes every time. Is there a way for me to have the solution skip every second row? Can I specify exact cells rather than a range (and does JavaScript use absolute references like sheet formulas)?
Can I specify multiple ranges within the sheet for this solution to act on? Is it as simple as defining a second range?
Is it possible to restrict the onEdit(e) function's activation to only fire when specific ranges are edited? I'm generating a list of "Talents" that need the script to run whenever a player changes their gear through a validation list but to my understanding (and testing) onEdit(e) is going to run when any cell is modified (which will lead to numerous unneccessary script activations).
If I have multiple copies of the same tab active in the same sheet is it possible for multiple players to be using their own version of the sheet at the same time? (I think this must require the removal/modification of the getSheetByName line, but I'm only 2 days into trying to learn this!)
Huge apology for having so many questions! Any and all help is deeply appreciated!
Visual of the sheet for formatting reference
Try it this way:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Copy of Digital") {
var range = sheet.getRange("R31:T59");
let notes = sheet.getRange("T31:T59").getDisplayValues()
range.clearNote();
sh.getRange(31,18,notes.length,notes[0].length).setNotes(notes);
}
}

Long processing time when inserting data in google sheets through google scripts

I have just built my first google script for a dashboard I am building in google sheets. Essentially, this dashboard has cells which act as filters for calculations in other cells. I am trying to build two buttons, one which returns the filter-cells values to a default value and one which copies the values for the filter from another sheet in the same google spreadsheet.
Problem
The script is taking between 2 and 120s to finish, for an action that done manually takes about 20s, essentially making the button "useless" when it takes more than 30s. Some times the script is incredibly fast, but I would need it to be that fast consistently for it to be worth having. I have tried optimizing the code to the best of my ability (which is not much) based on other threads I have found, but I haven't been able to optimize it yet and I was hoping to find a way to improve my code. Any suggestions?
Code
Default button
function Filters() {
var spreadsheet = SpreadsheetApp.getActiveSheet();
range_apply_1 = spreadsheet.getRange('C2:C8'),
range_apply_2 = spreadsheet.getRange('E4:E6');
var values_1 = SpreadsheetApp.getActiveSheet().getRange('\'Aux AM\'!A11:A17').getValues()
values_2 = SpreadsheetApp.getActiveSheet().getRange('\'Aux AM\'!A20:A22').getValues();
range_apply_1.setValues(values_1),
range_apply_2.setValues(values_2);
};
Copy values button
function test() {
var spreadsheet = SpreadsheetApp.getActiveSheet();
range_apply_1 = spreadsheet.getRange('C2:C8'),
range_apply_2 = spreadsheet.getRange('E4:E6');
var values_1 = SpreadsheetApp.getActiveSheet().getRange('\'AM Metrics - Orders\'!C2:C8').getValues()
values_2 = SpreadsheetApp.getActiveSheet().getRange('\'AM Metrics - Orders\'!E4:E6').getValues();
range_apply_1.setValues(values_1),
range_apply_2.setValues(values_2);
};
The basic to reduce the execution time in Google Apps Script is to to reduce the number of calls to Google Apps Script services. In your specific case you could reduce the calls by using a single getRangeList instead of using multiple getRange.
I think that the following modification should work:
function Filters() {
var sheet = SpreadsheetApp.getActiveSheet();
var name = sheet.getName();
var [range_apply_1, range_apply_2, range_1, range_2] = sheet
.getParent()
.getRangeList([
`${name}!C2:C8`,
`${name}!E4:E6`,
'Aux AM!A11:A17',
'Aux AM!A20:A22'
]).getRanges();
var values_1 = range_1.getValues();
var values_2 = range_2.getValues();
range_apply_1.setValues(values_1);
range_apply_2.setValues(values_2);
};
Regarding variation on the execution time that is normal. It's caused by things on your control and things out of it. One example of things that might be on your control is your PC load as the spreadsheet recalculation might be affected by the PC resources availability. An example of things out of your control is the Google servers responsiveness among other things.

Force Calculation script of Cell Range in Google Sheet

For some reason the formula isn't working when the data from importrange is being updated. What I have to do is to go to cells and pressing F2+enter to refresh and ya it works like that I tried creating a macro and it also works but I want it to be simplified.
I come up with this kind of script that doesn't really work because i don't have time to learn for and dig deeper for scripting
function CellRefesher() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("A1:O52");
range = SpreadsheetApp.flush();
};
so What I'm trying to do is I want to do something like force calculation for the cell range of A1 to O52 and base of google document(just base on my understanding)
this "SpreadsheetApp.flush();" will do something similar that's why I do this "range = SpreadsheetApp.flush();"
This might be like a spoon feeding but please do help me. I want to learn but don't just have much time yet. Thank you

Is there a way to dynamically add a sheet where it will be at the end when the script runs in Google Apps Script?

I am having a simple problem where I want to add a sheet at the end of the tabs when my script runs, but I can't find a way to do that.
I have tried using the insertSheet script, but from what I have read up on, that requires a number index as to where to place it (below it is 17). I am sure there is a way to place it at the end no matter how many tabs you have. Similar to the insertRowAfter function, I am sure there is something like ss2.insertSheetAfter(ss2.getLastSheet) but I can't seem to find a way to do that. Here is my basic code within my script. Thank you for any help!
var ss2 = SpreadsheetApp.getActive();
ss2.insertSheet(17);
Here is what I would do:
var ss2 = SpreadsheetApp.getActive();
ss2.insertSheet(); // Inserts a sheet that by default becomes active
var totalSheets = SpreadsheetApp.getActiveSpreadsheet().getNumSheets(); // Gets the total number of sheets
ss2.moveActiveSheet(totalSheets); // Moves the active sheet to the last position

Google Script for deleting blank or unused columns

I have searched and searched but can't find anything that works. I am trying to eliminate any empty columns that remain in the spreadsheet when creating a Google Form. It would be helpful because I have a formula for the sum of a string comparison in the sheet, but I don't want it to include comparisons of empty cells. (i.e. I compare the entries in from H:X in every individual row to H2:X2 and have it count how many are the same.) I know I could adjust the formula, but I am looking to build a template for fellow colleagues so they don't have to worry about altering anything. If I could get the empty columns at the end to disappear automatically, I could just change the sum formula to extend all the way to column CZ (just to be sure it goes far enough), without it calculating blanks in its comparison.
Any help would be great! Thanks!
i use these scripts in the script editor of my google sheets for cleaning them up. stumbled across them somewhere... perhaps they will help.
//Remove All Empty Columns in the Entire Workbook
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn);
}
}
}
//Remove All Empty Rows in the Entire Workbook
function removeEmptyRows() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow);
}
}
}
This is something you will have to get used to when using Google forms. Data is never really erased if you physically delete rows/columns, they will just reappear at some unknown time in the future. It's a pretty buggy and unreliable setup.
My advice is to keep your formulas and data completely separate. You do this by copying the form responses to another sheet (using code or otherwise). That sheet allows you to control the data being fed (or NOT fed) to the rest of your system. I would also go 1 step further and use formulas like
ImportRange(SheetSource!A:A)
in a 3rd sheet to allow you to control the order of the columns. From there you can add your formulas to act on the data being fed through in relative safety.
However, I had a situation not long ago, where the form and the sheet randomly lost their linkage, and it was not possible to restore it. I stayed up until 6am trying to fix the issue, however if I did not have the data copied to the separate sheet (as described above) the catastrophe would have been complete.
Luckily this appeared to have been a random glitch, because everything went back to normal at about 10am, but put simply, your data is not truly safe with Google Forms.
Using Arrayformula is better suited for this though it does the same thing.
Example:
=ARRAYFORMULA('Form Responses'!A1:EA)
Importrange has a noticeable (and annoying) delay as this what you would use if you wanted to reference sheets from other spreadsheet files. Arrayformula references sheets within the same file, no delays.