Google Apps Script: Display single column based on user email - google-apps-script

I am trying to develop a gradebook with Google Spreadhseets, but I only want to display individual grades based on user email. Is there a Google Apps Script function that looks at a logged in user's email, searches the spreadhseet for it (it would be at the top of a column) and then displays that column back to the user?

I think the best solution would probably be to create a small webapp Ui that shows only the relevant column in a html table. Depending on your programming skills it can be quite easy to setup. I could have suggested an example but you gave too few information about the type of data that are in the spreadsheet... User's data are in a single column but I suppose there is also some kind of headers or row identifiers that must be shown to understand what is shown ?
Users will have to authorize the webapp in order to let you identify them when they are logged in.
Edit : here is a simple example of a possible solution that you can start from.
I assumed mails are in row 1, descriptions in column A.
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheet/ccc?key=0AnqSFd3iikE3dGdQOXZpRmJnT0xjQklkdEZING5YREE#gid=0');
var sh1 = ss.getSheetByName('sheet1');
var logsheet = ss.getSheetByName('logsheet');
var data = sh1.getDataRange().getValues();
var user = Session.getEffectiveUser()
function doGet() {
var app = UiApp.createApplication();
if(!getcol(user)){
var warn = app.createTextBox().setWidth('500').setValue("Your results are not available or you don't have permission to view these data");// if user is not in the list, warning + return
app.add(warn)
return app
}
var grid = app.createGrid(data.length, 2).setBorderWidth(1).setCellPadding(2);
var text = app.createTextBox().setName('text').setId('text').setValue(user+' | idx'+getcol(user) ).setWidth('300px');
var btn = app.createButton('press here to confirm you have view these results');
var handler = app.createServerHandler('log').addCallbackElement(grid);
btn.addClickHandler(handler);
var col = getcol(user)
grid.setWidget(0,1,text).setText(0, 0, 'Results for');
grid.setStyleAttribute('textAlign','center')
for(n=1;n<data.length;++n){
grid.setText(n, 0, data[n][0])
grid.setText(n, 1, data[n][col])
}
app.add(grid).add(btn)
return app
}
function log(e){
var text = e.parameter.text
var app = UiApp.getActiveApplication();
// add user name to the log sheet + timestamp + eventually send a mail etc...
return app
}
function getcol(mail){
if(data[0].toString().indexOf(mail.toString())!=-1){
for(zz=1;zz<data[0].length;++zz){
if(data[0][zz] == mail){var colindex=zz;break}
}
return colindex
}
return false
}
EDIT2 :
To print numbers with the desired number of decimals you can use the recently implemented printf function like this :
grid.setText(n, 1, Utilities.formatString('%.2f',data[n][col]));// 2 decimals, no leading 0
to show dates like you want use formatDate()
Utilities.formatDate(date, Session.getTimeZone(), "MM-dd")
EDIT3 :
here is a version that handles the different types of data :
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheet/ccc?key=0AnqSFd3iikE3dGdQOXZpRmJnT0xjQklkdEZING5YREE#gid=0');
var sh1 = ss.getSheetByName('sheet1');
var logsheet = ss.getSheetByName('logsheet');
var data = sh1.getDataRange().getValues();
var user = Session.getEffectiveUser()
Logger.log(user)
function doGet() {
var app = UiApp.createApplication();
if(!getcol(user)){
var warn = app.createTextBox().setWidth('500').setValue("Your results are not available or you don't have permission to view these data");// if user is not in the list, warning + return
app.add(warn)
return app
}
var grid = app.createGrid(data.length, 2).setBorderWidth(1).setCellPadding(2).setStyleAttribute('background','#ffeeaa').setId('grid');
var text = app.createTextBox().setName('text').setId('text').setValue(user+' | idx'+getcol(user) ).setWidth('300px');
var btn = app.createButton('press here to confirm you have view these results');
var handler = app.createServerHandler('log').addCallbackElement(grid);
btn.addClickHandler(handler);
var col = getcol(user)
grid.setWidget(0,1,text).setText(0, 0, 'Results for');
grid.setStyleAttribute('textAlign','center')
for(n=1;n<data.length;++n){
grid.setText(n, 0, string(data[n][0]));
grid.setText(n, 1, string(data[n][col]));
}
grid.setStyleAttributes(n-1, 0, {'fontWeight':'bold','background':'#ffff99'})
grid.setStyleAttributes(n-1, 1, {'fontWeight':'bold','background':'#ffff99'})
app.add(grid).add(btn)
return app
}
function log(e){
var text = e.parameter.text
var app = UiApp.getActiveApplication();
app.getElementById('grid').setStyleAttribute('background','#bbffbb')
// add user name to the log sheet + timestamp + eventually send a mail etc...
return app
}
function string(value){
Logger.log(typeof(value))
if (typeof(value)=='string'){return value};// if string then don't do anything
if (typeof(value)=='number'){return Utilities.formatString('%.1f / 20',value)};// if number ther format with 1 decimal
if (typeof(value)=='object'){return Utilities.formatDate(value, Session.getTimeZone(), "MM-dd")};//object >> date in this case, format month/day
return 'error'
}
function getcol(mail){
if(data[0].toString().indexOf(mail.toString())!=-1){
for(zz=1;zz<data[0].length;++zz){
if(data[0][zz] == mail){var colindex=zz;break}
}
return colindex
}
return false
}

