Automatically keep one sheet synchronized with another - google-apps-script

I have a Google Sheets that I'm working with, and for reasons I'd rather not get into here I need to keep an updated copy of it in a second location. The original spreadsheet has two sheets/pages, I only need to copy from the second sheet/page to the first sheet/page of the new spreadsheet.
I have a server that can accomplish this if an API is necessary. I don't need an exact script to do it, but pointing me in the right direction would be very helpful. I'm most familiar with Python and PHP if they're needed.

I'm sure there is an easier way to do it using Apps Script, but if you want something that works in the short-term while you learn Apps Script, you can use the IMPORTRANGE function and Sheets exclusively.
=IMPORTRANGE("SpreadsheetKey","Sheet!Range")
Example:
I created a sheet called OriginalSheet and in A1:A5 put the numbers 1 through 5.
Next, I created a separate sheet called NewSheet and in cell A1, I put the the following function (with appropriate values for my OriginalSheet filled in):
This method works for me. I'm in the process of learning Apps Script and will post a better method when I learn it :)

Related

How can I make an Import function run without opening the sheet?

I have a Google Sheet that uses an IMPORTRANGE query to combine data from multiple other sheets. This combined import sheet is read by Google AppSheet. We have realized that the data AppSheet is reading is always outdated. It only reads the data as of the last time the sheet was manually opened.
I followed the steps in this post to try to fix this issue by creating this function: function refresh() {SpreadsheetApp.flush()}. I then set up a timed trigger to activate it once an hour. Logs show the function is running, but the data is still not updating until I manually open the sheet.
This is my first time using Apps Script. Any tips/ideas? Is there a different or better way to have the formulas update without opening the file?
Thank you for reading.
SpreadsheetApp.flush() only works for the script execution that calls it. If you need to refresh the data results from a formula it's uncertain how exactly the spreadsheet will respond as most of the formula calculations are done on the client side. You could verify this by yourself by using your web browser developer tools.
Anyway, spreadsheet formulas have several caveats so it will not be extrange that at some point you will have to rethink your solution. Assuming that you want to keep using AppSheet:
Use AppSheet for your front end and some no-code / low-code automation. Keep your app small, if you need many forms / views consider to distribute them among several apps.
Use Google Sheets only for data storage for your AppSheet app. Please bear in mind that it has 10 million cells limit for the whole spreadsheet, so you might want to delete the unused sheets and delete the unused columns and rows on each sheet.
You might use Google Apps Script to do the data import and transformation tasks. If you need that something be updated based on actions done on the AppSheet app, you might use an installable change trigger or use webhook from the AppSheet side to and a "simple" web application using Google Apps Script (you could use GET / POST http requests to trigger some Google Apps Script functions).
Also you might use other programming platforms for the data import / transformation tasks and keep using Google Sheets as your AppSheet database by using the Google Sheets API or other automation tools like Zappier, IFTTT, Integromat among many others.
solution #1
You can try this solution :
define a checkbox (for instance in A1 in tab Sheet1)
set this script
function myFunction() {
var chk = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange('A1')
chk.setValue(false);
SpreadsheetApp.flush();
Utilities.sleep(500);
chk.setValue(true);
}
define a trigger on it
define the formula as follows
=if(A1,importrange("1n-rjSYb63Z2jySS3-M0BQ78vu8DTPOjG-SZM4i8IxXI","A:Z"),"")
when A1 is unchecked, the result will be empty, then check A1 to fill once again the result as expected
solution #2
by script, try for instance
function myFunction() {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet9')
var data = SpreadsheetApp.openById('1n-rjSYb63Z2jySS3-M0BQ78vu8DTPOjG-SZM4i8IxXI').getSheets()[0].getDataRange().getValues()
sh.getRange(1,1,data.length,data[0].length).setValues(data)
}
put a daily triger as needed

How to solve the limitation of IMPORTRANGE formula in google sheet?

I have 50 google sheet files for 50 students. They need to key in their answers in their own google sheet file when they are asked to do so. I have a main google sheet to consolidate their data by using IMPORTRANGE formula. This is my formula:
=QUERY({IMPORTRANGE(...);IMPORTRANGE(...);IMPORTRANGE(...);...},"Select * where Col1 is not null")
I will have 50 IMPORTRANGE in the formula. So as expected, the main google sheet is very lag when the 50 students start to key in their answers at the same time. Sometimes, the formula will show #Value when all the students started to answer the questions at their own google sheet file. I need to keep refreshing the main google sheet so that the data will come out, but it will disappear again in a short while then I need to refresh it again (although it will settle down once most of the students finished answering the questions).
I know that using IMPORTRANGE is really not an efficient way to consolidate their answers in main google sheet file but I don't have other better way.
I tried to write a script so that they can send in their data by clicking the button assigned with the script. However, all the students need to go through the authorization process when they run the script for the first time. They don't know how to proceed when they saw the authorization process (not very good in using computer).
May I know is there any ways or tricks that I can use to solve the IMPORTRANGE issue? Or there are some way to write the script where we are not required to go through the authorization process when we run the script for the first time?
Hope to get some advice and help on this as I couldn't find a better way from Google already. Any help will be greatly appreciated!
If I understood correctly what you are looking is that your spreadsheet show in real-time the data being entered simultaneously on 50 different spreadsheets. I'm afraid that Google Sheets is not the right tool for what you are trying to do the way that you are trying to do it. Basically you have two options : change it or use a different tool.
It's not a good idea to have and array of multiple IMPORTRANGE functions that are being edited simultaneously because while the official docs says that IMPORTRANGE functions are updated every 30 minutes when the source and the spreadsheet having formula are opened at the same time the import is done practically immediately and could happen multiple times during the recalculation making causing it to start over an over again.
Replacing the above array by script might help only if you are open to not have the destination spreadsheet updated on real time as scripts are slow.
Replacing the above array by a program that uses the Google Sheets API also might help only if you are open to not have the destination spreadsheet updated on real time as the spreadsheet refresh.
Regarding running a script without requiring authorization that is only possible when using simple triggers and / or removing all the scopes that require authorization to run. Please bear in mind that you might create installable triggers to run other using the authorization of the user who creates them.
Related
Combining multiple spreadsheets in one using IMPORTRANGE
Why do two users sometimes see different values from importrange?
Multiple IMPORTRANGE
Using that many IMPORTRANGE formulas is definitely a bad idea. What I'd suggest you to do:
keep a list of all your student spreadsheet in your main document
write a script that will browse through all of the spreadsheets from that list and copy/paste values into your main document
create a time based trigger that will run the script every X minutes (or hours), depending on how accurate you want the results to be
This is a simple solution, but efficient. Depending on the amount of data and number of students/spreadsheets you may consider other solutions (like writing a cloud function that will do the same as the script) but I think this will work for your use case

