How to sync a Google Groups membership with a Google Spreadsheet? - google-apps-script

G'day,
I have several Google Groups under a Google Apps account that my nonprofit uses. Some of these groups are quite large and managing their membership with the web UI is pretty awful.
I'm hoping someone can help me come up with a script that can synchronize a list that I maintain on a Google Docs Spreadsheet with a Group so that when I remove someone from the sheet, they get removed from the group -- and when someone gets added to the sheet, they get added to the group.
I was taking a look at the Directory API but I'm not savvy enough to figure out how to build a script for what I need. Side note, I did play around with a different script someone posted here that uses similar API methods, and I got that particular script to work -- so in other words, my account is now properly set up for API access.
Perhaps the easiest way to do this programmatically would be to delete the group's membership entirely, then add each member again, each time the script runs?
Thank you in advance for your assistance.

Here are several functions that you can use to accomplish what you would like.
First here is the removal process. As commented by Sandy, create a column that you will use to mark members for removal. You should be able use what ever you like here as long as the ones not being deleted are blank. You will need to change the variable "marked" to reflect that columns number, you will also need to do the same for the "email" variable. That being the column that holds the users emails.
Make a copy of your sheet first and make sure that when the rows are deleted that they are the correct ones!
/**
*Removes members from group that are marked for removal in a specific column
*/
function removeMarkedMembers() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getDataRange();
var data = range.getValues();
//Adjust these following variables to match your sheet
var groupEmail = 'group#email.com';//change to your group email address
var marked = 7;//number of column used to mark for removal ex. Column A = 1
var email = 4; //number of column that holds member email address.
//must adjust columns numbers to zero based array integers
marked = marked-1;
email = email-1;
var rows = [];
for (var d in data) {
var rowData = data[d];
if(rowData[marked] != "" || rowData != null) {
removeGroupMember(groupEmail, rowData[email]);
rows.push(new Number(d)+1)
}
}
for (var r in rows) {
sheet.deleteRow(rows[r])
}
}
function removeGroupMember(groupEmail, userEmail) {
userEmail = userEmail.trim();
AdminDirectory.Members.remove(groupEmail, userEmail);
}
Lastly here are some other functions for managing groups, use them as needed. Probably best to create a menu that you can just call these from inside the spreadsheet.
/**
*Adds all email addresses in column to group.
*/
function addAllToGroup() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var row = sheet.getLastRow();
var column = 7;
var range = sheet.getRange(2, column, sheet.getLastRow(), 1)
var emails = range.getValues();
for (var e in emails) {
var email = emails[e]
if(validateEmail(email)) {
addGroupMember(email, 'your#groupemailaddress.com');////Must replace with group email address you want them added too.
}
}
}
function addGroupMember(userEmail, groupEmail) {
var member = {
email: userEmail,
role: 'MEMBER'
};
AdminDirectory.Members.insert(member, groupEmail);
}
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
/**
*Removes all members from a defined group
*/
function removeAllMembers() {
var groupEmail = 'group#email.com';
var members = AdminDirectory.Members.list(groupEmail).members;
for (var m in members) {
var member = members[m];
var email = member.email;
removeGroupMember(groupEmail, email);
}
}
This should help you get a nice finished product.
Edited Code to handle reported errors in the comments.
Also updated the marked column must use an "x" for marker to be removed.
Lastly to properly handle the deletion of the row, you need to clear it and then sort the entire sheet or your will get wrong rows removed. Remember to adjust to your parameters.
/**
*Removes members from group that are marked for removal in a specific column
*/
function removeMarkedMembers() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange(2, 1, sheet.getLastRow()-1, sheet.getLastColumn());
var data = range.getValues();
Logger.log(data)
//Adjust these following variables to match your sheet
var groupEmail = 'test-group#email.com';//change to your group email address
var marked = 2;//number of column used to mark for removal ex. Column A = 1
var email = 1; //number of column that holds member email address.
//must adjust columns numbers to zero based array integers.
marked = marked-1;
email = email-1;
Logger.log(marked+' : '+email)
var rows = [];
for (var d in data) {
var rowData = data[d];
if(validateEmail(rowData[email]) && rowData[marked] == "x") {
Logger.log('marked')
removeGroupMember(groupEmail, rowData[email]);
rows.push(new Number(d)+2)
}
}
for (var r in rows) {
var row = rows[r];
sheet.getRange(row, 1, 1, sheet.getLastColumn()).clear();
}
range.sort(1);
}
function removeGroupMember(groupEmail, userEmail) {
Logger.log(userEmail)
userEmail = userEmail.trim();
AdminDirectory.Members.remove(groupEmail, userEmail);
}
/**
*Adds all email addresses in column to group.
*/
function addAllToGroup() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var row = sheet.getLastRow();
var column = 1;
var range = sheet.getRange(2, column, sheet.getLastRow(), 1)
var emails = range.getValues();
for (var e in emails) {
var email = emails[e]
if(validateEmail(email)) {
addGroupMember(email, 'test-group#email.com');////Must replace with group email address you want them added too.
}
}
}
function addGroupMember(userEmail, groupEmail) {
var member = {
email: userEmail,
role: 'MEMBER'
};
var members = AdminDirectory.Members.list(groupEmail).members
for (var m in members) {
if(members[m].email == userEmail) {
return 'Member already exist';
}
}
AdminDirectory.Members.insert(member, groupEmail);
}
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
/**
*Removes all members from a defined group
*/
function removeAllMembers() {
var groupEmail = 'test-group#email.com';
var members = AdminDirectory.Members.list(groupEmail).members;
for (var m in members) {
var member = members[m];
var email = member.email;
removeGroupMember(groupEmail, email);
}
}

