How to parse JSON provided by GitHub API to Google Sheets? - google-apps-script

I'm doing multiple queries (find amount of open/closed issues, use the open/closed issues (per person) to create a leaderboard of developer issue closes, etc - find how many issues were closed this week and how many opened last week - what's the newest issue, what's the oldest issue) to GitHub's API and trying to get the resultant data written onto a Google Sheet. I'm using Google Apps Script Editor to do this.
I can get the JSON data from GitHub API to the console no problem, but I'm not sure how to then parse and write it to Google Sheets. How could I do this, please?
An example of a JSON data (this time it was "open issues" grabbed from http://github.com/vizorvr/patches/ )
[17-08-08 13:47:46:353 EEST] {"total_count":271,"incomplete_results":false,"items":[{"url":"https://api.github.com/repos/vizorvr/patches/issues/2035","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/events","html_url":"https://github.com/vizorvr/patches/issues/2035","id":245953104,"number":2035,"title":"Figure out which Three.JS plugins are missing from Patches and add them","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-07-27T07:41:52Z","updated_at":"2017-07-27T07:41:52Z","closed_at":null,"body":"Go to http://threejs.org and compare the list of ThreeJS plugins in Patches and what is available on the ThreeJS site. Then implement them.","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2034","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/events","html_url":"https://github.com/vizorvr/patches/issues/2034","id":245952930,"number":2034,"title":"Inspect the Radial Point Geometry -plugin and figure out what it does or should do","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-07-27T07:41:07Z","updated_at":"2017-07-27T07:41:07Z","closed_at":null,"body":"Go into Patches and add the Radial Point Geometry -patch.\r\n\r\nStudy what it does. Figure if it does anything. If it does something, come up with an example explaining how it should be used - then help with the popup documentation.\r\n\r\n![vizor](https://user-images.githubusercontent.com/4966687/28659400-fb6f7a5c-72b7-11e7-83c9-6da26fe5ce13.png)\r\n","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2023","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/events","html_url":"https://github.com/vizorvr/patches/issues/2023","id":241289959,"number":2023,"title":"Shortcut: Shift-Tab: improve by opening \"Inspector->Properties\"","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[{"id":165125694,"url":"https://api.github.com/repos/vizorvr/patches/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":346495796,"url":"https://api.github.com/repos/vizorvr/patches/labels/user","name":"user","color":"fbca04","default":false},{"id":270777736,"url":"https://api.github.com/repos/vizorvr/patches/labels/UX","name":"UX","color":"5319e7","default":false}],"state":"open","locked":false,"assignee":{"login":"gmarinov","id":14101296,"avatar_url":"https://avatars0.githubusercontent.com/u/14101296?v=4","gravatar_id":"","url":"https://api.github.com/users/gmarinov","html_url":"https://github.com/gmarinov","followers_url":"https://api.github.com/users/gmarinov/followers","following_url":"https://api.github.com/users/gmarinov/following{/other_user}","gists_url":"https://api.github.com/users/gmarinov/gists{/gist_id}","starred_url":"https://api.github.com/users/gmarinov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gmarinov/subscriptions","organizations_url":"https://api.github.com/users/gmarinov/orgs","repos_url":"https://api.github.com/users/gmarinov/repos","events_url":"https://api.github.com/users/gmarinov/events{/privacy}","received_events_url":"https://api.github.com/users/gmarinov/received_events","type":"User","site_admin":false},"assignees":[{"login":"gmarinov","id":14101296,"avatar_url":"https://avatars0.githubusercontent.com/u/14101296?v=4","gravatar_id":"","url":"https://api.github.com/users/gmarinov","html_url":"https://github.com/gmarinov","followers_url":"https://api.github.com/users/gmarinov/followers","following_url":"https://api.github.com/users/gmarinov/following{/other_user}","gists_url":"https://api.github.com/users/gmarinov/gists{/gist_id}","starred_url":"https://api.github.com/users/gmarinov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gmarinov/subscriptions","organizations_url":"https://api.github.com/users/gmarinov/orgs","repos_url":"https://api.github.com/users/gmarinov/repos","events_url":"https://api.github.com/users/gmarinov/events{/privacy}","received_events_url":"https://api.github.com/users/gmarinov/received_events","type":"User","site_admin":false}],"milestone":null,"comments":0,"created_at":"2017-07-07T14:57:29Z","updated_at":"2017-07-07T15:44:19Z","closed_at":null,"body":"Improve Shift-Tab so that it opens the Inspector ( press I ) AND selects Properties-tab instead of Node.\r\n\r\n","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2022","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/events","html_url":"https://github.com/vizorvr/patches/issues/2022","id":24086639
I have, thus far, grabbed something called pullJSON to try and pull the data, which looks like this - however, unfortunately it seems destructive - it does not write to a specific sheet, just any old active sheet. I'm not sure how to modify it to write it to a specific sheet so that it'll work, so that's why I'm asking about how to parse stuff.
function pullJSON() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = ss.getActiveSheet();
var url="https://api.github.com/search/issues?q=repo:vizorvr/patches+state:closed"; // Paste your JSON URL here
var response = UrlFetchApp.fetch(url); // get feed
var dataAll = JSON.parse(response.getContentText()); //
var dataSet = dataAll;
var rows = [],
data;
for (i = 0; i < dataSet.length; i++) {
data = dataSet[i];
rows.push([data.id, data.name,data.email]); //your JSON entities here
}
dataRange = sheet.getRange(1, 1, rows.length, 3); // 3 Denotes total number of entites
dataRange.setValues(rows);
}
Edit 2
Was able to get the number of open issues in the repo, and the number of closed issues in the repo, but to dump them to a specific cell is still very vague for me.
var openPatches = "https://api.github.com/search/issues?q=repo:vizorvr/patches+state:open"
var closedPatches = "https://api.github.com/search/issues?q=repo:vizorvr/patches+state:closed"
var openResponse = UrlFetchApp.fetch(openPatches, {'muteHttpExceptions': true});
var Data = JSON.parse(openResponse.getContentText());
Logger.log("Open issues on Patches GitHub:")
Logger.log(Data.total_count)
var closedresponse = UrlFetchApp.fetch(closedPatches, {'muteHttpExceptions': true});
var data = JSON.parse(closedresponse.getContentText());
Logger.log("Closed issues on Patches GitHub:")
Logger.log(data.total_count)
Logger.log(Object.keys(data))
Could I in theory just write Data.total_count and data.total_count to different cells on a sheet, without having to have a google sheet open in another tab on the browser?

Based on your update, to write the data in to a specific sheet you need to use getSheetByName to write in to a specific sheet
var url ="<<SPREADSHEET URL>>";
var ss= SpreadsheetApp.openByUrl(url)
var sheet = ss.getSheetByName("<<SHEET NAME>>");
To store the values in different cell, you can define the range and set the values accordingly.
sheet.getRange("A1").setValue(Data.total_count); //Open issues
sheet.getRange("B1").setValue(data.total_count); //Closed issues

Related

Google sheets appscript to copy tabs to new sheets

I have a google sheet with around 190 tabs on that i need to split into 190 different files
The files need to be named the same as the tab, the contents of the tab need to be copied as values but i also need to bring the formatting accross (just not the formulas).
I have looked around, and through a combination of previous questions and answers plus using the function list help have formed the following code. It actually works for the first few tabs but then throws up an error about being unable to delete the only sheet.
function copySheetsToSS() {
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){
var sheet = ss.getSheets()[n];// look at every sheet in spreadsheet
var name = sheet.getName();//get name
if(name != 'master' && name != 'test'){ // exclude some names
var alreadyExist = DriveApp.getFilesByName(name);// check if already there
while(alreadyExist.hasNext()){
alreadyExist.next().setTrashed(true);// delete all files with this name
}
var copy = SpreadsheetApp.create(name);// create the copy
sheet.copyTo(copy);
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
copy.getSheets()[0].setName(name);// rename first sheet to same name as SS
var target_sheet = copy.getSheetByName(name);
var source_range = sheet.getRange("A1:M50");
var target_range = target_sheet.getRange("A1:M50");
var values = source_range.getValues();
target_range.setValues(values);
}
}
}
I am hoping someone can tell me what i have done wrong as I cannot figure it out at this point. I am also open to better solutions though please be aware I am very much a beginner on google appscript, nothing too complex please.
thankyou
In principle your script correctly adds a new sheet to the new spreadsheet before removing the preexisting one
However, mind that calls to service such as SpreadsheetApp are asynchronous.
And this becomes the more noticeable, the longer your script runs.
In your case it apparently leads to behavior that the only sheet is being deleted before the new sheet is being created.
To avoid this, you can force the execution to be synchronous by implementing calls to SpreadsheetApp.flush().
This will ensure that the old sheet won't be deleted before the new one gets inserted.
Sample:
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
SpreadsheetApp.flush();
copy.getSheets()[0].setName(name);
You might want to introduce call toflush()` also at other positions where it is important for the code to run synchronously.

Inserting variable data into an existing Google Sheet in Google Scripts

I'm building a script that will ultimately take data from a csv file, populate a spreadsheet, use that spreadsheet to autofill a number of documents, and then automatically e-mail those documents to customers. It's also moving the documents from each time the script runs to a new folder with just that day's reports. I'm pretty new to Google Scripts, and this has been my learning project.
The steps I've got to work so far is the creation of documents for the spreadsheet with dummy data.
I ended up creating a second script to send e-mails which sends e-mails, but it is set up to look for all documents in the folder that are google docs, so it send a copy of every document to each customer. I thought that if I pointed the e-mail back to the original spreadsheet to grab just the correct document IDs (instead of the type) , I could only send customers the reports that belonged to them (all in PDF form). The step of creating a new folder and moving the documents into it afterwards works fine.
What I'm having an issue with is inputting data into my document ID column in the original spreadsheet. I have been able to watch it put the document ID of the first document into every row that has info to iterate over in the column, and then replace every row again with the second document's ID, etc.
I looked for ways to add data to a spreadsheet. Every method I've found so far involves creating a new column or row with new information from data within the spreadsheet. I need to put in data that I'm just now creating outside of the spreadsheet and then put it in the right place so I can point to it later.
I've gone over the methods within the documentation. It looks like .getCell.setvalue(variable) should work...if I could find a way to get the cell from the range (Which keeps showing me out of range).
function createDocument() {
var headers = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A1:P1');
var tactics = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A2:P');
var templateId = '1DU13OJHWyYnO5mLFovm97pWwXuU7ZTTDVJb2Mpdeebk';
for(var i = 0; i < tactics.values.length; i++){
var customer = tactics.values[i][0];
var pcname = tactics.values[i][1];
var date = tactics.values[i][2];
var virusvalue = tactics.values[i][3];
var cpuuse = tactics.values[i][4];
var ramuse = tactics.values[i][5];
var harddrive = tactics.values[i][6];
var netuse = tactics.values[i][7];
var downtime = tactics.values[i][8];
var cpuperform = tactics.values[i][9];
var ramperform = tactics.values[i][10];
var harddiskperform = tactics.values[i][11];
var reccomend = tactics.values[i][13];
var custID = tactics.values[i][14];
var newdoc = tactics.values[i][15];
//Make a copy of the template file
var documentID = DriveApp.getFileById(templateId).makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
//rename the copied file
DriveApp.getFileById(documentID).setName('MCHA ' + customer + ' - ' + pcname);
Logger.log('value1 ' + documentID);
//THIS IS THE AREA I'M TRYING TO FIX
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
var cell = ss.getRange('P2:P').getCell([i], [15]);
cell.setValue(documentID);
SpreadsheetApp.flush();
Logger.log('value2 ' + documentID);
//This area has code to replace the tags in the document with values from the spreadsheet. I cut it for not being relevant.
}
}
Obviously defining the range is just going to fill and autofill each cell. That code worked when I tried it
I originally tried using the variable from my earlier list for the autofill, but I've since realized that shouldn't work at all.
I tried to set the range of cells and then set the current cell by the same notation ([i][15] etc.) which throws an 'Range not found' error. I feel like I must be missing some syntax.
A link to the folder with all the documents is below, if that helps.
https://drive.google.com/drive/folders/1sRhti3R8R-Cym0hr2S4XkAVn3wyBbSRu?usp=sharing
I may not have entirely understood the problem you are facing. But I can see the cause of the 'Range not found' error.
Your loop starts with a value of 0 for i. This causes the script to look for a range called P0 in the first iteration.
One way to avoid that is to try :
var cell = ss.getRange("P"+(i+1));
For code efficiency, I'd also suggest moving some of the code outside the loop. For example, the following line runs each time in the loop. But it could be defined once outside the loop and then the variable ss could be reused inside the loop.
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
Similarly, you could define the template file outside the loop and then sue it inside to make copies:
var templateFile = DriveApp.getFileById(templateId); // Outside the loop
And then inside the loop:
var documentID = templateFile.makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
Google apps script best practices suggests minimising calls to the spreadsheet, i.e. get the data in one call, process it, and then post the data to the sheet in one call. More here.
Hope this helps.

Is it even possible to copy data from one sheet to another with a script? ImportRange style?

Ive searched for 2 days, can't seem to find the answer or anyone that is even doing something kinda like this. I am trying to bring my employees sheets over to my "Master sheet" with a script. I need to keep the formatting and the notes in the cells.
Best I can find is I can open their spread sheets and copy it to my master, but for 8 employees this will be a pain if I need to do this multiple times a day. I would like to just build a function that I can run my script from the "master sheet" and have it write over the last data in their tab. But I want to be able to do this from within my master sheet. I am the owner of all the sheets but this is becoming a pain.
function copyCell() {
var os = SpreadsheetApp.openById('1n4iFXGuC7yG1XC-UIbuT9VrQ7rJWngPkDCv0vsvDed4');
var sheets = os.getSheets();
var existingNote;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var jobnumber = sheets.getRange("C2:C").getValues();
for (var i = 0; i < jobnumber.length; i++)
if (jobnumber[i][0] == sheets){
existingNote = sheets.getRange("C" + (i+2)).getNote();
sheets.getRange("C" + (i+2)).ss.setNote(existingNote);
}
};
Someone please let me know if it is possible and maybe reference a script I can see to be able to modify to fit my needs. Thanks for any help.
This is a possible solution for copying the notes associated with a range of cells. And pasting them onto a similar-sized range of cells.
To copy the notes you can use getNotes(). Docs here.
To paste the notes you can use setNotes(). Docs here.
Ths sample code in this documentation is pretty good if you need to add it into your script.
If you have a script you'd like to share am happy to help you make it work properly.
Edit
Something like this:
function transferNotes() {
// Get notes (not comments) from the source spreadsheet
var sourceSpreadheet = SpreadsheetApp.openById('ID_of_source_spreadsheet');
var sourceTab = sourceSpreadheet.getSheetByName('Name_source_tab');
var sourceRange = sourceTab.getRange('Source_range_address'); // e.g. "A:D"
var notes = sourceRange.getNotes();
// Post the notes to the target spreadsheet
var targetSpreadheet = SpreadsheetApp.openById('ID_of_target_spreadsheet');
var targetTab = targetSpreadheet.getSheetByName('Name_target_tab');
var targetRange = targetTab.getRange('Target_range_address'); // Should be the same dimensions as sourceRange
targetRange.setNotes(notes);
}

Efficient Way of sending Spreadsheet over email using GAS function?

I am creating an addon for Google Sheets that my local High School's volunteer clubs can use to keep track of their member's volunteer hours. Most of the code is done and works very nicely, and I am currently working on a system that will send a member a spreadsheet listing all of the volunteer events that they have logged. I have GAS create a separate spreadsheet, and then send an email with that separate spreadsheet attached in PDF. When the email is received, the PDF is empty except for a singular empty cell at the top left of the page.
I am pretty new to GAS but have been able to grasp the content pretty easily. I have only tried one method of sending the Spreadsheet and that is by using the .getAs(MimeType.PDF). When I changed the "PDF" to "GOOGLE_SHEETS," GAS returned the error: "Blob object must have non-null data for this operation." I am not entirely sure what a Blob object is, and have not found any website or video that has fully explained it, so I am not sure how to go about troubleshooting that error.
I think I'm having a problem grabbing the file because it either sends an empty PDF or it returns an error claiming it needs "non-null data."
function TigerMail()
{
var Drive = DriveApp;
var app = SpreadsheetApp;
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var Name = LOOKUP.getRange("E1").getValue();
Name = Name + "'s Hours";
//app.openById(Name+"'s Hours");
var HOURS = app.create(Name);
var ESheet = HOURS.getSheets()[0];
var ROW = LOOKUP.getLastRow();
var arr = LOOKUP.getRange("D1:J"+ROW).getValues();
var cell = ESheet.getRange("A1:G"+ROW);
cell.setValues(arr);
////////////////////////////////////////////////////
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var cell = LOOKUP.getRange("D1");
var Addr = cell.getValue();
var ROW = LOOKUP.getLastRow();
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
TigerMail.sendEmail(Addr, "Hours", "Attached is a list of all of the
events you have volunteered at:", {attachments: [FORMAT]} );
}
the final four lines are where the errors are occurring at. I believe I am misunderstanding how the .next() and .getFilesByName() work.
(above the comment line: creating a spreadsheet of hours)
(below the comment line: grabbing the spreadsheet and attaching it to an email)
Here is the link to the Google Sheet:
https://docs.google.com/spreadsheets/d/1qlUfTWaj-VyBD2M45F63BtHaqF0UOVkwi04XwZFJ4vg/edit?usp=sharing
In your script, new Spreadsheet is created and put values.
You want to sent an email by attaching the file which was converted from the created Spreadsheet to PDF format.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
About Drive.getFilesByName(Name), unfortunately, there is no method of getFilesByName() in Drive.
I think that when you want to use the created Spreadsheet, HOURS of var HOURS = app.create(Name) can be used.
About var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS), in the case of Google Docs, when the blob is retrieved, the blob is automatically converted to PDF format. This can be also used for your situation.
In order to save the values put to the created Spreadsheet, it uses SpreadsheetApp.flush().
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify as follows.
From:
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
To:
SpreadsheetApp.flush();
var FORMAT = HOURS.getBlob();
Note:
In your script, it seems that var ROW = LOOKUP.getLastRow() is not used.
References:
flush()
getBlob()
If I misunderstood your question and this was not the result you want, I apologize.

Share Google Doc using a Google Script to set an array of emails (in Google Sheets) as editors

I basically am imagining commands that would change the sharing status of sheets it was allowed to edit. For example, I have a master document for a company (a "Roster" for keeping track of where employees are working and which other employees they are working with in that area) and other "Area Sheets" the employees use to keep track of progress. People move around quarterly, so I want to be able to update my Roster and then let the programming update the sharing status of their sheets instead of clicking on every sheet and copy-pasting emails.
As far as I know there is no current possible way to use google products to achieve this. Are there any other ways, barring a New Feature developed by Google?
I don't think I fully understand your requirements, so I'll start be rephrasing what I do understand.
For every employee, generate a list of who they're working with that quarter.
For a certain set of files owned by the employee, make those files editable to the employee's current co-workers.
For point 1, you already have the list built out in Google Sheets, so you can read the contents into arrays for processing. You can use Google Apps Script Spreadsheet Service for this. Example:
var roster = SpreadsheetApp.getActive(); //assuming the script is running within the Roster spreadsheet
var employeesSheet = roster.getSheetByName("Employees"); //assuming sheet name is "Employees"
var employeeEmails = employeesSheet.getRange("B2:B").getValues(); //assuming the emails are in column B with the first row as header
//In this next line I'm going to make a very big simplification.
//I will assume that the coworkers emails are listed as "coworker1#company.com;coworker2#company.com" in the cell.
//This is likely not the case, but you may already have this formatted in a way that works.
//If not, you can merge these values in Google Apps Script, but I'll consider that outside the scope of this question.
var coworkerEmails = employeesSheet.getRange("C2:C").getValues();
For point 2, I assume you already have a way of determining which files need to have their access permissions modified and that you have permission to make those modifications. You'll need to use the Drive Service for this part. In my example, I will assume that all relevant files are saved in a folder specific to each employee and that the folder ID is saved in column D.
var folderIds = employeesSheet.getRange("D2:D").getValues();
for (var i=0; i<employeeEmails.length; i++) {
var folder = DriveApp.getFolderById(folderIds[i][0]);
//At this point, you could give direct access to the entire folder,
//or just to the files within the folder, as I will do.
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
file.addEditors(coworkerEmails[i][0].split(";"));
}
}
Please note that in the above:
This is untested code and meant only to demonstrate how to achieve what I think you're looking for.
No access permissions are being revoked. This would be quite trivial to add, though.
The folder.getFiles() command only gets files that are children of the folder. So, if the folder has child folders, you'll need to traverse through those as well.
You may want to create a custom menu in your Roster spreadsheet to run this script
In the end, including the custom menu, your code could like:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name: "Reset Permissions", functionName: "resetPermissions"});
ss.addMenu("Roster Actions", menuEntries);
}
function resetPermissions() {
var roster = SpreadsheetApp.getActive();
var employeesSheet = roster.getSheetByName("Employees");
var employeeEmails = employeesSheet.getRange("B2:B").getValues();
var coworkerEmails = employeesSheet.getRange("C2:C").getValues();
var folderIds = employeesSheet.getRange("D2:D").getValues();
for (var i=0; i<employeeEmails.length; i++) {
var folder = DriveApp.getFolderById(folderIds[i][0]);
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
file.addEditors(coworkerEmails[i][0].split(";"));
}
}
}
For reference, this is how I assumed the example "Employees" sheet in the "Roster" spreadsheet to look: