GoogleSheets with GoogleApps. A little loss with Creating and copying - google-apps-script

A month into google apps/googlesheets. I've got some of the basics down, however struggling to put a lot of basic concepts together.
Step 1) Create and check if Spreadsheet exists in folder. If it doesn't exist create one based on the name in Cell A1 and COPY ActiveSpreadsheet() data to that new FILE with sheet name TODAY() date.
Step 2) If a spreadsheet with name exists, copy from ActiveSpreadsheet() to the spreadsheet named in Cell A1 with a NEW SHEET named after today's date.
So far I have got pieces of stuff together but I am MISSING basic knowledge of trying to put it altogether. Sorry if its a COMPLETE mess I'm trying to piece it together as I go. ANY HELP WILL be appreciated or websites/resources to lead me in the right direction.
function saveAsSpreadsheet(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var range = sheet.getRange('Sheet1!A1');
var destFolder = DriveApp.getFolderById("XXXXXXXXXX");
DriveApp.getFileById(sheet.getId()).makeCopy(NAME CHECK, destFolder);
if( ss.getSheetByName('') == null)
//if returned null means the sheet doesn't exist, so create it
var val = Utilities.formatDate(new Date(A2), "GMT+1", "MM/dd/yyyy");
ss.insertSheet(val, ss.getSheets().length, {template: templateSheet});
sheet2 = ss.insertSheet(A2);
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName(new Date(A2));
var C2=sh1.getRange('C2').getValue();
var L2=sh1.getRange('L2').getValue();
sh2.appendRow([new Date(),C2,L2]);
}

Probably you need something like this:
function main() {
// get current date
var date = Utilities.formatDate(new Date(), 'UTC', 'YYYY-MM-dd');
// get current spreadsheet
var cur_ss = SpreadsheetApp.getActiveSpreadsheet();
// get folder
var folder = DriveApp.getFolderById('###'); // <--- your folder ID goes here
// get file name from the cell 'Sheet1!A1'
var name = cur_ss.getSheetByName('Sheet1').getRange('A1').getValue();
// seach for the file in the folder and get it if it exists
var file = get_file_from_folder(folder, name);
// if file exists: copy current sheet to this file
// and rename copied sheet
if (file != null) {
var dest_ss = SpreadsheetApp.openById(file.getId());
cur_ss.getActiveSheet().copyTo(dest_ss).setName(date);
}
// if file doesn't exists: make а copy of current spreadsheet,
// and rename the sheet with the same name as your active sheet
else {
var file = DriveApp.getFileById(cur_ss.getId()).makeCopy(folder).setName(name);
var dest_ss = SpreadsheetApp.openById(file.getId());
dest_ss.getSheetByName(cur_ss.getActiveSheet().getName()).setName(date);
}
}
// functions searches for the file with given name in given folder and returns the file.
// In case the folder has no such file the function returns 'null'
function get_file_from_folder(folder, name) {
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getName() == name) return file;
}
return null;
}
Keep in mind, sometimes the destination spreadsheet can have a sheet with the same name already. It will get an error. Probably you need to handle that collision somehow.

Related

Issue with duplicating a google sheet to a new file - only range wanted - not all