Related

Google Scripts - View Log stuck at "Waiting for logs, please wait..."

I'm trying to run a script for my Google Sheet on Scripts, and a function isn't working properly. I have some loggers in place to check why this is happening, but anytime I try to open the Logs tab, I get this:
... and it's just stuck there forever.
Has anyone ever had this problem? Any potential fixes? Thanks
EDIT: My executions window looks like so:
EDIT 2: Here is the code I'm trying to run, with segment = 1. SPREADSHEETS is just a variable that I'm unfortunately not able to share, but it just contains some import segment information that directs to either 1 or 2.
function CopyPasteAllSheets(segment) {
for (x in SPREADSHEETS) {
if (SPREADSHEETS[x].IMPORTSEGMENT != segment) {
// DRR added app which is redundant to intakeSpreadhseet, but keeps logic more readable
app.toast('running loop')
console.log("ID: " + SPREADSHEETS[x].SOURCE.ID + "NO MATCH");
} else {
// Logger.log("x: "+ x) // keep commented out
var intakeSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].INTAKE.ID);
var intakeSheet = intakeSpreadsheet.getSheetByName(SPREADSHEETS[x].INTAKE.SHEET); //confirm formatting conventions
// This is functionally equivlent to the above, except we don't have a reference to intakeSpreadsheet anymore
// Access the Spreadsheet and sheet you want to copy the data TO
console.log("ID: "+ SPREADSHEETS[x].SOURCE.ID)
var sourceSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].SOURCE.ID);
var sourceSheet = sourceSpreadsheet.getSheetByName(SPREADSHEETS[x].SOURCE.SHEET);
var sourceStartRow = SPREADSHEETS[x].SOURCE.STARTROW;
var sourceStartCol = SPREADSHEETS[x].SOURCE.STARTCOL;
var sourceRangeCol = SPREADSHEETS[x].SOURCE.ENDCOL - SPREADSHEETS[x].SOURCE.STARTCOL + 1;
// Get the range of the data you want and the range where you want the data to go
var rowsToCopy = sourceSheet.getLastRow()-sourceStartRow+1; // is +1 too conservative, check...
var rangeToCopy = sourceSheet.getRange(sourceStartRow,sourceStartCol,rowsToCopy, sourceRangeCol);
var dataToCopy = rangeToCopy.getValues();
var numRows = rowsToCopy;
var numColumns = sourceRangeCol;
var intakeStartRow = SPREADSHEETS[x].INTAKE.STARTROW;
var intakeStartCol = SPREADSHEETS[x].INTAKE.STARTCOL;
var rangeToPaste = intakeSheet.getRange(intakeStartRow,intakeStartCol, numRows,numColumns); // WAS FORMERLY 1,20, ..,.. ~DRR 7/14
rangeToPaste.setValues(dataToCopy);
}
}
}