Related

Google Apps Script: Prevent duplicate copies based on two columns

I am working with some colleagues who want the following to happen within a Google Sheet:
A Google Form contains a question that asks which Counselor a student is assigned to (among other questions)
Forms are submitted throughout the year by students
When a form is submitted, the form data goes into a Google Sheet in a Responses sheet
The Counselors would like a copy of each row to appear in another sheet within the main Sheet, based on the name of the Counselor
In their own sheets, each Counselor needs to be able to manipulate the data (sorting, highlighting rows, adding notes to the row/submission) ←hence a copy is needed instead of a query
I have the following script that copies the rows in the correct Counselor sheet, and does not copy a row into a Counselor sheet if it already appears. However, if a Counselor modifies anything in the row, the script will make a duplicate row (with the original data) the next time it is run, perhaps because it sees the modified row as not an exact match.
Is there a way to modify my script so it can check against a unique part of a row in the Responses sheet (the columns at indexes 0 and 1 together in the same row create a unique entry) in any part of a Counselor sheet before it creates a copy? In other words, it would not create a duplicate row if the Counselor modifies anything except for columns 0 and 1.
function copyData() {
var formResponses = SpreadsheetApp.getActive().getSheetByName("Form Responses 1");
var formValues = formResponses.getDataRange().getValues();
formValues.shift(); // remove the header row
formValues.forEach(function(row) {
var sheetName = row[4]; // the value of "My College Counselor is" column
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getDataRange();
var data = range.getValues();
var duplicate = false;
for (var i = 0; i < data.length; i++) {
var currentRow = data[i];
if (currentRow.join() == row.join()) {
duplicate = true;
break;
}
}
if (!duplicate) {
sheet.appendRow(row);
}
});
}
I'm stuck at this point and am not sure how to proceed.
NOTE: I have code to add a button to the menu list for the Counselors to run this script as needed since the forms can be submitted at any time. Using "onFormSubmit" does not work because there is a potential for multiple students to submit the form at the same time, which I've seen can cause a row or two to not be copied over.
If I understand your question correctly, you want to find a way to avoid duplicated rows, even if you edit them.
In order to do that, you have to define a value for each row that won't change and that is unique. My suggestion would be the following :
Installable trigger with the function custom_onFormSubmit
In the function get Uid (unique ID), and add it to each row submitted
Edit your code in order to search duplicate only with this Uid
First, add this function your Google Form Apps Script:
//add unique ID at a defined column each time a google form is submitted
function custom_onFormSubmit(e){
var uuid = e.triggerUid;
//alternatily you can use:
//var uuid = Utilities.getUuid();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName([SHEETNAME]);
var range = sheet.getDataRange();
var row = range.getLastRow();
sheet.getRange(row, 10).setValue(uuid); //column 10 is for example, adapt to your need
}
------ EDIT: alternative function without trigger onFormSubmit, add this function before
function check_insert_uuid(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName([SHEETNAME]);
var range = sheet.getDataRange();
var values = range.getValues();
for (var x = 0; x < values.length; x++) {
if (values[x][10] == "") {
let uuid = Utilities.getUuid();
range.offset(x, 10, 1, 1).setValue(uuid);
}
}
SpreadsheetApp.flush(); //force new data to sync before copyData
copyData(); //call copy function
}
------ END EDIT -------
Then you just have to edit your function copyData
FROM:
for (var i = 0; i < data.length; i++) {
var currentRow = data[i];
if (currentRow.join() == row.join()) {
duplicate = true;
break;
}
}
TO:
for (var i = 0; i < data.length; i++) {
var currentRow = data[i];
if (currentRow[10] == row[10]) { //same example of column index 10
duplicate = true;
break;
}
}
References:
Installable Triggers
Google Form Events
Apps Script getuuid (Unique ID are not 100% unique in time and space, but will certainly answer your project)
Based on the help from #waxim-corp, here is the final script that accomplishes my goal:
function onOpen(e) {
let ui = SpreadsheetApp.getUi();
ui.createMenu("🤖 Copy Data 🤖")
.addItem("Let's Do This!", 'checkForID')
.addToUi();
};
function checkForID(){
var ss = SpreadsheetApp.getActive().getSheetByName("Form Responses 1");
var range = ss.getDataRange();
var values = range.getValues();
for (var x = 0; x < values.length; x++) {
if (values[x][0] == "") {
let uuid = Utilities.getUuid();
range.offset(x, 0, 1, 1).setValue(uuid);
}
}
SpreadsheetApp.flush(); //force new data to sync before copyData
copyData(); //call copy function
}
function copyData(){
var formResponses = SpreadsheetApp.getActive().getSheetByName("Form Responses 1");
var formValues = formResponses.getDataRange().getValues();
formValues.shift(); // remove the header row
formValues.forEach(function(row) {
var sheetName = row[5]; // the value of "My College Counselor is" column
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var rangeC = sheet.getDataRange();
var data = rangeC.getValues();
var duplicate = false;
for (var i = 0; i < data.length; i++) {
var currentRow = data[i];
if (currentRow[0] == row[0]) {
duplicate = true;
break;
}
}
if (!duplicate) {
sheet.appendRow(row);
}
});
}
I'm sure it could be more efficient, but it works well.

