Storing an array of values from Docs in Google Apps Scripts - google-apps-script

I'm a beginner working with Google Apps Script to pull data from a Google Doc, and I need some help...
I have a Google Doc that has a ton of cooking recipes. I'd like to write a function that randomly selects 4 recipes, and emails me the ingredients so I know what to shop for that week. All my recipes titles are 'Heading 3', with the ingredients as bullet points below them. I'm fully open to modifying the formatting if need be.
I think I have a way to identify all text that is of type 'Heading 3', but I need a way to store them in an array as text, to then randomly select 4 of them. I can't seem to solve this...
function onOpen() {
var ui = DocumentApp.getUi();
ui.createMenu('Generate Weekly Shopping List')
.addItem('Send Email', 'generateMenu')
.addToUi();
}
function generateMenu() {
var ps = DocumentApp.getActiveDocument().getBody()
var searchType = DocumentApp.ElementType.PARAGRAPH;
var searchHeading = DocumentApp.ParagraphHeading.HEADING3;
var searchResult = null;
while (searchResult = ps.findElement(searchType, searchResult)) {
var par = searchResult.getElement().asParagraph();
if (par.getHeading() == searchHeading) {
// Found one, update Logger.log and stop.
var h = searchResult.getElement().asText().getText();
return h;
//how do I store this back into an array...then randomly select 4?
}
// Get the email address of the active user - that's you.
var email = Session.getActiveUser().getEmail();
// Send yourself an email with a link to the document.
GmailApp.sendEmail(email, "Shopping List For The Week", "Here is the shopping list:" + h);
}
}

The first function generates an array of objects from your document
function generateObj() {
var body = DocumentApp.getActiveDocument().getBody()
var children=body.getNumChildren();
//var html='';
var rObj={rA:[]}
for(var i=0;i<children;i++) {
var child=body.getChild(i);
if(child.getType()==DocumentApp.ElementType.PARAGRAPH && child.asParagraph().getHeading()==DocumentApp.ParagraphHeading.HEADING3 && child.asParagraph().getText().length>0) {
//html+='<br />' + child.asParagraph().getText();
var prop=child.asParagraph().getText();
rObj.rA.push(prop);
rObj[prop]=[];
var n=1;
while(body.getChild(i+n).getType()==DocumentApp.ElementType.LIST_ITEM) {
//html+='<br />'+body.getChild(i+n).asListItem().getText();
rObj[prop].push(body.getChild(i+n).asListItem().getText());
n++;
}
i+=n-1;
}
}
//DocumentApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), 'Results')
return rObj;
}
//defaults to 4 selections
function pikn(n=4) {
var rObj=generateObj();//get array of objects
var rA=[];
var t=rObj.rA.slice();//copy array to temp array
for(var i=0;i<n;i++) {
var idx=Math.floor(Math.random()*t.length);//pick random index
rA.push(t[idx]);//save title of recipe
t.splice(idx,1);//remove that index
}
var s='';
//loop through selected recipes which are also object properties
rA.forEach(function(r,i){
var items=rObj[r];
s+='\n' + r;
items.forEach(function(item,j){s+='\n' + item});//loop through recipe items and collect everything in s as simple text to insert into standard body
});
GmailApp.sendEmail("Your email address","Random Recipes",s);
}
Requires Chrome V8

Related

Copying Google Doc text into a google sheet based on the Heading

I am a project manager for a construction company. I have a Google doc with a Master To-Do list for different Jobs and subcontractors. https://docs.google.com/document/d/14oVky5DA3xtjE7COMVAx6l26WXb35HvsL8V9dng3eIc/edit?usp=sharing
I want to be able to use a menu item to update a google sheet with items for a specific subcontractor.
So far I can make it find the heading. I'm Not sure how to go about "digging" down into the specific items below it.
I should be able to put it into the appropriate spreadsheet from there. I'm still super new to coding so let me know if I need to clarify anything.
function onOpen() {
var ui = DocumentApp.getUi();
ui.createMenu('Update Subs')
.addItem('Update', 'get_some_heading') //done
.addToUi();
}
function get_some_heading() {
var GlobalText = DocumentApp.getActiveDocument().getBody()
var searchType = DocumentApp.ElementType.PARAGRAPH;
var searchSubNames = DocumentApp.ParagraphHeading.HEADING4;
var searchResult = null;
while (searchResult = GlobalText.findElement(searchType, searchResult)) {
var par = searchResult.getElement().asParagraph();
var SubName = searchResult.getElement().asParagraph().asText().getText();
if (par.getHeading() == searchSubNames) {
if (SubName == "Subcontractor 2"){
DocumentApp.getUi().alert(SubName);
return SubName;
}
}
}
}