I have written a script that duplicates an entire google sheet to a new document and saves it in a folder when a button is pressed, however, I only want it to copy a specific range (A1:F52).
Every attempt I make at modifying the script to only have .getRange("A1:F52") throws an error that getrange is not a function, what am I doing wrong and how should it actually be?
This script functions perfectly as it is, but I just want it to only take the range across, any help would be greatly appreciated.
// SAVE INVOICE
function copyRowsWithCopyTo() {
// SET VALUES IN CELL
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A1:F60').activate();
spreadsheet.setCurrentCell(spreadsheet.getRange('F3'));
spreadsheet.getRange('A1:F60').copyTo(spreadsheet.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
//COPY CONTENTS TO INVOICE SHEET
let spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
let sourceSheet = spreadSheet.getSheetByName('DO NOT EDIT');
// SET SPREADSHEET MONTH
let targetSheet = spreadSheet.getSheetByName('JANUARY 2023');
let row = sourceSheet.getActiveRange().getRow();
let activeRow = sourceSheet.getRange( 2,1,1,20);
let last_row = targetSheet.getLastRow();
activeRow.copyTo(targetSheet.getRange('A'+(last_row+1)+':H'+(last_row+1)),{contentsOnly:true});
// REPLICATE INVOICE SHEET TO NEW DOCUMENT AND SAVE IN INVOICE FOLDER
var sheet = SpreadsheetApp.getActiveSheet(); // Get current active sheet.
//GET SHEET NAME
var sheet_name1 = sheet.getRange("F4").getValue(); // Get the value of cell F4, used to name the new spreadsheet.
var sheet_name2 = " - "
var sheet_name3 = sheet.getRange("A9").getValue(); // Get the value of cell A9, used to name the new spreadsheet.
var sheet_name = sheet_name1 + sheet_name2 + sheet_name3
var folder = DriveApp.getFolderById("1DVdganPrCfePk41kteKnXSqKmL4Pivu5");
// Get the ID of the folder where you will place a copy of the spreadsheet.
var newSS = SpreadsheetApp.create(sheet_name); // create new blank spreadsheet in a root folder
var asFile = DriveApp.getFileById(newSS.getId()); // get new spreadsheet as a file
folder.addFile(asFile); // add this file to destination folder
DriveApp.getRootFolder().removeFile(asFile); // remove a file from root folder
var copiedSheet = sheet.copyTo(newSS).getRange("A1:F52"); // copy active sheet to new spreadsheet
copiedSheet.setName(sheet_name); // rename copied sheet
newSS.deleteSheet(newSS.getSheetByName('Sheet1')); // remove "Sheet1" sheet which was created by default in new spreadsheet
// run Clear function
clear()
}
I am clearly mistyping the .getrange parameter as it throws an error, but I cannot work out what im doing wrong.

Add a new sheet from template into a raw google script and organize into a folder

I have this spreadsheet and I would like to copy the template in a directory for each month and add the link in each corresponding cell, adding the corresponding data
Now I do it adding new sheets but I wanted to organize by directories for each month.
It's possible?
Thanks
https://docs.google.com/spreadsheets/d/1abdggD73Zb0XmRoFaMx0ssULjtLLTHhFQV3ikbaEK_I/edit?usp=sharing
function newSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var templateSheet = ss.getSheetByName("Plantilla")
var sheet1 = ss.getSheetByName("Sheet1")
var getNames = sheet1.getRange("G2:G").getValues().filter(String).toString().split(",");
for (var i = 0; i < getNames.length; i++) {
var copy = ss.getSheetByName(getNames[i]);
if (copy) {
Logger.log("Sheet already exists");
} else {
templateSheet.copyTo(ss).setName(getNames[i]);
ss.setActiveSheet(ss.getSheetByName(getNames[i]));
ss.moveActiveSheet(ss.getNumSheets());
}
}
}
// function to create the index
function createIndex() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var namesArray = sheetNamesIds(sheets);
var indexSheetNames = namesArray[0];
var indexSheetIds = namesArray[1];
// check if sheet called sheet called already exists
// if no index sheet exists, create one
if (ss.getSheetByName('index') == null) {
var indexSheet = ss.insertSheet('Index',0);
}
// if sheet called index does exist, prompt user for a different name or option to cancel
else {
var indexNewName = Browser.inputBox('The name Index is already being used, please choose a different name:', 'Please choose another name', Browser.Buttons.OK_CANCEL);
if (indexNewName != 'cancel') {
var indexSheet = ss.insertSheet(indexNewName,0);
}
else {
Browser.msgBox('No index sheet created');
}
}
// add sheet title, sheet names and hyperlink formulas
if (indexSheet) {
printIndex(indexSheet,indexSheetNames,indexSheetIds);
}
}
// function to update the index, assumes index is the first sheet in the workbook
function updateIndex() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var indexSheet = sheets[0];
var namesArray = sheetNamesIds(sheets);
var indexSheetNames = namesArray[0];
var indexSheetIds = namesArray[1];
printIndex(indexSheet,indexSheetNames,indexSheetIds);
}
// function to print out the index
function printIndex(sheet,names,formulas) {
sheet.clearContents();
sheet.getRange(1,1).setValue('Workbook Index').setFontWeight('bold');
sheet.getRange(3,1,names.length,1).setValues(names);
sheet.getRange(3,2,formulas.length,1).setFormulas(formulas);
}
// function to create array of sheet names and sheet ids
function sheetNamesIds(sheets) {
var indexSheetNames = [];
var indexSheetIds = [];
// create array of sheet names and sheet gids
sheets.forEach(function(sheet){
indexSheetNames.push([sheet.getSheetName()]);
indexSheetIds.push(['=hyperlink("#gid='
+ sheet.getSheetId()
+ '";"'
+ sheet.getSheetName()
+ '")']);
});
return [indexSheetNames, indexSheetIds];
}
Solution
With the use of the Drive service in Apps Script and Spreadsheet service you can easily achieve your intentions. The following is the self explanatory commented piece of code that achives what you are aiming for.
NOTE : I have only made an example of how it would be with the first month (ENERO) as to achive the rest it would just be copying what I did with the first month.
function myFunction() {
// Get the sheet where we have all the dates
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
// Get the month name. Repeat this for the other months you might want to include
// For example if February is on A10 just get the range from A10
var Month1 = sheet.getRange('A1').getValue();
// Create a parent folder in the root of your drive where you will be storing everything for this project
var parentFolder = DriveApp.createFolder('Months');
// Create a folder for each month using the name we got before. Repeat for the rest of the months.
var Month1Folder = DriveApp.getFolderById(parentFolder.getId()).createFolder(Month1);
// Get the dates of each month. Repeat for the rest of the months.
// For example, if February dates go from A11 to A21 get the range A11:A21
// Also, getValues returns an array of arrays. Therefore I have flatten it to make a simple 1D array
var Month1Days = sheet.getRange('A2:A4').getValues().flat();
// For every element of our Month 1 date names array
// Repeat this block for the rest of the months substituying the array and column (to not overwrite) accordingly
for(i=0;i<Month1Days.length;i++){
// Create a new spreadsheet with the name of the dates of that month and get its id
var idFile = SpreadsheetApp.create(Month1Days[i]).getId();
// Get the file object of the new Spreadsheet
var file = DriveApp.getFileById(idFile);
// Add this file to our month folder
Month1Folder.addFile(file);
// Remove the duplicate that is still in the root folder
DriveApp.getRootFolder().removeFile(file);
// Paste a link to this new spreadsheet in our original sheet in the third column
sheet.getRange(i+1, 3).setValue(file.getUrl());
}
}
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