Split a Google Sheet into multiple tabs based on column values

I used a previous answer (thanks kessy!) to split 7000 or so rows into 40 or so different tabs based upon values in a column. I ran the same script on another nearly identical file and I get the error "TypeError: Cannot read property 'getRange' of null (line 5, file "Code")". I tried with a greatly simplified file and get the same error. Any help getting this to work is very much appreciated.
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]);
}
}
}
I am not sure what is exactly your goal, but based on the error message you are getting it seems that you are not getting the active sheet properly. Instead, I would suggest you to specify the sheet by its name. Let's assume the desired name of the sheet you want to get is Sheet1. Then, in the first line of your function you can replace this:
var sheet = SpreadsheetApp.getActiveSheet();
with this:
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
I also optimized your code a little by removing all the unnecessary SpreadsheetApp.getActiveSpreadsheet() calls:
function myFunction() {
var ss = SpreadsheetApp.openById("SpreadsheetId");
var sheet = ss.getSheetByName('Sheet1');
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C"+sheet.getLastRow()).getValues();
// This var will contain all the rows
var rows = sheet.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 = ss.insertSheet("No Room");
} else {
var currentSheet = ss.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
sheet.appendRow(rows[i]);
}
}
}
You can also run a loop within the loop and keep things server side for a faster result (at least it worked for me, I was having trouble with long spreadsheets timing out).
You have to know how many columns you want to pass over, maybe there is a better way to push the values than I have done (I only dabble in script).
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
//Let's delete any sheets that were previously split, so we can rerun the script again and again
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
// This var will contain all the values from column C -> Your splitting Key
var key = theSheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = theSheet.getDataRange().getValues();
//Set the first row as the header, get the range so we can keep the formatting
var headerFormat = theSheet.getRange("2:2");
//Store the rooms already created
var completedSheets = [];
//We start at i=2 because we're on row 3, row zero for the button, row one for the header
for (var i = 2; i < key.length; i++) {
//We don't want to run the loop if we've already created the blank page and the row key is also blank.
if(completedSheets.includes('Blank') && key[i][0] === ""){
//do nothing
}else{
//Check if the room is already done, if not go in and create the sheet
if(!completedSheets.includes(key[i][0]) ) {
//Set the Sheet name = unique key (except if there is no name, then = Blank)
if (key[i][0] === "") {
var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
//To avoid pasting formulas, we have to paste contents, copying allows us to keep formatting
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
headerFormat.copyTo(currentSheet.getRange(1,1),{formatOnly:true});
//Now here find all the rows containing the same key address and push them, this way doing it server side
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
if((rows[j][2] == key[i][0]) || (rows[j][2] === '' && currentSheet.getName() == "Blank")){
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],rows[j][8]);
b++;
}
}
var outrng = currentSheet.getRange(2,1,theNewRows.length,9);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
//The new sheet name gets added to the completed sheets list and the value of var last is updated in prep of next step
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
}
}
}
}
//And return to the Master
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
Example here, just click the button on the page
https://docs.google.com/spreadsheets/d/1pfeU2CFDbZbA4O0b4z80l5MyCKDNQnUdkpKlzODbAiI/edit?usp=sharing
It's not perfect, but hope it helps.

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 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)
})
}