Different behaviour of Classroom API in function if called individually or from a loop

This is a complex and strange issue I'm facing and I hope anyone could bring me, if not a solution to the problem, something to circumvent it.
Starting from the beginning, I'm the administrator in a school that uses Google Suite for Education and Classroom for managing coursework with students, specially now that we are forced to work from home.
We think it should be a good idea to use Classroom API to get a good view of the students work and their grading as a whole. So we made a Spreadsheet and a script to get a report that shows all coursework from a student with his gradings in all the subjects (courses in Classroom) he is attending. So far, after solving some misunderstandings of the API, we managed to code the script.
After this we tried to upsize the script so that we could make a report for one group of students in one time, which is a great advance since making 30 reports individually is time-consuming. That wasn't very difficult, since we have all the list of students and groups in a sheet and more or less it is done with a loop. And it worked and we can get a full report of all students in a group, for all their coursework in all the subjects.
But the problem came when we started to run this script and we are getting errors in some groups, in some students, and we do not know why. After some investigation, we found that the call to Classroom.Courses.list(optionalArgs) gives 430 courses, although we are using a filter for the student in the report. 430 courses is not a high number but the loop for all of them, even considering that the student is not enrolled in them, takes longer than 1800 s and the script rises an 'out of time error'.
Surprisingly, if I run the same function called inside the loop for the group but in a direct call, then it works flawlessly and gives me no error. In this case, the student that gives the previous error is not enrolled in any course so the call to Classroom.Courses.list(optionalArgs) gives me a null set of courses and the function returns almost instantly without making any report.
I am showing the full script code, but the interesting part of it is not so long. It has many parts that do supporting tasks such as getting data form other sheets or showing Html dialogs on screen.
Best regards
Rafael
P.S. Excuse me for this long text, but the code is much longer and I think explaining the situation can improve understanding our problem.
// Menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Functions')
.addItem('Group report','fReportGroupHtml')
.addItem('Student report','fStudentReportHtml')
.addToUi();
}
//------------------------------------------------------------------------
// Report for 1 student
// Opens HTML dialog to select group and student
//------------------------------------------------------------------------
function fStudentReportHtml()
{
// Html dialog
try {
var output = HtmlService.createHtmlOutputFromFile('SelecStudent');
SpreadsheetApp.getUi().showModalDialog(output,'Select student');
}
catch(err) {
Logger.log(err);
return;
}
}
//---------------------------------------------------------------------------
// Report for 1 student
// Receives information from dialog and makes report
//------------------------------------------------------------------------
function fStudentReport(form_data)
{
// Form information
var nameStudent=form_data.student;
// Split students name and surname
var posco=nameStudent.indexOf(',');
var surname=nameStudent.substring(0,posco);
var name=nameStudent.substring(posco+2);
// Gets student email
var idStudent=getUserEmail(name,surname);
// List student coursework
var ssCourses=SpreadsheetApp.getActive();
var hWorks=ssCourses.getSheetByName("StWork");
fListStudentWork(idStudent,hWorks,nameStudent);
}
//---------------------------------------------------------------------------
// Report for all students in a group
// Opens HTML dialog to select group
function fReportGroupHtml()
{
// Html dialog
try {
var output = HtmlService.createHtmlOutputFromFile('SelecGroup');
SpreadsheetApp.getUi().showModalDialog(output,'Select group');
}
catch(err) {
Logger.log(err);
return;
}
}
//---------------------------------------------------------------------------
// Report for all students in a group
// Receives information from dialog and makes report
function fReportGroup(form_data)
{
var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";
var idTemplateInf="1K6YgZvh195eo4b-DLtRHuvjFdG3SOBJILodzS4CUnBs";
var idCarpInf="16nH3BhAwHPP3BcAUK9eW_FQdWRM6B87T";
var valAlu=[[]];
// Opens students sheet
var ss = SpreadsheetApp.openById(idStudents);
var coursesSS = ss.getSheetByName("Students");
var lastrow=coursesSS.getLastRow();
var rangeA=coursesSS.getRange(2,2,lastrow-1,3).getValues();
// First student in the group
for(var ii=0; ii<lastrow-1; ii++)
{
if (rangeA[ii][2]==form_data.groupAlu)
break;
}
var ini=ii;
// Opens sheet for messaging
var ssClass = SpreadsheetApp.getActive();
// Copies template sheet into a new file
var idNewFileInf = DriveApp.getFileById(idTemplateInf).makeCopy(idCarpInf).getId();
DriveApp.getFileById(idNewFileInf).setName('Report '+form_data.groupAlu);
// Opens new sheet and writes general information
var ssInf = SpreadsheetApp.openById(idNewFileInf);
var hjInfAlu = ssInf.getSheetByName("Students");
hjInfAlu.getRange(2,1).setValue("GRUPO: "+form_data.groupAlu);
var dateToday=new Date();
hjInfAlu.getRange(3,1).setValue("FECHA: "+Utilities.formatDate(dateToday,"GMT+1","dd/MM/yyyy"));
// All students in the group
var iiAlu=1;
while(rangeA[ii][2]==form_data.groupAlu)
{
// Gets student's information
var surname=rangeA[ii][0];
var name=rangeA[ii][1];
var nameStudent=surname+", "+name;
// Gets student email
var idStudent=getUserEmail(name,surname);
// Copies sheet
var hjInfWork = ssInf.getSheetByName("Works").copyTo(ssInf).setName(nameStudent);
// Makes report of student's work and submissions
var mens="Creando informe de student: "+nameStudent;
ssClass.toast(mens, "Informe de tareas",-1);
var states=fListStudentWork(idStudent,hjInfWork,nameStudent);
// Writes student information in first sheet
valAlu[0][0]=iiAlu;
valAlu[0][1]=nameStudent;
valAlu[0][2]=states[0];
valAlu[0][3]=states[1];
valAlu[0][4]=states[2];
hjInfAlu.getRange(iiAlu+5, 1, 1, 5).setValues(valAlu);
ii++;
iiAlu++;
}
// Deletes sheet 'Works'
ssInf.deleteSheet(ssInf.getSheetByName("Works"));
// Final message
ssClass.toast("Fin del informe", "Informe de tareas",5);
}
//---------------------------------------------------------------------------
// List coursework for a student
function fListStudentWork(idStudent, dataSheet, nameStudent)
{
// Variables
var ii=0;
var lWorks=[[]];
var stateWork=[0,0,0];
var pageToken = null;
var pageToken2 = null;
var colorCourse1= "#ddffdd";
var colorCourse2= "#f9e9b0";
var iiColor=0;
var optionalArgs=
{
pageToken: pageToken,
courseStates: 'ACTIVE',
studentId: idStudent,
pageSize: 0
};
var optionalArgs2=
{
userId: idStudent,
pageSize: 0
};
// Date today
var now = new Date();
var year=now.getFullYear();
var month=now.getMonth();
// Date starting term
if (month>=8)
var cadfecha="September 1, "+year.toString();
else
var cadfecha="September 1, "+(year-1).toString();
var fechaini=new Date(cadfecha);
// Empty sheet
var rowWorks=dataSheet.getLastRow();
var colWorks=dataSheet.getLastColumn();
if (rowWorks>3)
{
var rnWorks=dataSheet.getRange(4, 1, rowWorks-3, colWorks);
rnWorks.clearContent().clearFormat();
}
// General information in sheet
if (!nameStudent)
nameStudent=getUserName(idStudent);
Logger.log("INI: "+nameStudent);
dataSheet.getRange(1, 1).setValue("ENTREGAS DEL ALUMNO: "+nameStudent);
var dateToday=new Date();
dataSheet.getRange(2, 1).setValue("FECHA: "+Utilities.formatDate(dateToday,"GMT+1","dd/MM/yyyy"));
// First: courses for the student
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
if (!courses || courses.length === 0)
dataSheet.getRange(4, 2).setValue("No hay clases");
else
{
Logger.log(" courses: "+courses.length);
for (course in courses)
{
var fechaCourse=new Date(courses[course].creationTime);
if (fechaCourse>=fechaini)
{
// Information from the course
var idCourse=courses[course].id;
var nomprof=getUserName(courses[course].ownerId);
var colorCourse=(iiColor==0)? colorCourse1 : colorCourse2;
iiColor=1-iiColor;
// Gets coursework from the course
var responseT = Classroom.Courses.CourseWork.list(idCourse);
var works = responseT.courseWork;
if (!works || works.length === 0)
dataSheet.getRange(4, 2).setValue("No hay información");
else
{
for (work in works)
{
var idWork=works[work].id;
var maxPoints=(works[work].maxPoints==null)? "" : works[work].maxPoints;
// Gets submissions
try
{
var responseE = Classroom.Courses.CourseWork.StudentSubmissions.list(idCourse, idWork, optionalArgs2);
var submis = responseE.studentSubmissions;
}
catch(ee)
{
var submis=null;
}
if (submis && submis.length >0)
{
for (subm in submis)
{
lWorks[0][0]=ii+1;
lWorks[0][1]=courses[course].name;
lWorks[0][2]=nomprof;
lWorks[0][3]=works[work].title;
var dateWork=works[work].dueDate;
if (dateWork==null)
lWorks[0][4]="--/--/----";
else
lWorks[0][4]=dateWork.day+"/"+dateWork.month+"/"+dateWork.year;
var points=(submis[subm].assignedGrade==null)? "" : submis[subm].assignedGrade;
lWorks[0][5]=points+" / "+maxPoints;
var dateSubmis=submis[entrega].creationTime;
if (dateSubmis==null)
lWorks[0][6]="--/--/----";
else lWorks[0][6]=Utilities.formatDate(new Date(dateSubmis), "GMT+1","dd/MM/yyyy");
var state=submis[subm].state;
// Gets state of submission
var cState="";
var lateSubmis=0;
if ((state=="RETURNED")||(state=="TURNED_IN"))
{
cState="ENTREGADO";
stateWork[0]++;
}
else
{
// Checks if late or not handed
if (dateWork!=null)
{
var dateEnd=new Date(dateWork.year,dateWork.month-1,dateWork.day);
if (dateEnd.valueOf()<dateToday.valueOf())
{
cState="NO ENTREGADO";
stateWork[1]++;
lateSubmis=1;
}
else
stateWork[2]++;
}
}
lWorks[0][7]=cState;
dataSheet.getRange(ii+4, 1, 1, 8).setValues(lWorks).setBackground(colorCourse);
if (lateSubmis)
dataSheet.getRange(ii+4, 8).setFontColor("red").setFontWeight("bold");
else
dataSheet.getRange(ii+4, 8).setFontColor("black").setFontWeight("normal");
ii++;
}
}
}
}
}
}
}
return(stateWork);
}
/////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Gets list of groups
function listGroups()
{
var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";
try
{
// Opens students sheet
var ss = SpreadsheetApp.openById(idStudents);
var coursesSS = ss.getSheetByName("Students");
var lastrow=coursesSS.getLastRow();
var rangeA=coursesSS.getRange(2,4,lastrow-1,1).getValues();
var courses = [];
var courseant="";
for(var ii=0; ii<lastrow-1; ii++)
{
if (rangeA[ii][0]!=courseant)
{
courseant=rangeA[ii][0];
courses.push(courseant);
}
}
return courses;
}
catch(err) {
Logger.log(err);
}
}
//------------------------------------------------------------------------
// Gets list of students in group
function listStudents(group)
{
var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";
try
{
// Opens students sheet
var ss = SpreadsheetApp.openById(idStudents);
var coursesSS = ss.getSheetByName("Students");
var lastrow=coursesSS.getLastRow();
var rangeA=coursesSS.getRange(2,1,lastrow-1,4).getValues();
// First student
for(var ii=0; ii<lastrow-1; ii++)
{
if (rangeA[ii][3]==group)
break;
}
var ini=ii;
// Last student
while(rangeA[ii][3]==group)
ii++;
// Gets array of students
var students=coursesSS.getRange(2+ini,2,ii-ini,2).getValues();
return students;
}
catch(err) {
Logger.log(err);
}
}
//------------------------------------------------------------------------
// Gets list of courses
function listCourses()
{
try
{
// Opens courses sheet
var ss = SpreadsheetApp.getActive();
var coursesSS = ss.getSheetByName("Courses");
var lastrow=coursesSS.getLastRow();
var courses=coursesSS.getRange(3,2,lastrow-2,4).getValues();
return courses;
}
catch(err) {
Logger.log(err);
}
}
//---------------------------------------------------------------------------
// Gets user name from ID
function getUserName(usid)
{
var result = AdminDirectory.Users.get(usid, {fields:'name'});
var fullname = result.name.fullName;
return fullname;
}
//----------------------------------------------------------------------------
// Gets user email from name
function getUserEmail(name,surname)
{
var userIds = AdminDirectory.Users.list({domain:"iesciudadjardin.com",
query:"givenName:'"+name+"' familyName:'"+surname+"'"}).users;
if ((userIds==undefined)||(userIds.length!=1))
return null;
else
return userIds[0].primaryEmail;
}
Aditional information
I understand the question is too big, so I am going to try explaining things with as little code as possible.
Essentialy my code works. It hangs sometimes with some students. The main part of it is the function fListStudentWork:
// List coursework for a student
function fListStudentWork(idStudent, dataSheet, nameStudent)
{
var optionalArgs=
{
pageToken: pageToken,
courseStates: 'ACTIVE',
studentId: idStudent,
pageSize: 0
};
// First: courses for the student
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
if (!courses || courses.length === 0)
// NO COURSES
else
{
// MAKE REPORT
}
}
And now, if I call it with one special student, it works, returning a null set of courses, since John Smith is not enrolled in any course (it also happens with students enrolled in some courses):
function OneStudentReport()
{
fListStudentWork("john.smith#mydomain.com",mySheet,"John Smith");
}
But if I loop with a set of students, in which John Smith is part, all the previous students reports are Ok, and when the loop reaches him, it returns all the courses in my school, and the script aborts with a time out error.
function fReportGroup(form_data)
{
// All students in the group
while(StudentGroup=="MYGROUP")
{
// Gets student's information
var surname=rangeA[ii][0];
var name=rangeA[ii][1];
var nameStudent=surname+", "+name;
// Gets student email
var idStudent=getUserEmail(name,surname);
// Makes report of student's work and submissions
var states=fListStudentWork(idStudent,MySheet,nameStudent);
}
}
P.S. If the administrator thinks I should delete the first part of the message with the full code because it is too long, let me know.
Thank you
and sorry for my late answer. I have been thoroughly testing my code and I think I found the source of the error. The function fListStudentWork receives an id of the student to list his work. This id can be his email. If I called it directly, I used his email directly, so no errors were given. However, when I used it in the loop for all the group, I recalled his id from his name and surname, and here I found some errors: in a couple of sudents with the same familyName and very similar givenName, which is the way I use to get his email, using Directory API.
In these few cases, the email returned was null, and function fListStudentWork did not check for a null email. In addition, inside the function, Classroom API returned me all the 400 courses when I requested assignments for the null student, which finally led my script to time out, unable to handle such number of courses. Now the question could be why the Directory API returns null for such users, but I will write it in another question.
Thank you all for your help.