create new folder and copy google sheet, with new name to new folder

I am trying to automate the creation of new folders in Google drive from data in a spreadsheet. The spreadsheet gets information from a form. I have figured out how to make the new folders using scripts but have the following questions:
How do I avoid creating duplicate folders each time the script is run? For example if a folder named '1808' has already been created, I don't want the script to create '1808(1)'
I want to copy a spreadsheet from another location and insert in the new folders that I am creating. The new file name is to be created with information from the same spreadsheet.
function makeFolders(){
//Get array of folder names (fixed & variable)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var bottomRow = sheet.getMaxRows()-2;
var folderNames = sheet.getRange(2, 12, bottomRow, 1).getValues();
//If fixed name cell is blank it throws up an alert and stops
if (folderNames === "") {
SpreadsheetApp.getUi().alert('Cell A3 is blank.');
}
//If folder ID cell is blank it throws up an alert and stops
var folderUrl = sheet.getRange(3,14).getValue();
if (folderUrl === "") {
SpreadsheetApp.getUi().alert('Cell C3 is blank.');
}
//If fixed name cell and file URL aren't empty, run the program
if (folderNames !== "" && folderUrl !== "") {
//Get ID from folder URL
var folderId = folderUrl.match(/[-\w]{25,}/);
//Get master folder
var getRootFolderId = DriveApp.getFolderById(folderId);
//Copy master n times and rename each copy with the new name
//stops when it comes to a blank cell
for (n=0;n<bottomRow;n++){
if(folderNames[n] == ""){break;}
var newFolder = getRootFolderId.createFolder(folderNames[n]);
}
ss.toast("Your folders have been made! :)","Finished",3);
}
}**strong text**
How do I avoid creating duplicate folders each time the script is run?
For example if a folder named '1808' has already been created, I don't
want the script to create '1808(1)'
You could get a list of all the folder names and loop through them to check if any match the new folder name! Something like:
function MakeNewFolders(rootFolderID, folderNames)
// Get master folder
var rootFolder = DriveApp get folder (rootFolderID)
var subfolders = rootFolder.getFolders()
var alreadyExists = false;
var newFolder;
// Check if any subfolder already exists
// loop for folders first, to minimize GAS function calls
while (subfolders.hasNext()) {
var subfolderName = subfolders.next().getName();
loop (each folder in folderNames) {
if (subfolderName == newFolderName)
alreadyExists = true;
}
if (!alreadyExists)
newFolder = rootFolder.createFolder(newFolderName)
}
If there are a TON of folders you need to loop through (more than 60-ish), here is a code snippet you might find handy. Basically, the original Google example for getting a list of subfolders runs twice as slow as necessary by checking for folder.hasNext(), when you can just use a try-catch block and ignore the "you've reached the end!" exception it throws. Should cut execution time in half! :)
I want to copy a spreadsheet from another location and insert in the
new folders that I am creating. The new file name is to be created
with information from the same spreadsheet.
Try something like this!
function CopyFile(originalFileID, destinationFolderID) {
var destinationFolder = DriveApp.getFolderById(destinationFolderID) ;
var fileToCopy = DriveApp.getFileById(originalFileID)
var copiedFile = fileToCopy.makeCopy(<new name>, destinationFolder)
fileToCopy.setTrashed(true); // alternatively, you can use
// Drive.Files.remove(originalFileID);
}
Hope this helps! Been a while since I used GAS, hopefully this compiles :)
You could add a timestamp to the name of the new folder created. Each timestamp would be unique to the millesecond:
function uniqueFolderName() {
var folderName = "1808";
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyyMMddHHmmssSSS");
var newFolderName = folderName + formattedDate;
Logger.log(newFolderName);
}