Google Apps Script: Move data between spreadsheets

I'm trying to use Google Apps Script to move data between two Google spreadsheets.
The spreadsheet named Ex_ID has one sheet and one row for each employee, each row has six important values including an id number and five data values.
The spreadsheet named CR_ID has a different sheet for each employee and each sheet has an id number and five cells where I need to enter the data.
I'm trying to write a script that moves data values from Ex_ID to CR_ID where the id values match. (Note: Since I'm still trying to see how the code can work, I limited the example to one of the five data values and I'm only testing it on three employees.)
function myFunction() {
var CR_ID = "1BbAmoSsS_2-nvOxv_l5gZ8ftXclD5muWs0ZAmkDUR-Y";
var ssCR = SpreadsheetApp.openById(CR_ID);
var sCR = ssCR.getSheets();
var Ex_ID = "1lkpXdWdbfe8Wkj1RGQOb-pWjEQe41hstKj2i4TxMQd8";
var ssEx = SpreadsheetApp.openById(Ex_ID);
var sEx = ssEx.getSheetByName('Sheet1');
for (var i = 2; i = 4; i++) {
//Get id value from Ex_ID
var ExCell = sEx.getRange(i, 2).getValue();
for (var j = 0; j = 2; j++) {
var sheet = sCR[j];
//Get id value from CR_ID
var val = sheet.getRange(1, 7).getValue();
if (ExCell[i] == val[j]) {
var ExWrite = sEx.getRange(i, 11).getValue();
sheet.getRange(3, 20).setValue(ExWrite);
}
}
}
}
Best,
Mayne Dela
I don't know how your data is organized and where you want to put them on the sheets but I would try something like this:
function myFunction()
{
var ess = SpreadsheetApp.openById("1BbAmoSsS_2-nvOxv_l5gZ8ftXclD5muWs0ZAmkDUR-Y");
var es = ess.getSheets();
var dss = SpreadsheetApp.openById("1lkpXdWdbfe8Wkj1RGQOb-pWjEQe41hstKj2i4TxMQd8");
var ds = dss.getSheetByName('Sheet1');
var dr = ds.getDataRange();
var dA = dr.getValues();
for(var i=1;i<dA.length;i++)//started with 1 assuming that you have a header.
{
var row=dA[i];//loop through rows of employee data I will assume data to be in this ID,DataValue1,DataValue2,DataValue3,DataValue4,DataValue5
for(var j=0;j<es.length;j++)
{
if(row[0]==es[j].getSheetName())//checking the ID value assuming it is row[0];
{
var sht=es[j];
sht.getRange('D1').setValue(row[1]);//DataValue1
sht.getRange('D2').setValue(row[2]);//DataValue2
etc...
}
}
}
}