Apps Script getEventById() returns null

I am new to Apps Script and struggling with the "getEventById()" function.
My goal is to delete an event entry on Google Calendar via Google Sheets when you press a button.
I already managed to get the event id via Apps Script and it´s Google API V3, but when I hand it over to "getEventById" as parameter, it returns null, even when I "hardcode" the id.
Here´s my code. I removed some parts since those aren´t important I think:
function calDate(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var calId = spreadsheet.getRange("N1").getValue();
var calEvent = CalendarApp.getCalendarById(calId);
var ui = SpreadsheetApp.getUi();
var selection = spreadsheet.getSelection();
var selectedRow = selection.getActiveRange().getA1Notation();
var rowRange = sheet.getRange(selectedRow);
var rowNumber = rowRange.getRow();
var colRange = sheet.getRange(selectedRow);
var colNumber = colRange.getColumn();
if (colNumber !== 15){
//wait for showtime
}
else{
// its showtime
var combinedRange = "O" + rowNumber;
var sheetData = sheet.getRange(rowNumber, 3, 1, 15).getValues();
if(sheetData[0][12] == false){
var dateStart = new Date(sheetData[0][7]);
var dateEnd = new Date(sheetData[0][8]);
var KdName = sheetData[0][0];
var BV = event_id[0][4];
var combinedNames = KdName + " - " + BV;
var items = Calendar.Events.list(calId, {
timeMin: dateStart.toISOString(),
timeMax: dateEnd.toISOString(),
q: combinedNames
}).items;
}
else{
var testVar = calEvent.getEventById(/*This is where I would put the htmlLink (the event-id)*/);
console.log(testVar);
}
}
}
Hopefully those informations are enough and if not, feel free to ask for more.
I really hope you guys can help me out!
Kind regards
EDIT & SOLUTION
Okay guys, thanks to Mateo Randwolf, who kindly opened an issue at Google about this, I was able to figure it out. This is the link with an example how to get the the ID from the event and hand that id over to the "getEventById()" function. Or here as a code-block:
function findEventID() {
var now = new Date();
var nextEvent = new Date(now.getTime() + (2 * 60 * 60 * 1000));
var event = CalendarApp.getDefaultCalendar().getEvents(now, nextEvent);
ID = event[0].getId();
Logger.log('EventID: ' + event[0].getId());
Logger.log(CalendarApp.getDefaultCalendar().getEventById(ID));
}
Now it gets funny. This line:
Logger.log('EventID: ' + event[0].getId());
returns the event-id like it should.
But this one:
Logger.log(CalendarApp.getDefaultCalendar().getEventById(ID));
doesn´t show anything except "{}", which is weird.
But if you apply "deleteEvent()" on it, like so:
calEvent.getEventById(ID).deleteEvent(); //calEvent is a CalendarApp, see above
It actually deletes the event from the calendar!
With that, I´d say we found the solution or at least a bypass.
Issue
Hi ! So it seems to me that getEventById() has a bug that returns null instead of the event object as I was getting the exact same behaviour you were reporting in this question. I have filed this behaviour to the public issue tracker, you can find it here with all the discussion on this behaviour.
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)
Using the Calendar API search query to find events in a calendar
function calDate(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getActiveSheet();
var calId=ss.getRange("N1").getValue();
var calEvent=CalendarApp.getCalendarById(calId);
var row=ss.getActiveRange().getRow();
var col=ss.getActiveRange().getColumn()
if (col!=15){
//wait for showtime
}else{
var vs=sh.getRange(row, 3, 1, 15).getValues();
if(vs[0][12] == false){
var dateStart=new Date(vs[0][7]);//col J
var dateEnd=new Date(vs[0][8]);//col K
var KdName=vs[0][0];//col 3
event_id below is not defined
var BV=event_id[0][4];//col G
var combinedNames=KdName + " - " + BV;
var items=Calendar.Events.list(calId, {timeMin: dateStart.toISOString(),timeMax: dateEnd.toISOString(),q: combinedNames}).items;
}
else{
var testVar=calEvent.getEventById(/*This is where I would put the htmlLink (the event-id)*/);
console.log(testVar);
}
}
}
Since you couldn't share your spreadsheet I share mine with an example
One thing that helps a lot is playing with the API explorer to figure what works and what doesn't. If you want to display all of the fields you can use * and this example proved very helpful as well
Here's the code:
function myOwnEventSearch() {
var calendarId='***********calendar id**************';
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet235');
var sr=2;
var sc=2
var rg=sh.getRange(sr,sc,sh.getLastRow()-sr+1,sh.getLastColumn()-sc+1);
var vA=rg.getValues();
var hA=sh.getRange(sr-1,sc,1,sh.getLastColumn()-sc+1).getValues()[0];
var idx={};//locates the data index from column header names
hA.forEach(function(h,i){idx[h]=i;});
var cal=CalendarApp.getCalendarById(calendarId);
var html='<style>td,th{}</style><table><tr><th>Summary</th><th>Start</th><th>End</th><th>Id</th></tr>'
for(var i=0;i<vA.length;i++) {
if(!vA[i][idx['Id']] && vA[i][idx['DateFrom']] && vA[i][idx['DateTo']] && vA[i][idx['SearchString']]) {
var request={timeMin:new Date(vA[i][idx["DateFrom"]]).toISOString(),timeMax:new Date(vA[i][idx["DateTo"]]).toISOString(),q:vA[i][idx["SearchString"]],showDeleted: false,singleEvents:true,maxResults:10,orderBy:"startTime"};
var resp=Calendar.Events.list(calendarId, request);
if(resp.items.length>0) {
var idA=[];
resp.items.forEach(function(item,j){
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>', item.summary,item.start,item.end,item.id);
idA.push(item.id);
});
sh.getRange(i+sr,idx['Id']+sc).setValue(idA.join(', '))
}
}
}
html+='<table>';
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html).setWidth(800), "My Events")
}
Here's the spreadsheet before the script runs.
Here's the dialog that displays the search results.
Here's what the spreadsheet looks like after running script:
The Event Ids were copied into the Id Column
And these were the four events I created on my calendar::
Here is how I worked around this. I stored all the events (from the range I was interested in) in a JavaScript Map() so I can find them later:
var cal = CalendarApp.getCalendarById("/* supply your calendar ID here*/");
if (!cal) {
Logger.log("Calendar not found for ID:" + calendarID);
} else {
var calEvents = cal.getEvents(new Date("March 8, 2010"), new Date("March 14, 2025"));
// Store them by eventId for easy access later
var calEventMap = new Map();
for (var j in calEvents) {
calEventMap.set(calEvents[j].getId(), calEvents[j]);
}
/* Later when you need to look up by iCalID... */
var calEvent = calEventMap.get(eventID);
}
Works for me when you get the calendar by id like this:
const calendar = CalendarApp.getCalendarById("theCalendarId");
const event = calendar.getEventById("someEventId");
Now the event is not null, but the actual event, and you can do whatever you want with it from here!