How do i get event.getEventById('id') or event.getEventSeriesById('id') to return anything other than null

Is there a way to make the .getEventById or .getEventSereiesById return anyhting other than null? I get valid ID for the initial event creation and can make that into a full functional URL but cannot use it in its native environment for its native purpose.
I am trying to make a basic google sheets schedule system that can refer to the calendar invite to check for changes and update the sheet or vise versa based on which side is further out in time. The system will be used in an environment where the scheduling has multiple users and meetings can be moved around a lot, generally further out in time. Everything works right up until i try to get information from the calendar even, .getStartTime(), due to the .getEvent calls returning null. not sure how to fix what other sources are telling me is a nonfunctional command that yet still "functions as intended".
function IEPscheduler() {
var spreadsheet = SpreadsheetApp.getActiveSheet(); // call sheet
//var calendarID = spreadsheet.getRange("H1").getValue();
var eventCal = CalendarApp.getCalendarById("mascharterschool.com_0edapns33khde9ig0di31i2mvc#group.calendar.google.com");
var signups = spreadsheet.getRange("A2:C2").getValues();
var lr = spreadsheet.getLastRow();
var lc = spreadsheet.getLastColumn(); //
var count = spreadsheet.getRange(2,1,lr-1,lc-1).getValues();// get meeting data
for (x=0; x<count.length; x++){
var shift = count[x]; // pull row from meeting data
var Start = shift[0];
var End = shift[1];
var Student = shift[2];
var guests = shift[3];
var description = shift[4];
var location = shift[5];
var run=shift[6]; // run following based on status column
// new meeting is scheduled
if(run == null || run == ''){
var event = {
'location': location,
'description':description ,
'guests':guests +',',
'sendInvites': 'True',
}
var invite = eventCal.createEvent(Student, Start, End, event);
invite.setGuestsCanInviteOthers(true); // allow guests to invite others
var eventId = invite.getId();
var date = invite.getDateCreated();
spreadsheet.getRange(x+2,7).setValue('Invite created'); // set status in sheet
spreadsheet.getRange(x+2,8).setValue(date); // inital date for created meeting invite
spreadsheet.getRange(x+2,9).setValue(eventId);
}
// check existing meetings for updates
else {
var id = shift[9];
var invite = eventCal.getEventSeriesById('id');
// if the time or location has changed update calander
if(invite.getStartTime() !== Start || invite.getEndTime() !== End || invite.getLocation() !== location){
// if sheet override flagged
if(shift[lc-1] !== null || Shift[lc-1] !== ''){
invite.setTime(Start,End); // update start/end time
invite.setLocation(location); // update location
}
// if canalder invite is further out than spreadsheet --> update spreadsheet
if(invite.getStartTime() >> Start){
spreadsheet.getRange(x+2,1).setValue();
spreadsheet.getRange(x+2,2).setValue();
}
// if spread sheet time is later than invite --> updater invite
else{
invite.setTime(Start,End); // update start/end time
invite.setLocation(location); // update location
}
var date = invite.getLastUpdate();
spreadsheet.getRange(x+2,7).setValue('Updated'); // set new status in sheet
spreadsheet.getRange(x+2,8).setValue(date); // set date meeting was updated
}
// if guest list has changed ???
if
}
}
}
// set script to be runnable from sheet tool bar
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Sync to Calendar') // tool bar banner
.addItem('Create Events Now', 'IEPscheduler') // sub catageory (title, function to run)
.addToUi();
}
We actually figured it out shortly after posting and I couldn't get back to this. Turns out the ID from .getId is the iCalUID and the .getEventById() takes a iCalID. The difference is that the UID has '#google.com' appended to the end of the ID. Split at the '#' and the get event works perfectly.
It's a stupid quirk that the getId command returns the right data in a useless form that requires editing to be used for its intended purpose.
No nulls returned for me with this script:
function getEvents() {
const cal=CalendarApp.getDefaultCalendar();
const dt=new Date();
const start=new Date(dt.getFullYear(),dt.getMonth()-1,dt.getDate());
const end=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate());
var events=cal.getEvents(start, end);
let eObj={idA:[],tA:[]}
events.forEach(function(ev,i){
eObj.idA.push(ev.getId());
eObj.tA.push(cal.getEventById(ev.getId()).getTitle());
});
Logger.log(JSON.stringify(eObj));
return eObj;
}

