How do I convert this google script to work offline? - google-chrome

I am trying to automate data entry while roaming offline using my Chromebook.
I know that google drive is enabled offline and a standalone script in GAS should in theory do the trick but im not sure how to put the pieces together. So far I have the below code which works perfectly online (gets stuck in "running" offline) and I've got the GAS app installed. Any guidance would be greatly appreciated!
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Invoice/Receipt System')
// creates a menu item "Submit Order"
.addItem('Record Invoice', 'menuItem1')
.addToUi();
}
function menuItem1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var is = ss.getSpreadsheetByName("Template_Invoice");
var lastID = is.getRange("j6");
var nextID = is.getRange("j7");
var lastIDValue = lastID.getValue();
var source = ss.getSpreadsheetByName("Key_Invoice");
// sets the 'Key_DailyInput' Sheet as source
var target = ss.geSpreadsheetByName("DataBase_Invoice");
// sets 'Key_DailyInput' sheet as the target for copying data to.
var sourceData = source.getSheetValues(5,1,source.getLastRow(),15);
// sets range to gather source 'Key_DailyInput' data by finding last row, and Line 5 to Column 15
target.getRange(target.getLastRow()+1, 1, sourceData.length,15).setValues(sourceData);
// finds last row of target 'Orders' and writes to +1 row past last row up to column 15 using setValues of sourceData
// Following simply clears DailyInput so new data can be entered
is.getRange('C5:c8').clearContent();
is.getRange('G7:G8').clearContent();
is.getRange('B12:h28').clearContent();
is.getRange('b31:b34').clearContent();
// increases value by +1 so next Sales Order ID is incremented by 1
var cell = is.getRange("j6");
var cellValue = cell.getValue();
cell.setValue(cellValue + 1);
nextID.setValue(lastIDValue + 1);
}

As stated in other responses, the answer appears to be 'No'. However, while researching I did find the Command Line Interface for Apps Script (clasp) to manage and edit your projects offline. I'll post it here hoping it will be helpful to Apps Script developers.
CLASP Features
Develop Locally. clasp lets you write code on your own computer and upload it to Apps Script via command line when you're done. You can also download existing Apps Script projects and then edit them locally. Once the code is local, you can use your favorite development tools like git to work on Apps Script projects.
* Manage Deployment Versions.
* Create, update, and view multiple deployments of your project.
* Structure Code. clasp automatically converts your flat project on script.google.com into folders.
You can find more information on clasp at https://developers.google.com/apps-script/guides/clasp. However, you'll also need to activate the Linux(beta) on your Chromebook utilizing these instructions.

Short answer
Google Apps Script can't be ran offline because they run on the server-side.
Explanation
From https://developers.google.com/apps-script/overview
Google Apps Script is a scripting language based on JavaScript that
lets you do new and cool things with Google Apps like Docs, Sheets,
and Forms. There's nothing to install — we give you a code editor
right in your browser, and your scripts run on Google's servers.

Related

Web App from Standalone script to be opened in multiple identical google spreadsheets

