Copying data between spreadsheets from sheets with the same name - google-apps-script

I want to copy the cell with the daily score from the sheets of all of my students in a spreadsheet where they are calculated and collected to another spreadsheet where they are used as currency to buy rewards. Both spreadsheets contain a sheet for every student that is named after that student, e.g. "John Smith"
The original google script that I created worked, but it was poor coding because I had to repeat the coding for every single name, and therefore add a new paragraph of code every time we get a new student. I would like to create a new google script that is more elegant and powerful and works without specifying the students' names so that it never needs to be amended. I can't quite get it and keep hitting a "Syntax error" with the last line.
function ImportDailyScore() {
var dailyinput = "J27"; // Mon=J3, Tue=J9, Wed=J15, Thu=J21, Fri=J27
var dollaroutput = "B2"; // Today=B2, Yesterday=B3, etc.
var dollarspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var checkinspreadsheet = SpreadsheetApp.openById('some id');
var checkinsheets = checkinspreadsheet.getSheets(); // get all the sheets from check in doc
var dollarsheets = dollarspreadsheet.getSheets(); // get all the sheets from dollar doc
for (var i=0; i<checkinsheets.length; i++){ // loop across all the checkin sheets
var checkindata = checkinsheets[i].getRange(dailyinput).getValue();
var namedcheckin = checkinsheets[i].getSheetName()
for (var j=0; j<dollarsheets.length; j++){
var nameddollar = dollarsheets[j].getSheetName();
if (namedcheckin = nameddollar, dollarsheets[j].getRange(dollaroutput).setValue(checkindata))
}
}
}
For reference, the original code (which works just as I would like it to) but needs to specify the name of every single student is:
function ImportDailyScore() {
var dollarspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var checkinspreadsheet = SpreadsheetApp.openById('1Y9Ys1jcm1xMaLSqmyl_pFnvIzbf-omSeIcaI2FgjFIs');
var dailyinput = "J3"; // Mon=J3, Tue=J9, Wed=J15, Thu=J21, Fri=J27
var dollaroutput = "B4"; // Today=B2, Yesterday=B3, etc.
var JohnCHECKIN = checkinspreadsheet.getSheetByName('John Smith');
var JohnCHECKINData = JohnCHECKIN.getRange(dailyinput).getValue();
var JohnDOLLAR = dollarspreadsheet.getSheetByName('John Smith');
JohnDOLLAR.getRange(dollaroutput).setValue(JohnCHECKINData);
var JenniferCHECKIN = checkinspreadsheet.getSheetByName('Jennifer Scott');
var JenniferCHECKINData = JenniferCHECKIN.getRange(dailyinput).getValue();
var JenniferDOLLAR = dollarspreadsheet.getSheetByName('Jennifer Scott');
JenniferDOLLAR.getRange(dollaroutput).setValue(JenniferCHECKINData);
etc.

Try this:
I don't have enough information to know how to handle the dollaroutput programmatically.
function ImportDailyScore() {
var inputA=['','J3','J9','J15','J21','J27',''];
var dailyinput = inputA[new Date().getDay()];
if(dailyinput) {
var dollaroutput = "B2"; // Today=B2, Yesterday=B3, etc. Not sure how to handle this programmatically I'd have to see the sheet
var dollarspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var checkinspreadsheet = SpreadsheetApp.openById('id');
var checkinsheets = checkinspreadsheet.getSheets(); // get all the sheets from check in doc
var dollarsheets = dollarspreadsheet.getSheets(); // get all the sheets from dollar doc
for (var i=0; i<checkinsheets.length; i++){ // loop across all the checkin sheets
var checkindata = checkinsheets[i].getRange(dailyinput).getValue();
var namedcheckin = checkinsheets[i].getSheetName();
for (var j=0; j<dollarsheets.length; j++){
var nameddollar = dollarsheets[j].getSheetName();
if (namedcheckin == nameddollar) {
dollarsheets[j].getRange(dollaroutput).setValue(checkindata);
}
}
}
}else{
SpreadsheetApp.getUi().alert(Utilities.formatString('Today is %s no school.', Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E")));
}
}

Related

How to compare values from a google sheets column to an array and send to specific emails based on column data

I'm automating my google sheets to send out emails automatically and I can't figure out how to compare a variable array to a column in my google sheets. I keep getting all values returned rather than just the ones in my array when I put it in an 'IF' statement.
I want my code to only show data that is in my gEnglish variable but instead shows me all data. I was also wondering how I'd be able to send emails based on regions to users.
function sendEmails() {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Main").activate();
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var decisionOne = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Templates").getRange(1,1).getValue();
var decisionTWO = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Templates").getRange(2,1).getValue();
var decisionThree = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Templates").getRange(3,1).getValue();
var decisionFour = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Templates").getRange(5,1).getValue();
var decisionFive = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Templates").getRange(4,1).getValue();
var gEnglish = ["IT","US","GB","UK"];
var gSpanish = ["SP"];
var user1 = ["JP","SK","IND","RU"];
var user2 = [gEnglish,"UK"];
var user3 = [gSpanish, "BR", "PT"];
var user4 = ["FR", "DE","IT"];
for (var i = 2;i<=lr;i++){
var type = ss.getRange(i,6).getValue();
var region = ss.getRange(i, 5).getValue();
var decision = ss.getRange(i,15).getValue();
//I used to have if(region == gEnglish)
if(gEnglish.includes(region[i]) ){
var channel = ss.getRange(i,3).getValue();
var decision = ss.getRange(i,15).getValue();
var url = ss.getRange(i, 9).getValue();
var name = ss.getRange(i,11).getValue();
var messageBody = {};
var subjectLine = "blahblahblah";
}
}
}
If my array only has one value inside it works fine but as soon as I put more I just get all values returned.
What am I doing wrong?
Putting a column into a flat array.
function col2array() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const vA=sh.getRange(1,1,sh.getLastRow(),1).getValues().flat();
}
In Google Apps Script a column looks like:
[[Row1],[Row2],[Row3],[Row4]....]
function lookingforarrayvaluesinacolumn() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const vA=sh.getRange(1,1,sh.getLastRow(),1).getValues();
const coolpeople = ["p1","p2"];
vA.forEach(function(r,i){
if(coolpeople.indexOf(r[0])!=-1) {
//the r[0] is a cool person
}
});
}

I need to split a Google Sheet into multiple tabs (sheets) based on column value

I have searched many possible answers but cannot seem to find one that works. I have a Google Sheet with about 1600 rows that I need to split into about 70 different tabs (with about 20-30 rows in each one) based on the value in the column titled “room”. I have been sorting and then cutting and pasting but for 70+ tabs this is very tedious.
I can use the Query function but I still need to create a new tab, paste the function and update the parameter for that particular tab.
This script seemed pretty close:
ss = SpreadsheetApp.getActiveSpreadsheet();
itemName = 0;
itemDescription = 1;
image = 2;
purchasedBy = 3;
cost = 4;
room = 5;
isSharing = 6;
masterSheetName = "Master";
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Update Purchases')
.addItem('Add All Rows To Sheets', 'addAllRowsToSheets')
.addItem('Add Current Row To Sheet', 'addRowToNewSheet')
.addToUi();
}
function addRowToNewSheet() {
var s = ss.getActiveSheet();
var cell = s.getActiveCell();
var rowId = cell.getRow();
var range = s.getRange(rowId, 1, 1, s.getLastColumn());
var values = range.getValues()[0];
var roomName = values[room];
appendDataToSheet(s, rowId, values, roomName);
}
function addAllRowsToSheets(){
var s = ss.getActiveSheet();
var dataValues = s.getRange(2, 1, s.getLastRow()-1, s.getLastColumn()).getValues();
for(var i = 0; i < dataValues.length; i++){
var values = dataValues[i];
var rowId = 2 + i;
var roomName = values[room];
try{
appendDataToSheet(s, rowId, values, roomName);
}catch(err){};
}
}
function appendDataToSheet(s, rowId, data, roomName){
if(s.getName() != masterSheetName){
throw new Error("Can only add rows from 'Master' sheet - make sure sheet name is 'Master'");
}
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
var roomSheet;
if(sheetNames.indexOf(roomName) > -1){
roomSheet = ss.getSheetByName(roomName);
var rowIdValues = roomSheet.getRange(2, 1, roomSheet.getLastRow()-1, 1).getValues();
for(var i = 0; i < rowIdValues.length; i++){
if(rowIdValues[i] == rowId){
throw new Error( data[itemName] + " from row " + rowId + " already exists in sheet " + roomName + ".");
return;
}
}
}else{
roomSheet = ss.insertSheet(roomName);
var numCols = s.getLastColumn();
roomSheet.getRange(1, 1).setValue("Row Id");
s.getRange(1, 1, 1, numCols).copyValuesToRange(roomSheet, 2, numCols+1, 1, 1);
}
var rowIdArray = [rowId];
var updatedArray = rowIdArray.concat(data);
roomSheet.appendRow(updatedArray);
}
But I always get an unexpected token error on line 51 or 52:
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
(And obviously the column names, etc. are not necessarily correct for my data, I tried changing them to match what I needed. Not sure if that was part of the issue.)
Here is a sample of my data: https://docs.google.com/spreadsheets/d/1kpD88_wEA5YFh5DMMkubsTnFHeNxRQL-njd9Mv-C_lc/edit?usp=sharing
This should return two separate tabs/sheets based on room .
I am obviously not a programmer and do not know Visual Basic or Java or anything. I just know how to google and copy things....amazingly I often get it to work.
Let me know what else you need if you can help.
Try the below code:
'splitSheetIntoTabs' will split your master sheet in to separate sheets of 30 rows each. It will copy only the content not the background colors etc.
'deleteTabsOtherThanMaster' will revert the change done by 'splitSheetIntoTabs'. This function will help to revert the changes done by splitSheetIntoTabs.
function splitSheetIntoTabs() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var header = rows[0];
var contents = rows.slice(1);
var totalRowsPerSheet = 30; // This value will change no of rows per sheet
//below we are chunking the toltal row we have into 30 rows each
var contentRowsPerSheet = contents.map( function(e,i){
return i%totalRowsPerSheet===0 ? contents.slice(i,i+totalRowsPerSheet) : null;
}).filter(function(e){ return e; });
contentRowsPerSheet.forEach(function(e){
//crate new sheet here
var currSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
//append the header
currSheet.appendRow(header);
//populate the rows
e.forEach(function(val){
currSheet.appendRow(val);
});
});
}
// use this function revert the sheets create by splitSheetIntoTabs()
function deleteTabsOtherThanMaster() {
var sheetNotToDelete ='Master';
var ss = SpreadsheetApp.getActive();
ss.getSheets().forEach(function(sheet){
if(sheet.getSheetName()!== sheetNotToDelete)
{
ss.deleteSheet(sheet);
}
});
}
I was using Kessy's nice script, but started having trouble when the data became very large, where the script timed out. I started looking for ways to reduce the amount of times the script read/wrote to the spreadsheet (rather than read/write one row at a time) and found this post https://stackoverflow.com/a/42633934
Using this principle and changing the loop in the script to have a loop within the loop helped reduce these calls. This means you can also avoid the second call to append rows (the "else"). My script is a little different to the examples, but basically ends something like:
`for (var i = 1; i < theEmails.length; i++) {
//Ignore blank Emails and sheets created
if (theEmails[i][0] !== "" && !completedSheets.includes(theEmails[i][0])) {
//Set the Sheet name = email address. Index the sheets so they appear last.
var currentSheet = theWorkbook.insertSheet(theEmails[i][0],4+i);
//append the header
//To avoid pasting formulas, we have to paste contents
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
//Now here find all the rows containing the same email address and append them
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++)
{
if(rows[j][0] == theEmails[i][0]) {
theNewRows[b]=[];//Initial new array
theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7]);
b++;
}
}var outrng = currentSheet.getRange(2,1,theNewRows.length,8); //Make the output range the same size as the output array
outrng.setValues(theNewRows);
I found a table of ~1000 rows timed out, but with the new script took 6.5 secs. It might not be very neat, as I only dabble in script, but perhaps it helps.
I have done this script that successfully gets each room and creates a new sheet with the corresponding room name and adding all the rows with the same room.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
} else {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
currentSheet.appendRow(rows[i]);
}
}
}
Please test it and don't hesitate to comment for improvements.

Google App Script - getCommenter

I'm brand new to Google Apps Script, and I'm trying to create a simple spreadsheet that will allow me to share files by user email through a single spreadsheet.
I have written the following script, which will allow me to add editors and viewers, but not commenters.
I keep getting an error that states that the function addCommenter cannot be found in object spreadsheet.
function shareSheet () {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.toast('Updating access level...');
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var range1 = sheet.getRange (3,1, lastRow,9);
var data = range1.getValues();
for (var i= 0; i < data.length; i++) {
var row = data [i];
var accessLevel = row [5];
var values = row [8];
var ss2 = SpreadsheetApp.openById(values);
var values2 = row [4];
// Add new editor
if (accessLevel == 'Edit') {
var addEditors = ss2.addEditor(values2);
}
// Add new viewer
if (accessLevel == 'View'){
var addViewers = ss2.addViewer(values2);
}
// Add new commenter
if (accessLevel == 'Comment') {
var addCommenters = ss2.addCommenter(values2);
}
}
}
The Spreadsheet object does not support the addCommentor() method. You should use DriveApp service instead.
DriveApp.getFileById(id).addCommenter(emailAddress)

Google Apps Script to list all Google Classroom courses and put them in a Google Spreadsheet

I am trying to create a Google Apps Script that will list all the Google Classroom courses that are active and archived, along with the ID, NAME, SECTION, and COURSESTATE. Everything works except that I have no idea how to fix the .getRange so that it will put all the information in the Google Spreadsheet. The error I get is "Incorrect range height, was 4 but should be 10". If I put the .getRange as simply "A1:D1", it will overwrite what is there for each Class until the script finishes, so I know the rest of the script works, but I can't figure out how to put the range so that all the Classes can be listed. What I have up to now is this:
function listCourses() {
var response = Classroom.Courses.list();
var courses = response.courses;
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
var ids = course.id;
var title = course.name;
var sec = course.section;
var state = course.courseState;
var arr1 = [];
arr1.push(ids,title,sec,state);
var arr2 = [];
while(arr1.length) arr2.push(arr1.splice(0,1));
var s = SpreadsheetApp.getActiveSpreadsheet();
var sh = s.getSheetByName('LISTS');
for (var x=0; x<arr2.length; x++){
var newRow = sh.getLastRow() + 1;
// Here in the .getRange is where it gives me the error. A new row should be added for each Class until all the Classes (with the information mentioned above) are listed.
sh.getRange(1, 1, newRow, arr2[0].length).setValues(arr2);
}
}}}
Try something like this:
I don't use Classroom API much and I only have one class in it but this works for it.
function listCourses() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('LISTS');
var response = Classroom.Courses.list();
var courses = response.courses;
var arr=[];//You could put column headers in here
for (i = 0; i < courses.length; i++) {
var course = courses[i];
var ids = course.id;
var title = course.name;
var sec = course.section;
var state = course.courseState;
arr.push([title,sec,state]); //you could also go sh.appendRow([title,sec,state]); here if you wish and avoid the use of the two dimensional array all together as suggested in the other answer.
}
sh.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}
Jason. You could bypass the range issue by using append row. This will however mean manipulating the data so that each class's details is on one row. I prefer this approach because it is let prone to errors. Also it automatically finds the next empty row in the sheet, so you don't need to get the last row.
function appendRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
// Try is added to catch failures on automated scripts
try {
sheet.appendRow(['cell1','cell2','cell3'])
} catch (e){
Logger.log(e)
// Could send an email here to alert script failure
}
// In a loop
var data = [['row 1 cell 1','row 1 cell 2'],['row 2 cell 1','row 2 cell 2 ']]
data.forEach(function(item){
sheet.appendRow(item)
})
}

Copy rows from one sheet to another

I need to create a master sheet for our managers that only include activity happening in their areas (I have a script for this that does what I need). These sheets are populated from my master sheet (and are not in the same workbook).
I need to have rows copy to the applicable sheet when there is a new submission. My code below dumps them all to one sheet and I'm not sure why. I'm assuming I don't have the right condition but how do you say "move rows to this sheet when it's this manager"? I thought calling the sheet ID by row would be enough, but I guess not.
Would I then have to do an if else statement for every manager? That will get tedious since I have about 80 managers.
Below is my current code. Any help or advice is appreciated!
function moveRows(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Form Responses 1");
var data = sh.getDataRange().getValues();
// Grab the Headers
var headers = sh.getRange(1,1,1,sh.getLastColumn()).getValues();
var nameCol = headers[0].indexOf('Enter Your Name');
var candCol = headers[0].indexOf('Choose Candidate');
var typeCol = headers[0].indexOf('Merged Experience');
var docCol = headers[0].indexOf('Document Link');
var timeCol = headers[0].indexOf('Timestamp');
var subjectCol = headers[0].indexOf('Choose the Content Area');
var ratingsCol = headers[0].indexOf('Ratings Summary');
var accessCol = headers[0].indexOf('Access');
var activityCol = headers[0].indexOf('Copied to Activity Sheet');
var masterCol = headers[0].indexOf('Master Activity Sheet');
var target = [];
// Loop over the values line by line
for (var i = 0; i < data.length; i++) {
var row = data[i];
var name = row[nameCol];
var activity = row[activityCol];
var master = row[masterCol];
var candidate = row[candCol];
var type = row[typeCol];
var subject = row[subjectCol];
var ratings = row[ratingsCol];
var access = row[accessCol];
var guide = row[docCol];
var time = row[timeCol];
if (activity == "") { // if condition is true copy the whole row to target
target.push([time,name,candidate,subject,type,guide,ratings]);
Logger.log(target);
var ss2 = SpreadsheetApp.openById(master);
var sh2 = ss2.getSheetByName("Sheet1");
sh2.getDataRange().offset(sh2.getLastRow(), 0, target.length, target[0].length).setValues(target);
}
}
}
Note: I have a bunch of different scripts that move rows from one tab to another, but I couldn't find anything where I move it across multiple workbooks.
What you can do is to have an array with for each manager the sheet where to copy the data.
var managerSheet = {
"manager1":"SheetAzerty",
"manager2":"SheetQwerty"
};
to select the sheet just do :
var manager = "manager1"; //This is the value you retrieve
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(managerSheet[manager]);
Stéphane