Script to cause an ImportRange function to run on demand - google-apps-script

first, let me say, i am not a programmer, but i understand and can build spreadsheet ok. I'm newer to doing so in Google Sheets. I am using an Importrange function to copy date from one spreadsheet tab to another spreadsheet. I'm finding that there is a delay in the updating. I checked my Settings for recalculations and Google Sheets is set to refresh when data is changed. However, its not doing so. So, i wanted to create a button the user can hit and force the ImportRange refresh.
I found some code that appears to accomplish this, but I can't get it to work:
function myFunction() {
SpreadsheetApp.getActive().getRange('A1').setValue('IMPORTRANGE('https://docs.google.com/spreadsheets/d/abcd123abcd123', 'sheet1!A:B')')
}
Here is my import range which is in CELL C4 of a sheet named CALCS:
=importrange("https://docs.google.com/spreadsheets/d/1B5PEI5f4-TAhS77-poCYTiWI6w2t2BEzfiP5kXVq9lk/edit#gid=1661941505","PO TRACKING!A4:T1000")

Welcome to the wonderful world of scripts! There is so much you can do with your projects when using Google Apps Script.
Your question is a good one but I took a different approach. Rather than using an import range that needs to be recalculated, I just wrote a script that would do a similar thing. If you need to collect a specific range of data instead of the entire sheet, then just make one small change to the script provided.
//CHANGE
var poData = poTab.getDataRange().getValues();
//TO
var poData = poTab.getRange('INSERT A1 NOTATION HERE').getValues();
That should allow you to select a specifc range if needed.
//TRANSFER DATA FROM ONE TAB TO ANOTHER
function transferFromSameSpreadsheet() {
//GETS ACTIVE SPREADSHEET
var ss = SpreadsheetApp.getActiveSpreadsheet();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS STORED
var poTab = ss.getSheetByName('Po Tracking');
//STORES ALL DATA FROM SHEET
var poData = poTab.getDataRange().getValues();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS SENT
var transferTo = ss.getSheetByName('insert sheet name here');
//CLEARS OLD DATA
transferTo.clearContents();
//UPDATES SHEET WITH NEW DATA
transferTo.getRange(1,1,poData.length,poData[0].length).setValues(poData);
}
//TRANSFER DATA FROM A MASTER SHEET TO ANOTHER WORKBOOK. RUN SCRIPT IN ACTIVE WORKBOOK, NOT MASTER.
function transferFromDifferentSpreadsheet() {
//OPENS SPREADSHEET BY ID
var ms = SpreadsheetApp.openById('insert spreadsheet id here');
//GETS ACTIVE SPREADSHEET
var ss = SpreadsheetApp.getActiveSpreadsheet();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS STORED
var poTab = ms.getSheetByName('Po Tracking');
//STORES ALL DATA FROM SHEET
var poData = poTab.getDataRange().getValues();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS SENT
var transferTo = ss.getSheetByName('insert sheet name here');
//CLEARS OLD DATA
transferTo.clearContents();
//UPDATES SHEET WITH NEW DATA
transferTo.getRange(1,1,poData.length,poData[0].length).setValues(poData);
}
Good luck!
Get range A1 notation documentation: https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangea1notation

What your code is doing is just setting the cell value as the string you input in the setValue()
If you want to input formula you can either include "=" when using setValue() or use setFormula() method
Sample:
function myFunction() {
SpreadsheetApp.getActive().getRange('A1').setFormula('IMPORTRANGE("https://docs.google.com/spreadsheets/d/xxxxx", "sheet1!A:B")');
SpreadsheetApp.getActive().getRange('D1').setValue('=IMPORTRANGE("https://docs.google.com/spreadsheets/d/xxxxx", "sheet1!A:B")');
}
Note:
I also fixed the string format in your code so that it will provide a string input to the setValue() and setFormula()
References:
Range.setValue()
Range.setFormula()

Related

Google Sheets Apps Script: Copy selected Cells into new Worksheet

I'm new to Apps Script and don't know how I can get my selected cells copied into another worksheet.
I want the following but instead of copying A7:A9 into a new worksheet i want the cells/range that are selected at the time to be copied. the selected cells/range are going to be different each time that i want to use the script.
var rangeList = SpreadsheetApp.getActiveRangeList();
function TestMakro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A7:A9').activate();
spreadsheet.insertSheet(1);
spreadsheet.getRange('Tabellenblatt1!A7:A9').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
};
thanks
So instead of using SpreadsheetApp.getActive() it's better to define separately the spreadsheet and the active range. you can then get A1 annotation of the active range to find the same area in the new sheet (I assumed that is what you are trying to achieve). Key here is that even if you create a new sheet, it doesn't become Active. Hence you still need to find the same range in it.
Here is the code:
function TestMakro() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const activeRange = spreadsheet.getActiveRange();
const newSheet = spreadsheet.insertSheet(1)
activeRange.copyTo(newSheet.getRange(activeRange.getA1Notation()) , SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false)
}

Trigger when change is happening in specific sheet