How can I compare the values in one column to the values in other columns?

If I have a spreadsheet where people go on and rank songs (each song has its own row and each user has their own column), how can I write a script to iterate over each user column and then compare the individual user's rating of each song to the column that is the input?
The goal is to output the column with rankings most similar to the input column's rankings (ideally this should be an iterative recursive function with space complexity of O(1) and time complexity of O(log(n)).
I would think to make a function that produces a formula to be used like =FINDMOSTSIMILAR(<USER>) that outputs the value of the first row in the output column, but I'm not sure where to begin. I have some experience in JavaScript and I know Google Apps Script is based on JS, but I don't know how to do this function in GAS.
Spreadsheet
I tried to give you a start. Go to sheet, click Tools > Script Editor. Paste this code.
To find similar user, what this code does is, between input user and any other user, calculate differences for every song ratings and add them to produce a value. Do this for every user in sheet. Then find the lowest of differences and output which user produces this value. This logic is separated in compare function and you can change it to suit your needs.
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Find Similar User')
.addItem('Similar User', 'showPrompt')
.addToUi();
}
// this configuration is based on current sheet names and formatting
// if sheet names change, change needed here
// if track title and user columns change, change needed here
var config = {
// sheet name: [ col of track title, col of 1st user, col of last user ]
"UV6": [6, 9, 28],
"Midnight Underground": [7, 11, 28],
"Furious Fap February": [8, 12, 23],
"March Masturbation Madness": [7, 11, 31]
};
function showPrompt() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt(
'Find similar user.',
'Enter user name:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
text = text.trim();
if (!text) return;
FINDMOSTSIMILAR(text);
} else if (button == ui.Button.CANCEL) {
} else if (button == ui.Button.CLOSE) {
}
}
function FINDMOSTSIMILAR(username) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var aSheet = ss.getActiveSheet();
var aSheetName = aSheet.getName();
// get positions from config
if (!config[aSheetName]) return;
var userNamesColsStart = config[aSheetName][1];
var userNamesColsSpan = config[aSheetName][2] - config[aSheetName][1] + 1;
var titleColStart = config[aSheetName][0];
// read user names
var users = aSheet.getRange(1,userNamesColsStart,1,userNamesColsSpan).getValues()[0];
// Logger.log(users);
// read title col
var tArr = aSheet.getRange(2,titleColStart,aSheet.getLastRow()-2,1).getValues();
// Logger.log(tArr);
// data structure
var DS = {};
users.forEach(function(h, i) {
var obj = {};
var colValues = aSheet.getRange(2,userNamesColsStart+i,aSheet.getLastRow()-2,1).getValues();
tArr.forEach(function(v, i) {
obj[i] = colValues[i][0];
});
DS[h] = obj;
});
// Logger.log(DS);
var target = DS[username];
delete DS[username];
var results = [];
Object.keys(DS).forEach(function(user) {
var obj = {};
obj.prop = username+'__'+user;
obj.value = compare(target, DS[user]);
results.push(obj);
});
// sort based on difference values
results.sort(comp);
Logger.log(results);
// user with lowest difference is answer
var similarUser = results[0].prop.split('__')[1];
Logger.log(similarUser);
try {
ss.getActiveCell().setValue([similarUser]);
} catch(e) {}
}
function comp(a, b) {
if (a.value < b.value) return -1;
else if (a.value > b.value) return 1;
else return 0;
}
// this takes a difference of two users ratings for the same song
// accumulate all the differences for all songs into a single value
// change here how to compare 2 song ratings
function compare(target, user) {
var v = 0;
Object.keys(target).forEach(function(key, i) {
v += Math.abs(target[key] - user[key]);
});
return v;
}