Google Apps Script - copy to existing spreadsheet

I'm attempting to use Google Apps Script copy() to 'publish' my master spreadsheet to an output spreadsheet, but getting multiple copies each time instead of it replacing the output file. Can anyone suggest a way to replace the contents of a destination spreadsheet so I can keep the same file-ID for the output and manually trigger a 'publish'. Have tried copyTo, but just makes multiple sheets instead.
The master spreadsheet is a staff roster that needs to be able to be worked on by multiple managers without staff seeing the live version. When the manager has finished updating, it can be pushed to staff.
Edit: Got it working
function publishRoster() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var sheet = source.getActiveSheet();
var updatedDateTime = sheet.getRange("A1");
var now = Utilities.formatDate(new Date(), "GMT+10:30", "dd/MM/yyyy H:mm")
updatedDateTime.setValue("Last Published " + now);
var sourceName = source.getSheetName();
// var sValues = source.getDataRange().getValues();
var destination = SpreadsheetApp.openById('my-destination-sheet-id-here');
var destinationSheet = destination.getSheetByName(sourceName);
if (destinationSheet == null ) { }
else { destination.deleteSheet(destinationSheet); }
sheet.copyTo(destination).setName(sourceName);
}
copyTo() creates a copy of existing sheet in the destination spreadsheet.
If you want to publish the changes from a specific sheet to specified destination sheet then you can copy the data from source sheet to destination sheet, instead of copying the whole sheet.[which will of course create new sheets each time you copy]
So the code to copy/publish data from master sheet to slave sheet goes as follows :
var SOURCEID = 'xxxxxxxxxxxxx'; //put your source spreadsheet id here
var SOURCESHEETNAME = 'XXXXX'; //put your source sheet name here
var DESTINATIONID = 'xxxxxxxxxxxxx'; //put your destination spreadsheet id here
var DESTINATIONSHEETNAME = 'XXXXX'; //put your destination sheet name here
var data = SpreadsheetApp.openById(SOURCEID).getSheetByName(SOURCESHEETNAME).getDataRange().getValues();
SpreadsheetApp.openById(DESTINATIONID).getSheetByName(DESTINATIONSHEETNAME).clear(); //This line is to clear the existing data in destination.
SpreadsheetApp.openById(DESTINATIONID).getSheetByName(DESTINATIONSHEETNAME).getRange(1, 1, data.length; data[0].length).setValues(data);
//data.length = no. of rows in source
//data[0].length = no. of columns in source
var now = Utilities.formatDate(new Date(), "GMT+10:30", "dd/MM/yyyy H:mm");
SpreadsheetApp.openById(DESTINATIONID).getSheetByName(DESTINATIONSHEETNAME).getRange("A1").setValue("Last Published " + now);
This is not a tested code, let me know if any issues arises, I'll be happy to help you.
Thanks