I have a sheet with data that is imported through IMPORTRANGE to another sheet. When I make changes to Spreadsheet 1 >>> Spreadsheet 2 I have a script that copy the data from Copy to Paste. It is working all fine however I want to make sure that the script only runs when changes are made in the 'Copy' sheet and not any other. At the moment it runs independent on what sheet I make changes in.
Spreadsheet 1
Spreadsheet2
I tried onChange trigger two different ways...
This one makes the change but triggers when changes are made in any of the sheets
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getName() === 'Copy') {
copyInfo();
}
}
AND
(this does not work)
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
if( sheet.getActiveSheet().getName() !== "Copy" ) return;{
copyInfo();
}}
The copy code looks like this...
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
// get source range
var source = copySheet.getRange(2,1,12,8);
// get destination range
var destination = pasteSheet.getRange(2,1,12,8);
// copy values to destination range
source.copyTo(destination);
}
Copy Changes from one Sheet To Another after Imported Range Changes
Spreadsheet 2 Imports a range to Sheet1 from Spreadsheet 1 Sheet1 and when a change occurs in Spreadsheet 2 Sheet1 we copy data from Spreadsheet2 Sheet1 to Spreadsheet2 Sheet 2.
function onMyChange(e) {
//Logger.log(JSON.stringify(e));//useful during setup
//e.source.toast("Entry");//useful during setup
const sh = e.source.getSheetByName("Sheet1")
Logger.log(sh.getName());
if(e.changeType == "OTHER") {
let tsh = e.source.getSheetByName("Sheet2");
sh.getRange(2,1,12,8).copyTo(tsh.getRange(2,1));//Only need the upper left hand corner of the range. Thus you change change the size of the source data without having to change the target range.
}
}
One might be inclined to attempt to use e.source.getActiveSheet() unfortunately this does not necessary get the sheet that you have written unless in is SpreadsheetApp.getActive().getSheets()[0]. onChange event object always returns the most left sheet in the spreadsheet in this situation so it can't be used to identify the sheet that is being written to from the importRange function.
With this function you no longer require another function.
A simple if should work, but you're trying to compare the name to the wrong field. According to Google's documentation, the source field in the Event Object e returns the Spreadsheet object, which is the entire document.
Instead you can use the e.range field, which contains the exact range where the edit was made, and the Range class has a getSheet() method to get the parent Sheet.
So you can modify your sample to the following:
function Trigger2(e) {
var sheet = e.range.getSheet();
if (sheet.getName() === 'Copy') {//Edit: this will only work if sheet is the most left sheet in the spreadsheet
copyInfo();
}
}

Copy a range in active sheet, create new spreadsheet with custom name, paste range

I recently wrote my first Google Apps script that makes a copy of a spreadsheet (including all tabs within that spreadsheet) and places it into a specific folder in the user's Drive. The copy is renamed based on a cell value in the original spreadsheet. Here is my script, for reference:
function copyDocument() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Get current active spreadsheet.
var id = ss.getId(); // Get current active spreadsheet ID.
var sstocopy = DriveApp.getFileById(id); // Get spreadsheet with DriveApp.
var sheet = ss.getActiveSheet(); // Get current active sheet.
var sheet_name = sheet.getRange("B1").getValue(); // Get the value of cell B1, used to name the new spreadsheet.
var folder_name = sheet.getRange("C23").getValue(); // Get the target folder ID.
var folder = DriveApp.getFolderById(folder_name); // Get the ID of the folder where you will place a copy of the spreadsheet.
sstocopy.makeCopy(sheet_name,folder); // Make a copy of the spreadsheet in the destination folder.
}
This script works, but I have been asked to modify it because the script I wrote is copying over unnecessary tabs and data that is causing confusion to users.
The new script should make a copy of a specific range in a specific sheet, create a new spreadsheet, and paste that range into it. It should also name itself after a cell value in the range.
However, the only method I have come across that specifically copies a sheet into a new spreadsheet is copyTo(spreadsheet). However, the Google Apps Script Guide specifies that "the copied sheet will be named 'Copy of [original name]'" by default.
I want to be able to rename the copied sheet after a specific cell. My question is, can I use copyTo(spreadsheet) and give the new spreadsheet a custom name, based on a cell?
Thanks!
You will need to get the data from the specific sheet to copy out to a variable of using:
var sourceSheet = ss.getSheetByName("Sheet1");
var sourceData = sourceSheet.getDataRange().getValues();
var originalRangeNotation = sourceSheet.getDataRange().getA1Notation();
Then you need to create a new, empty file and make a sheet with the same name
var ssNew = SpreadsheetApp.create("My New File Name");
ssNew.insertSheet('My New Sheet');
Then add the contents from the saved data to the new file. Since the insertSheet makes the new sheet the active one, we use:
var sheetNew = ssNew.getActiveSheet();
var rangeNew = sheet.getRange(originalRangeNotation);
range.setValues(sourceData);
Thanks. I ended up using this in place of sstocopy.makeCopy()
folder.addFile()
new_sheet.getActiveSheet().getRange("X:Y").setValues(sheet.getRange("X:Y").getValues())
DriveApp.getRootFolder().removeFile(temp)