Pushing a simple Log string from a Google Ad Script to a Google Sheet

I am trying to set up a script which can push data from an App Script into a Google Sheet.
I have the script successfully logging what I want, which goes in the following format Account budget is 12344, but now I want to push this into a Google Sheet. I have set up a variable containing the URL and another variable containing the sheet name, and also a clear method to delete anything already there.
Find the code I have below:
// - The link to the URL
var SPREADSHEET_URL = 'abcdefghijkl'
// - The name of the sheet to write the data
var SHEET_NAME = 'Google';
// No to be changed
function main() {
var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = spreadsheet.getSheetByName(SHEET_NAME);
sheet.clearContents();
}
function getActiveBudgetOrder() {
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
Logger.log("Budget Order Amount " + budgetOrder.getSpendingLimit());
}
}
Assuming you want to clear the entire Sheet every time you extract the data this should work for you. You will need to set the url and shtName variables.
function getActiveBudgetOrder() {
var url = 'https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxx/';
var shtName = 'Sheet1';
var arr = [];
var sht = SpreadsheetApp.openByUrl(url).getSheetByName(shtName);
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
arr.push(["Budget Order Amount " + budgetOrder.getSpendingLimit()]);
}
sht.clearContents();
sht.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}

Send email when cell contains a specific value

I am working on a Google Form that allows our employees to submit an in-field inspection of their equipment. I have a script that takes the form responses and creates a new sheet based on the date and the specific unit number of the equipment. The user goes through a checklist and selects either "Good" or "Needs Repair" for each item on the list. They can also add comments and upload pictures of any issues.
I am trying to have the script automatically send an email if "Needs Repair" is selected for any of the checks, as well as if the user adds a comment or a picture. This way we do not have to open every submitted sheet to know if any repairs are required. What I have is just not sending emails and I cannot figure out why. Any help is greatly appreciated!
Here is my current script:
function onFormSubmit() {
// onFormSubmit
// get submitted data and set variables
var ss = SpreadsheetApp.openById("*Spreadsheet Link*");
var sheet = ss.getSheetByName("Submissions");
var row = sheet.getLastRow();
var Col = sheet.getLastColumn();
var headings = sheet.getRange(1,1,1,Col).getValues();
var lastRow = sheet.getRange(row, 1, 1, Col);
var UnitNumber = sheet.getRange(row,3).getValue();
var newSheet = sheet.getRange(row,4,Col).getValue();
var fileExist = false;
var drillSheet = null;
var folder = DriveApp.getFoldersByName("Fraser Drill Inspections").next();
var files = folder.getFilesByName(UnitNumber);
var file = null;
var employee = sheet.getRange(row,2);
var checks = sheet.getRange(row, Col, 1, 20);
// check if Drill has sheet
while (files.hasNext())
{
fileExist = true;
file = files.next();
break;
}
if (fileExist) //If spreadsheet exists, insert new sheet
{
drillSheet = SpreadsheetApp.openById(file.getId());
drillSheet.insertSheet("" + newSheet);
}
else //create new spreadsheet if one doesn't exist
{
drillSheet = SpreadsheetApp.create(UnitNumber);
var ssID = drillSheet.getId();
file = DriveApp.getFileById(ssID);
file = file.makeCopy(UnitNumber, folder);
DriveApp.getFileById(ssID).setTrashed(true);
drillSheet = SpreadsheetApp.openById(file.getId());
drillSheet.renameActiveSheet(newSheet);
}
// copy submitted data to Drill sheet
drillSheet.getSheetByName(newSheet).getRange(1,1,1,Col).setValues(headings);
drillSheet.appendRow(lastRow.getValues()[0]);
drillSheet.appendRow(['=CONCATENATE(B6," ",B5)']);
drillSheet.appendRow(['=TRANSPOSE(B1:2)']);
//Hide top rows with raw data
var hiderange = drillSheet.getRange("A1:A3");
drillSheet.hideRow(hiderange);
//Widen columns
drillSheet.setColumnWidth(1,390);
drillSheet.setColumnWidth(2,700);
//Send email if there are any comments or if anything needs repair
if(lastRow.getValues() == "Needs Repair") {
function SendEmail() {
var ui = SpreadsheetApp.getUi();
MailApp.sendEmail("email#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.")
}
}
}
The function to send an email is:
GmailApp.sendEmail(email, subject, body);
Try changing
if(lastRow.getValues() == "Needs Repair") {
function SendEmail() {
var ui = SpreadsheetApp.getUi();
MailApp.sendEmail("email#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.")
}
}
to just the following:
if(lastRow.getValues() == "Needs Repair") {
GmailApp.sendEmail("youremail#domain.com", "Drill Needs Repair", "This drill requires attention according to the most recent inspection report.");
}
It looks like you've still got some additional work to do too, e.g. to make it send to the email address from the form submission instead of a hardcoded one.