Recording any copies made of a google sheet

I would like to create a Google sheet that records any copies that have been made of another Google Sheet even when the user creates a copy by putting 'copy' into the URL (https://docs.google.com/spreadsheets/d/"spreadsheet-ID"/copy).
I have found a solution on the post: Track number of copies made from a google spreadsheet but this doesn't record copies made when changing the URL as the above example.
I would like to know the whole URL of the sheet preferably but just the Spreadhseet ID would still be enough for me.
Has anyone got any ideas how I can record all this data onto a spreadsheet please?
Basically you won't have access to see who is copying your sheet if you use the built-in Make Copy.
What you could do is integrate part of the question you have provided, while disabling the built-in sharing method. So the only way to copy your sheet could be through a custom function you can control and register every time it executes.

Google script: unprotect a sheet through function when user has no right to do so

I have a spreadsheet with different sheets in Google sheet, 3 users can edit each one a sheet (protections are set, each user can edit only one sheet). They all can execute a google script function that writes what they edited in a summary sheet. I don't want anyone to be abble to edit the summary sheet, so I set myself as the only available editor.
So my problem is to authorize the 3 users, only through the google script function, to write in the summary sheet. I tried to use the following function :
var unprotected = summarySheet.getRange('G3:G10');
protection.setUnprotectedRanges([unprotected]);
but since the users are not allowed to edit the summary sheet, and since the function is run with the active user, so they can't give themselves the right to unprotect a range in the summary sheet... Do you know how to workaround this problem?
Thanks a lot!
I see two script-based choices, one easy and one quite hard, and one sheet-based choice, that is easiest:
Easy:
You run the "summarize" script instead of them or, you set the summarize script run on a trigger out of your account. Then you actually leave protections alone. You could set the summarize script to run on open with error catching if the user doesn't have the necessary authority to unprotect the summary sheet and/or write to the summary sheet.
Hard:
When they run the "summarize" script it calls a published standalone script that has been given the authorization to make the necessary protection changes. I'll be honest, I wouldn't be able to code this but have seen/heard of similar implementations.
Easiest:
Finally, I want to make sure you've considered having the summary sheet itself contain the necessary formulas, parsing, etc. to summarize data from the other sheets without any need of scripts for this aspect of the sheet. The sheet could call custom functions as needed if the parsing or other summarization functionality is beyond built-in functions' capabilities. The sheet could stay fully protected and update itself in real time as users enter data (no need for users to trigger the summary creation, unless spreadsheet settings have auto-recalculate turned off).
Edited to add: put in A1 of Summary sheet something like:
=summarize()
And have that custom function return a 2-dimensional array of the summarized data.

How do I copy a script from one spreadsheet to another spreadsheet?

I need to copy an existing script, which I did not write, into my existing spreadsheet. The script is called "SaveBack", and can be found here: https://docs.google.com/a/levelgroup.com/spreadsheet/ccc?key=0Agcb8bUVVOOodHhoV3BrRGZ6UEdSYnVLSEk3bllxRnc#gid=1.
My existing spreadsheet is too complex (it has many other spreadsheets that link to it) to copy all of its sheets into the spreadsheet that contains the script. I have already copied the SaveBack editor sheet template sheet to my main spreadsheet, but I can't figure out how to copy the SaveBack script that goes along with it into my spreadsheet.
Can anyone help? Thanks!
Open the save back script, select the script text, copy it, go to your sheet, create a new script (blank template), paste the script you copied into your new script, name is SaveBack (assuming it's a project), check the triggers on the original script and make sure your triggers match, and you should be good to go. You will have to change any sheet, document, or other string ID's to match your files' Id's, but that isn't too bad.
I do this sort of thing all of the time when I'm migrating things back and forth between my work and personal account. You could also create a copy of the other person's spreadsheet that you linked above, and it will move the script over with it. Then you can go to the script and get the project key to use it as library in your own scripts. Since you're using your copy as the library, you don't have to worry about someone else changing the script and breaking your functionality.