Uploading Google Spreadsheet to a Google Site

I'd like to begin by stating that the end goal is to display our company directory (a list of our employees names/job title/extension#/office location/email), which is in a Google Sheet, on a page in one of our Google Sites.
I tried to use Google's embed function, and it works... but it is very clunky, does not have a "Sort" function, and it just looks weird.
I pulled a Google Apps Script from somewhere online like 3 months ago and it actually did pull in a way that made me happy:
(This is as it appears currently on the Google Sites page. So in this screenshot, the embedded Sheet is at the top. The Sheet when, imported via the script, is below. Yes, they are both on the same page. I'm in testing!)
This is the code I used (I THINK - I don't remember how I implemented it):
function myFunction() {
}
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(ScriptProperties.getProperty("page url") == null){
ss.addMenu("List page", [{name: "Create list", functionName: "create_list"},null,
{name: "Fetch list items", functionName: "fetch_items"}]);
}
else{
ss.addMenu("List page", [{name: "Push Items", functionName: "push_items"}]);
}
}
function create_list() {
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var parent_page = Browser.inputBox("URL of the parent page:");
var title = Browser.inputBox("Choose a name for your list page:");
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(parent_page).createListPage(title, title.split(' ').join(''), '', data[0]);
ScriptProperties.setProperty("page url", list.getUrl());
onOpen();
push_items();
}
function push_items(){
var done = false;
while(!done){
try{
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(ScriptProperties.getProperty("page url"));
var list_items = list.getListItems();
for(i in list_items){
list_items[i].deleteListItem();
}
for(var i = 1; i < data.length; i++){
var item = list.addListItem(data[i]);
}
done = true;
}
catch(e){
}
}
SpreadsheetApp.getActiveSpreadsheet().toast(ScriptProperties.getProperty("page url"), "List page updated", 10);
}
function fetch_items(){
var url = Browser.inputBox("URL of your list page:");
var col_number = Browser.inputBox("Number of columns in the list:");
var data = new Array();
var list_items = SitesApp.getPageByUrl(url).getListItems();
for(i in list_items){
var row = new Array();
for(j = 0; j < col_number; j++){
row.push(list_items[i].getValueByIndex(j));
}
data.push(row);
}
SpreadsheetApp.getActiveSheet().getRange(1, 1, data.length, data[0].length).setValues(data);
}
[I do not take credit for writing this!]
So I would like to ask (since this ceases to make much sense to me) is if this is viable code for a Google Apps Script, and if so, how do I implement it to output Sheet data similarly in the same type of format as in the screenshot?
Alternatively, is there a better way to display this Sheet data in Google Sheets?
A totally different alternative would be to use Romain Vialard's "awesome tables" gadget. It works... awesome, and it is really easy to use. Besides, it admits filters, ...