Templated email using Apps Script based on data in Google Sheets

I'm new to scripting so I hope someone can help me create one.
I'm trying to create a script for a sheet that can send an email when a custom menu is pressed.
Here's a file to work on.
https://docs.google.com/spreadsheets/d/1Ea-3eZoclHrAkZLwRmWWFbmbnn4dESNWvK_6pn1DCbE/edit?usp=sharing
Also, it should only send it if a column (ex. Column I) has a specific Value like 'Approved'
Email content should look like:
Subject: Leave Application # 'ColumnC'
Hi 'ColumnA',
We received your 'ColumnB' request for 'ColumnE'
Status: 'ColumnG'
More Details: 'ColumnH'
-Admin
Email should be sent to Column E and F.
The script must also update the spreadsheet to avoid duplicate emails.
Here is the working example
Here is the code:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var startRow = 2; // First row of data to process
var lastRow = sheet.getSheetByName('Journal').getLastRow(); // Last row with content
var rangeEmailSent = sheet.getRangeByName('Journal!EmailSent');
var dataEmailSent = rangeEmailSent.getValues();
var dataRequestedBy = sheet.getRangeByName('Journal!RequestedBy').getValues();
var dataRequestType = sheet.getRangeByName('Journal!RequestType').getValues();
var dataRefNo = sheet.getRangeByName('Journal!RefNo') .getValues();
var dataStatus = sheet.getRangeByName('Journal!Status') .getValues();
var dataToEmail = sheet.getRangeByName('Journal!ToEmail') .getValues();
var dataSupComment = sheet.getRangeByName('Journal!SupervisorComment').getValues();
var subjectTemplate = sheet.getRangeByName('SubjectTemplate1').getValue();
var bodyTemplate = sheet.getRangeByName('BodyTemplate1').getValue();
var msgSubject;
var msgBody;
for (var i = (startRow-1); i <= (lastRow-1); i++) {
// send e-mail if "Email Sent" is not blank and if "Status" is not empty
if ( !(dataEmailSent[i]=='Yes') && !(dataStatus[i] =='')) {
msgSubject = subjectTemplate.replace('$REF$', dataRefNo[i]);
msgBody = bodyTemplate
.replace('$REQUESTED_BY$', dataRequestedBy[i])
.replace('$REQUEST_TYPE$', dataRequestType[i])
.replace('$EMAIL$', dataToEmail[i])
.replace('$STATUS$', dataStatus[i])
.replace('$SupervisorComment$', dataSupComment[i]);
// Logger.log(msgSubject);
// Logger.log(msgBody);
MailApp.sendEmail(dataToEmail[i], msgSubject, msgBody);
// Change "Email sent" to "Yes"
rangeEmailSent.getCell(i+1,1).setValue('Yes'); // note: getCell(1,1) refers to the 1st cell
}
}
}