automatically change the report name to the current date in Google Sheets

I need help with one small change, I have a script that will run daily in the morning and will display a report from a csv to a google sheet file and I have that working. my only problem is that for the report name I want the sheet name to be the current date of each day instead of having the way i have is just NEWDATA sheet name, I want current date added to the sheet name.
for example in this line var newsheet = ss.insertSheet("NEWDATA"); I don't want the newdata I want to grab the current date for the sheet name
example of the sheet name
function importData() {
var fSource = DriveApp.getFolderById("0ByXeCX01JfKJN1dTNk1SRlQyb1k"); // reports_folder_id = id of folder where csv reports are saved
var fi = fSource.getFilesByName('201707160600070685.csv'); // latest report file
var ss = SpreadsheetApp.openById("1T2JU4KwpJsnlJk0LOEZoHr9uqnNrVYwBWI1NxOwL4PU"); // data_sheet_id = id of spreadsheet that holds the data to be updated with new report data
if ( fi.hasNext() ) { // proceed if "report.csv" file exists in the reports folder
var file = fi.next();
var csv = file.getBlob().getDataAsString();
var csvData = CSVToArray(csv); // see below for CSVToArray function
var newsheet = ss.insertSheet("NEWDATA"); // create a 'NEWDATA' sheet to store imported data
// loop through csv data array and insert (append) as rows into 'NEWDATA' sheet
for ( var i=0, lenCsv=csvData.length; i<lenCsv; i++ ) {
newsheet.getRange(i+1, 1, 1, csvData[i].length).setValues(new Array(csvData[i]));
}
/*
** report data is now in 'NEWDATA' sheet in the spreadsheet - process it as needed,
** then delete 'NEWDATA' sheet using ss.deleteSheet(newsheet)
*/
// rename the report.csv file so it is not processed on next scheduled run
file.setName("report-"+(new Date().toString())+".csv");
}
};
How about a following modification? Current date is retrieved using new Date(). The format of date is modified using Utilities.formatDate).
From :
var newsheet = ss.insertSheet("NEWDATA");
To :
var sheetname = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "MM/dd/yyyy");
var newsheet = ss.insertSheet(sheetname);
If I misunderstand your question, I'm sorry.