Help! I have one standalone script, which I want to be called in 50 separate google spreadsheets onOpen of each of these spreadsheets. I need a web app or something that will run this script as myself, not the user opening one of those 50 sheets, as the script is referring to some other files, to which the users do not have access. **My problem I do know how to make this standalone script run when one of the 50 files is opened - should I put some code (like doGet()) in my spreadsheet bound script orwhere & what???**And should I put something in my standalone script? And can I do this avoiding creating HTML pages etc.? Thanks!
My standalone script that I want to apply to my multiple spreadsheets (in short it gets a name from a cell from the opened spreadsheet, creates a copy of a separate master spreadsheet, put the saved name to the master spsheet, recalculates there, put its back to the open spreadsheet (where the name was initially):
var masterFileId = "1i-------------------DM";
var tempHolderFolder = "1D---------------ae";
function ImportIndReport2(personalFileId) {
var mf = SpreadsheetApp.openById(masterFileId);
var destFolder = DriveApp.getFolderById(tempHolderFolder);
var indCopyTest = DriveApp.getFileById(mf.getId()).makeCopy("test temp2", destFolder);
var URLCopy = indCopyTest.getUrl();
var currentIdToUse = SpreadsheetApp.openById(personalFileId).getSheetByName('Welcome').getRange('D6').getValue();
SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName("Welcome").getRange("C6").setValue(currentIdToUse);
var now = new Date();
SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName("jj").getRange("A1").setValue(now);
var oldSheet = SpreadsheetApp.openById(personalFileId).getSheetByName("jj");
SpreadsheetApp.openById(personalFileId).deleteSheet(oldSheet);
var CurrentIdDestination = SpreadsheetApp.openById(personalFileId);
var indReportToCopy = SpreadsheetApp.openById(indCopyTest.getId()).getSheetByName('jj');
SpreadsheetApp.openById(indCopyTest.getId()).insertSheet('Temp');
var tempDestination = SpreadsheetApp.openById(indCopyTest.getId());
indReportToCopy.getRange("A1:Z30").copyTo(tempDestination.getSheetByName('Temp').getRange("A1:Z30"),SpreadsheetApp.CopyPasteType.PASTE_VALUES,false);
indReportToCopy.getRange("A1:Z30").copyTo(tempDestination.getSheetByName('Temp').getRange("A1:Z30"),SpreadsheetApp.CopyPasteType.PASTE_FORMAT,false);
tempDestination.getSheetByName('Temp').copyTo(CurrentIdDestination).setName('jj');
var tempDestinationForDelete = DriveApp.getFileById(indCopyTest.getId());
tempDestinationForDelete.setTrashed(true);
var html = HtmlService.createHtmlOutput("<div>The report has been updated</div>").setSandboxMode(HtmlService.SandboxMode.IFRAME).setWidth(150).setHeight(100);
SpreadsheetApp.getUi().showModalDialog(html, "Message");
}
Updated - solved it with clickable url of the webapp script in the individual spreadsheet bound script, and adding simple doGet() with name of my main function (indreport..) to standalone script.
Instead of using a "web app" it might be better to use an installable on open trigger, but installable triggers are limited to 20 per script / user.
You might create three stand alone projects to hold the triggers and put the main code on a fourth stand alone project to be used as library on the three projects.
Another option is to create and add-on. This might imply extra work but it will be done only once and will be easier to maintain.
Reference
https://developers.google.com/apps-script/guides/triggers/installable

Google Sheets - make application scripts run on phone App version

I have created a Google Sheets spreadsheet and some scripts to help me track the attendance of members at a gym.
My project has the following scripts associated:
- a trigger that sends periodic events based on some checks I do and also updates some cells. This works fine I scheduled it to run daily and it seems to work properly even if my PC isn't on :D
- I have also added a onEdit function that's supposed to remind the user that he/she has modified a cell and let him know that he can undo in order to revert the changes. This is the function:
function onEdit(e) {
var ui = SpreadsheetApp.getUi();
var cell = e.range;
var actual_cell = e.source.getActiveRange().getA1Notation();
var row = cell.getRow();
var column = cell.getColumn();
var old_content = e.oldValue;
ui.alert('Modificare celula' + ' ' + actual_cell + '.' + ' UNDO to rever the changes')
}
This, however, does not work on the mobile version: Sheets for Android.
Is there any way I can make my app fully functional on the mobile version as well? I want to have that warning message that tells the user a cell was modified on the mobile version as well. How could I do that?
Also another question regarding the trigger that sends periodic emails.
I have used a Time Based daily trigger that runs some function. Do I have to be logged on my account at all times on my PC(or any other device) in order for this script to run periodically? Or is it on the cloud and it will run even if I'm not logged on any device?
To be able to trigger the script bound to the spreadsheet from a mobile App (Android or iOS), you have to deploy the script as a API executable (Publish -> Deploy as API executable from menu bar in script) [1].
As stated by a Googler in this comment [2], since last year you can't display any element from the Ui class [3] in mobile versions.
About your other question: It doesn't matter if your computer is on, the script/trigger will run either way.
[1] https://developers.google.com/apps-script/api/quickstart/target-script
[2] https://b.corp.google.com/issues/36763692#comment6
[3] https://developers.google.com/apps-script/reference/base/ui

Interacting with activeSheet, activeCell in a stand-alone script

I created a google sheet that stores info row by row that I send out in different emails as templates. I'd like to give a copy of the file to my colleagues, but I'm figuring out it's probably better from a version-control standpoint to deploy the script as an add-on so they can install it individually, yet I retain centralized control of the code. However, my code uses a decent amount of ActiveSpreadsheet() and ActiveSheet() and activeCell() functions that apparently only container-bound scripts can use.
Anyone have any recommendations as to how to replace the following lines in a stand-alone script? Also any commentary on my idea to change my script to a stand-alone script to deploy as an add-on would be appreciated!
var sheet = SpreadsheetApp.getActiveSheet();
var scriptRow = sheet.getActiveCell().getRow();
var tst1 = SpreadsheetApp.getActiveSpreadsheet().toast('Draft create failed');
Try this instead of the above code:
var sheet = SpreadsheetApp.openById("your-spreadsheet-id").getSheetByName("your-table-name");
var scriptRow = sheet.getActiveCell().getRow();
var tst1 = sheet.toast('Draft create failed');
It's hard to say if an add-on would be good. Depends on the exact work the script is doing: You can either publish it as Add-on, Web app or API.

Google Apps Script - Editors Execute Script on Spreadsheet

I created a Google spreadsheet to help my team keep track of their calendar for the upcoming week. The spreadsheet has 2 tabs: an "Overview" tab and a "Details" tab. On the "Details" tab, the top row lists all of the weekdays through the end of the year and the left column lists the team members names. For each day, the team member writes what school they will be at and the description of support.
The "Overview" tab has the same structure as the "Details" tab (i.e., dates across the top and names listed on the left), but provides a visual summary of where each person will be. I wrote a script that takes the school name from the "Details" tab and enters it in the cell that corresponds to the correct person/date on the "Overview" tab. Then it takes the description of support and creates a Note on the "Overview" tab with the description. It also shades the cell on the Overview tab so I can see that support is planned. So from the "Overview" tab, all I have to do is mouse over the cell and it will show me the planned support.
It works great in my own account. However, I have shared the spreadsheet with some team members and it is not working. I don't want my team to be able to edit the "Overview" tab because I only want them to input their information on the "Details" tab. If I give them editing rights to the "Overview" tab, then they can change the Notes, cell colors, sheet structure, etc (that is why I want the Overview tab as view-only). So, I protected the "Overview" tab so they cannot make edits. However, because they cannot edit this sheet, it seems that the script will not run and update the Overview tab when they edit the Details tab. I assume this is because they don't have permission (because the sheet is protected). When I remove the sheet protection, the script runs just fine for them.
Any thoughts on how I can get around this? I really need to keep the Overview tab View-only. Thanks.
If, as you stated in comments, you use an installable on Edit trigger the problem you describe should not occur since "When a script runs because of a trigger, the script runs using the identity of the person who installed the trigger, not the identity of the user whose action triggered the event. This is for security reasons. For example, if you install a trigger in your script, but your coworker performs the action that triggers the event, the script runs under your identity, not your coworker's identity. For complete information on this, see Understanding Permissions and Script Execution."
see doc here
EDIT : sorry, I didn't see the issue about this special case : issue 1562 posted on july 2012, status "triaged"
EDIT 2 : I tried #tracon6 suggestion to remove the protection temporarily but it doesn't work either... the script generates an error when trying to apply the protection.
but
as an EDIT 3
I found a workaround that works ! We can add an editor just for the time we write to the targetSheet and remove it right after... using a flush in between it works.
here is the code I used to test :
function onEditInstallable(event) {
var sheet = event.source.getActiveSheet();
if(sheet.getName()=='Sheet1'){return};
var r = event.source.getActiveRange();
var column = r.getColumn();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var targetSheet = ss.getSheetByName("TFA");
var targetSheet = ss.getSheetByName("Sheet1");
var user = SpreadsheetApp.getActive().getEditors()[1];
var permission = targetSheet.getSheetProtection();
permission.addUser(user);
targetSheet.setSheetProtection(permission);
SpreadsheetApp.flush();
targetSheet.getRange(1,column).setValue('value change on sheet 2, column : '+column);
SpreadsheetApp.flush();
permission.removeUser(user)
targetSheet.setSheetProtection(permission)
}
EDIT 4 :
Since it seems that many editors could have access to these sheets (see last comments), there has to be a way to know who is active on the spreadsheet. In a non domain environment this is not possible with a triggered function so I would suggest a small uiApp with a list from which the user has to choose, trigger that with an installable onOpen and store the value to use it in the main function.
Heres is a piece of code to handle that aspect.
function SpecialonOpen(){
var s = SpreadsheetApp.getActive();
var app = UiApp.createApplication().setTitle('Please select your email in this list').setWidth(300).setHeight(300);
var list = app.createListBox().setName('list')
var editors = s.getEditors();
for(var n in editors){
list.addItem(editors[n].getEmail());
}
var handler = app.createServerHandler('getMail').addCallbackElement(list);
var btn = app.createButton('select',handler);
s.show(app.add(list).add(btn))
}
function getMail(e){
var email = e.parameter.list;
var editors = ScriptProperties.getProperty('currentEditors')||' ';
if(editors.indexOf(email)==-1){
editors+=(','+email);
ScriptProperties.setProperty('currentEditors',editors);
}
var app = UiApp.getActiveApplication().close();
var editors = ScriptProperties.getProperty('currentEditors').split(',');
if(editors[0].length<2){editors.splice(0,1)};
Logger.log(editors)
return app
}
You should also add a function to reset this list at some moment... don't know what would be best ? on a daily base maybe ? I'll let this to you ;-)
Do the Overview tab and the Details tab need to be in the same spreadsheet?
Perhaps you can have the Overview tab in a spreadsheet that only you can access and have the Details tab in a spreadsheet that everyone else can access -- then you can modify your script to use the information in the new "Details Spreadsheet" to update the new "Overview Spreadsheet".
Alternatively, in your script, you could turn off the protection on the Overview tab, then run the main part of the script, then turn on the protection on the Overview tab again before the script ends (see https://developers.google.com/apps-script/reference/spreadsheet/page-protection#setProtected(Boolean))
Admittedly, neither of these options are as clean the onEdit option.
Here is a hacky workarround you can try: publish the script as a content service with anonymous access, and call it with urlFetch from your trigger, passing the necessary param to your service.
This should cause the code to execute in a different context where the effective user is the script publisher and not te user. Will be slower thou.
Why don't you protect the Overview sheet using the options in the Data tab and set permissions for edit to 'Only you'. This will protect the sheet from everyone, even editors, leaving the sheet in View Only mode for everyone except yourself.

Unable to access (an save) my SpreadSheet Google App Script

I have a Google SpreadSheet (filled through a form) associated to a Google Apps Script.
Now, the spreadsheet contains too many cells and I'm not able to load it anymore (server timeout).
I'm not interested in the data, but I really want to retrieve the script (the source code).
Unfortunately, the only way I know is to load the SpreadSheet first :-(
(Copying the Document ends in a timeout and the script is not copied.)
Any suggestion?
Regards,
Rémi.
Create another script which makes a copy of original spreadsheet to backup the data and then clear the original spreadsheet through the script.
However this seems an issue which should be reported to Google Spreadsheet Forum
Here is an example code which will backup the spreadsheet as a copy and clear the original spreadsheet
function backupDataAndClear(){
var sourceSS = SpreadsheetApp.openById('ID_OF_ORIGINAL_SS');
var newSS = sourceSS.copy('Backup SS');
var sourceSheets = sourceSS.getSheets();
for(var i in sourceSheets){
sourceSheets[i].clear();
}
}