return values of selected row into a metadata file

Is it possible to return the values row that is selected within a spreadsheet? We would like to have the ability to have a user select a row in a spread sheet, activate our application, and the values from that row are spit out into some sort of metadata file that we can read. Is this possible to do or is there something like this that we can modify to work for our application?
Getting the data from currently selected cells in a spreadsheet is relatively straightforward in Apps Script. For a container-bound script attached to the spreadsheet in question, you can do the following:
function getSelectedData() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // returns the Spreadsheet
var sheet = ss.getActiveSheet(); // returns the active Sheet
var selected = sheet.getActiveRange(); // returns the selected Range
var selectedData = selected.getValues(); // returns a 2D Object array of cell data
Logger.log(selectedData); // Writes the data to the Log
}
Once you have the values, you can send or write those values wherever you need.

GAS: Range Protection?

I've looked everywhere and it seems that GAS hasn't caught up with Google Spreadsheet. Is there a brute force method for setting protection on certain ranges in a sheet..? (And making a protected sheet with all the formulas and referring to them won't help me.)
I found this through google: https://code.google.com/p/google-apps-script-issues/issues/detail?id=1721
I even commented at the bottom. (More of a complaint than anything useful.) But the guy above me there posted this code:
//Function to Protect Target Sheet
function ProtectTargetSheet() {
//Enter ID for each Worksheet
var IDs = ["Sheeet_1", "Sheet_2"]
//Enter Page to protect, in order of WorkSheets
var Sheet_names = ["Page_1", "Page_2"]
//For each sheet in the array
for ( i = 0; i < IDs.length; i ++) {
//id of sheet
var sheet = IDs[i]
//activate dedicated sheet
var ActiveSheet = SpreadsheetApp.openById(sheet)
//Find last row and column for each sheet
var LastRow = ActiveSheet.getSheetByName(Sheet_names[i]).getLastRow();
var LastCol = ActiveSheet.getSheetByName(Sheet_names[i]).getLastColumn();
//Name of protectSheet
var Name = "Protect_Sheet";
//Range for Protection
var Named_Range = ActiveSheet.getSheetByName(Sheet_names[i]).getRange(1, 1, LastRow, LastCol);
//Impletment Protect Range
var protected_Range = ActiveSheet.setNamedRange(Name, Named_Range);
}
}
I don't see how this can work to give protection to a range when shared. It seems that it would just create a Named Range. He does say to set the permissions manually first. But I can't figure what exactly he meant.
Anyways, I was hoping that someone had found a way by now to do this until Google syncs GAS with its counterpart.
My wish is to, through 100% code, select a range in a sheet, within a spreadsheet, and make it so that when I share this whole spreadsheet to a person, that he or she can't edit that range. There will be other parts in that sheet that they have to be able to edit. Just not that range. It is easy to do this manually, but when having to create 100s of spreadsheets, it would be help to be able to do this through GAS.
Thanks.
Part of your question asked about protecting a sheet. Please have a look here: setProtected(protection)
As for programmatically protecting a range no. However, you could protect a sheet, does not need to be in the same spreadsheet and then create an onEdit trigger that would replace any change in your "protected" range with the original source data.
Something like this:
function onLoad() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var protected = ss.getSheets()[1].getRange("A1:B2").getValues(); //You could use openByID to get from a different ss
var target = ss.getSheets()[0].getRange("A1:B2").setValues(protected);
}
function onEdit(){
onLoad();
}
Every time a change is made to the spreadsheet the script will rewrite the data in the sheet for the range you specify.
The easiest approach I've found is to use Data Validation: that is, write a script which will examine each cell to be 'protected' and create and apply a validation rule which enforces entry of the existing content and rejects anything else. [Of course, this also implies that you have designed your spreadsheet such that all entered data is on sheet or sheets separate from those which have formula embedded. On these you use normal sheet protection.]
According to Control protected ranges and sheets in Google Sheets with Apps Script, posted on February 19, 2015, now is possible to protect a Google Sheets range.
From https://developers.google.com/apps-script/reference/spreadsheet/protection
Class Protection
Access and modify protected ranges and sheets. A protected range can
protect either a static range of cells or a named range. A protected
sheet may include unprotected regions. For spreadsheets created with
the older version of Google Sheets, use the PageProtection class
instead.
// Protect range A1:B10, then remove all other users from the list of editors.
var ss = SpreadsheetApp.getActive();
var range = ss.getRange('A1:B10');
var protection = range.protect().setDescription('Sample protected range');
// Ensure the current user is an editor before removing others. Otherwise, if the user's edit
// permission comes from a group, the script will throw an exception upon removing the group.
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
If you are sending 100s of copies of the same sheet. Then create a template sheet, protect the ranges in it manually and send a copy of the template. It will retain the protection.
Sorry but as others have said there is not script method of setting protection at a sub-sheet level.