I have written an app script that will interact with the bigQuery API and fetch the bigQuery results.
API is been hit around close to 60-70 times. In this process sometimes I'm getting the following error message:
"JavaScript runtime exited unexpectedly"
could someone help me here?
Let me know if you need any additional information
function fetchTables() {
var timezone = "GMT+" + new Date().getTimezoneOffset()/60;
var date = Utilities.formatDate(new Date(), timezone, "yyyy-MM-dd HH:mm");
Logger.log(date);
var sheet = SpreadsheetApp.getActive().getSheetByName('Tables');
var value = sheet.getRange('A1').getValue();
var projectId = 'corpbi-dev';
var request = {
query: 'select table_id from [corpbi-dev:' + value + '.__TABLES__]'
};
var queryResults = BigQuery.Jobs.query(request, projectId);
var jobId = queryResults.jobReference.jobId;
var rows = queryResults.rows;
if (rows) {
// Append the results.
var data = new Array(rows.length);
for (var i = 0; i < rows.length; i++) {
var cols = rows[i].f;
data[i] = new Array(cols.length);
for (var j = 0; j < cols.length; j++) {
data[i][j] = cols[j].v;
}
}
}
var queries = [];
var totalbytes = [];
var timelapsed = [];
var rowcount = [];
var jobcode = [];
var finalSheet = SpreadsheetApp.getActive().getSheetByName('Final Metrics');
for(var i=1; i<=data.length; i++) {
var sql = "SELECT stage_loading_query FROM (SELECT stage_loading_query, job_start_time FROM [corpbi-dev:etl_log.job_details] WHERE target_table LIKE '%"+ value + "." + data[i-1]+"%' " +
"GROUP BY 1, 2 ORDER BY 2 desc) LIMIT 1"
var request1 = {
query: sql
};
var queryResults1 = BigQuery.Jobs.query(request1, projectId);
var sql1 = queryResults1.rows;
//Logger.log(sql1);
// break;
if (sql1) {
// Append the results.
var data1 = new Array(sql1.length);
for (var k = 0; k < sql1.length; k++) {
var cols = sql1[k].f;
data1[k] = new Array(cols.length);
for (var j = 0; j < cols.length; j++) {
data1[k][j] = cols[j].v;
}
}
}
queries.push(data1);
}
//Logger.log(queries.length);
finalSheet.getRange(2, 1, queries.length).setValues(queries);
// queries = finalSheet.getRange(2, 1, finalSheet.getLastRow()-1).getValues();
var endtime = (new Date()).getTime();
Logger.log(endtime);
for(var i=1; i<=queries.length; i++) {
var sqlstmt = queries[i-1];
try{
var request2 = {
query: sqlstmt,
useLegacySql: true
};
var sleepTimeMs = 50000;
var queryResults2 = BigQuery.Jobs.query(request2, projectId);
var jobId1 = queryResults2.jobReference.jobId;
while (!queryResults2.jobComplete) {
Utilities.sleep(sleepTimeMs);
sleepTimeMs *= 2;
queryResults2 = BigQuery.Jobs.getQueryResults(projectId, jobId1);
//continue;
}
var queryDetails = BigQuery.Jobs.get(projectId, jobId1);
totalbytes.push(queryDetails.statistics.totalBytesProcessed);
timelapsed.push(queryDetails.statistics.totalSlotMs);
rowcount.push(queryResults2.totalRows);
}
catch(e){
try{
var request2 = {
query: sqlstmt,
useLegacySql: false
};
var sleepTimeMs = 50000;
var queryResults2 = BigQuery.Jobs.query(request2, projectId);
var jobId1 = queryResults2.jobReference.jobId;
while(!queryResults2.jobComplete) {
Utilities.sleep(sleepTimeMs);
sleepTimeMs *= 2;
queryResults2 = BigQuery.Jobs.getQueryResults(projectId, jobId1);
// continue;
}
var queryDetails = BigQuery.Jobs.get(projectId, jobId1);
totalbytes.push(queryDetails.statistics.totalBytesProcessed);
timelapsed.push(queryDetails.statistics.totalSlotMs);
rowcount.push(queryResults2.totalRows);
}
catch(e){
Logger.log(e);
}
Logger.log(e);
}
finalSheet.getRange(i+1, 2,).setValue((totalbytes[i-1]/1048576).toFixed(2) + 'MB');
finalSheet.getRange(i+1, 3,).setValue((timelapsed[i-1]/1000).toFixed(2) + 's');
finalSheet.getRange(i+1, 4,).setValue((rowcount[i-1]));
}
}
The V8 Runtime for Google Apps Script is known to be on Alpha stage, which means you may encounter some bugs/errors.
In order to use the stable GAS version, you only need to change the runtimeVersion value in your script's manifest from V8 to STABLE.
Related
I am once in a while getting this error:
Jan 5, 2023, 11:03:48 AM Error Exception: Failed to retrieve form
data. Please wait and try again. at processForm(Code:2:22)
Line 2 is:
var form = FormApp.getActiveForm();
Full script
function processForm(event) {
var form = FormApp.getActiveForm();
Logger.log("Form retrieved " + form)
var allResponses = form.getResponses();
var latestResponse = allResponses[allResponses.length - 1];
var itemResponses = latestResponse.getItemResponses();
//const itemResponses = event.response.getItemResponses();
const propertyAddress = itemResponses[0].getResponse();
Logger.log("Processing address " + propertyAddress);
// Get the number of units. index 5
const moreThan200Units = parseInt(itemResponses[5].getResponse(), 10);
var mainFolder = null;
if(moreThan200Units > 199) {
mainFolder = DriveApp.getFolderById('xx');
} else {
mainFolder = DriveApp.getFolderById('yy');
}
var propertyFolder = null;
for (var i = 0; i < itemResponses.length; i++) {
Logger.log(itemResponses[i].getItem().getType())
if (itemResponses[i].getItem().getType() == "FILE_UPLOAD") {
if(propertyFolder == null) {
propertyFolder = mainFolder.createFolder(propertyAddress);
}
var ids = itemResponses[i].getResponse();
for (var j = 0; j < ids.length; j++) {
var uploadedFile = DriveApp.getFileById(ids[j]);
Logger.log("Moving file" + uploadedFile + " to " + propertyFolder);
uploadedFile.moveTo(propertyFolder);
}
}
}
}
I have created a trigger which will first execute after 5 minutes, the when the trigger function starts executing, the trigger is deleted at first place , then the required code executes, and then new trigger is created which executes in 1 minutes. Also I have a condition where in the trigger is deleted and never created.. but the trigger still continues to execute.
I get total_uploads value as 29393, but it ended up adding nearly 100k records.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Fetch CoA')
.addItem('Fetch CoA', 'main')
.addToUi();
}
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var config = ss.getSheetByName("Config");
var hostname = config.getRange(2, 2).getValue();
var authorization = config.getRange(3, 2).getValue();
var coaID = config.getRange(4, 2).getValue();
var documentProperties = PropertiesService.getDocumentProperties();
documentProperties.setProperties({
'hostname': hostname,
'authorization': authorization,
'coaID': coaID,
'offset': '0',
'is_last_set': 0
});
var coaSheet = ss.getSheetByName("COA");
if (coaSheet.getLastRow() > 1) {
coaSheet.deleteRows(1, coaSheet.getLastRow() - 1);
}
console.log("Getting CoA Validaton");
writeCOA();
}
function writeCOA() {
var documentProperties = PropertiesService.getDocumentProperties();
var hostname = documentProperties.getProperty('hostname');
var authorization = documentProperties.getProperty('authorization');
var coaID = documentProperties.getProperty('coaID');
var offset = parseInt(documentProperties.getProperty('offset'));
var is_last_set = parseInt(documentProperties.getProperty('is_last_set'));
var options = {
"method": "GET",
"headers": {
"Authorization": "Token token=" + authorization
}
};
var today = new Date();
console.log("Writing the uploads array");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var coaSheet = ss.getSheetByName("COA");
coaSheet.getRange("A1:D1").setValues([["id", "account_number", "created_at", "updated_at"]]);
var rowNum = 2;
var uploads = [];
var next_page = true;
var total_uploads = "...";
console.log("Getting all Account numbers for CoA: " + coaID);
while (next_page) {
documentProperties.setProperty('next_page', next_page);
if (isTimeUp(today) && is_last_set == 0) {
console.log("RepeatWriteCoA triggered");
// schedule a trigger for a different function
ScriptApp.newTrigger("repeatWriteCoA")
.timeBased()
.everyMinutes(5)
.create();
break;
}
console.log(`Getting uploads ${offset} to ${offset + 500} of ${total_uploads}`);
var platformUrl = `${hostname}/api/v3/chart_of_accounts/${coaID}/account_numbers?limit=500&offset=${offset}`;
//console.log(platformUrl);
var response = UrlFetchApp.fetch(platformUrl, options);
var jsonResponse = response.getContentText();
var page_of_uploads = JSON.parse(jsonResponse);
total_uploads = page_of_uploads['meta']['total'];
documentProperties.setProperty('total_uploads', total_uploads);
uploads = page_of_uploads['account_numbers'];
try {
offset = page_of_uploads['meta']['next']['offset'];
documentProperties.setProperty('offset', offset);
} catch (Exception) {
next_page = false;
documentProperties.setProperty('next_page', next_page);
console.log("Exception in getting the next page offset hence stopping the loop");
deleteTriggers();
// For last set of records that needs to added in our spreadsheet
console.log("Adding Last set of Records..");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of Last set of Records..");
is_last_set = 1;
}
console.log("Adding to spreadsheet");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of adding rows");
if (offset >= total_uploads) {
deleteTriggers();
}
}
documentProperties.setProperty('is_last_set', is_last_set);
}
function repeatWriteCoA() {
console.log("Triggered RepeatFunction... Running from Repeat function");
// Delete trigger , so that new function wont be trigger untill this function ends
deleteTriggers();
var documentProperties = PropertiesService.getDocumentProperties();
var hostname = documentProperties.getProperty('hostname');
var authorization = documentProperties.getProperty('authorization');
var coaID = documentProperties.getProperty('coaID');
var offset = parseInt(documentProperties.getProperty('offset'));
var next_page = documentProperties.getProperty('next_page');
var rowNum = parseInt(documentProperties.getProperty('rowNum'));
var total_uploads = parseInt(documentProperties.getProperty('total_uploads'));
var options = {
"method": "GET",
"headers": {
"Authorization": "Token token=" + authorization
}
};
var today = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var coaSheet = ss.getSheetByName("COA");
var is_last_set = parseInt(documentProperties.getProperty('is_last_set'));
console.log("Getting all Account numbers for CoA: " + coaID);
while (next_page) {
if (is_last_set == 1) {
console.log('last set already executed');
break;
}
console.log(`Getting uploads ${offset} to ${offset + 500} of ${total_uploads}`);
var platformUrl = `${hostname}/api/v3/chart_of_accounts/${coaID}/account_numbers?limit=500&offset=${offset}`;
var response = UrlFetchApp.fetch(platformUrl, options);
var jsonResponse = response.getContentText();
var page_of_uploads = JSON.parse(jsonResponse);
total_uploads = page_of_uploads['meta']['total'];
uploads = page_of_uploads['account_numbers'];
try {
offset = page_of_uploads['meta']['next']['offset'];
documentProperties.setProperty('offset', offset);
} catch (Exception) {
next_page = false;
documentProperties.setProperty('next_page', next_page);
console.log("Exception in getting the next page offset hence stopping the loop");
// For last set of records that needs to added in our spreadsheet
console.log("Adding Last set of Records..");
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
console.log("End of Last set of Records..");
is_last_set = 1;
}
for (i = 0; i < uploads.length; i++) {
acc_number = uploads[i].account_number.replace(/[^a-zA-Z0-9\s\p{P}]/g, '-');
coaSheet.getRange("A" + rowNum + ":D" + rowNum).setValues([[uploads[i].id, acc_number, uploads[i].created_at, uploads[i].updated_at]]);
rowNum += 1;
documentProperties.setProperty('rowNum', rowNum);
}
// Creating new trigger so that it will trigger this function next time ,
// this time we are creating the trigger with 1 min
if (offset <= total_uploads && isTimeUp(today) && is_last_set == 0) {
console.log("RepeatWriteCoA triggered");
// schedule a trigger for a different function
ScriptApp.newTrigger("repeatWriteCoA")
.timeBased()
.everyMinutes(1)
.create();
break;
}
}
documentProperties.setProperty('is_last_set', is_last_set);
}
function isTimeUp(today) {
var now = new Date();
console.log(now.getTime() - today.getTime());
return now.getTime() - today.getTime() > 240000;
// 240000 = 4 mins; this is the threshold limit
// you are free to setup your own threshold limit
}
function deleteTriggers() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
if (allTriggers.length > 0) {
console.log("Deleting trigger....");
for (var i = 0; i < allTriggers.length; i++) {
// If the current trigger is the correct one, delete it.
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
}
After getting interested in a code proposed on Github to get WooCommerce orders in Google Sheets: https://github.com/mithunmanohar/woocommerce-orders-google-sheets-integration
I integrated this code in a sheet with its script but this code has a defect: when updating the status of an order the script adds an extra line instead of changing the status of the line corresponding to the order.
We get a duplicate as you can see here :
Same ID but statut different
You can find the complete code of the script here: https://github.com/mithunmanohar/woocommerce-orders-google-sheets-integration/blob/master/wc-gc-integration.gs
// Updated code to v2 Woocommerce API
function start_syncv2() {
var sheet_name = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
fetch_orders(sheet_name)
}
function fetch_orders(sheet_name) {
var ck = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name).getRange("B4").getValue();
var cs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name).getRange("B5").getValue();
var website = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name).getRange("B3").getValue();
var manualDate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name).getRange("B6").getValue(); // Set your order start date in spreadsheet in cell B6
var m = new Date(manualDate).toISOString();
var surl = website + "/wp-json/wc/v2/orders?consumer_key=" + ck + "&consumer_secret=" + cs + "&after=" + m + "&per_page=100";
var url = surl
//Logger.log(url)
var options =
{
"method": "GET",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"muteHttpExceptions": true,
};
var result = UrlFetchApp.fetch(url, options);
Logger.log(result.getResponseCode())
if (result.getResponseCode() == 200) {
var params = JSON.parse(result.getContentText());
//Logger.log(params);
}
var doc = SpreadsheetApp.getActiveSpreadsheet();
var temp = doc.getSheetByName(sheet_name);
var consumption = {};
var arrayLength = params.length;
for (var i = 0; i < arrayLength; i++) {
var a, c, d;
var container = [];
a = container.push(params[i]["billing"]["first_name"]);
a = container.push(params[i]["billing"]["last_name"]);
a = container.push(params[i]["billing"]["address_1"]+ " "+ params[i]["billing"]["postcode"]+ " "+ params[i]["billing"]["city"]);
a = container.push(params[i]["shipping"]["first_name"] + " "+ params[i]["shipping"]["last_name"]+" "+ params[i]["shipping"]["address_2"]+" "+ params[i]["shipping"]["address_1"]+" "+params[i]["shipping"]["postcode"]+" "+params[i]["shipping"]["city"]+" "+params[i]["shipping"]["state"]+" "+params[i]["shipping"]["country"]);
a = container.push(params[i]["billing"]["phone"]);
a = container.push(params[i]["billing"]["email"]);
a = container.push(params[i]["customer_note"]);
a = container.push(params[i]["payment_method_title"]);
c = params[i]["line_items"].length;
var items = "";
var total_line_items_quantity = 0;
for (var k = 0; k < c; k++) {
var item, item_f, qty, meta;
item = params[i]["line_items"][k]["name"];
qty = params[i]["line_items"][k]["quantity"];
item_f = qty + " x " + item;
items = items + item_f + ",\n";
total_line_items_quantity += qty;
}
a = container.push(items);
a = container.push(total_line_items_quantity); // Quantity
a = container.push(params[i]["total"]); //Price
a = container.push(params[i]["discount_total"]); // Discount
d = params[i]["refunds"].length;
var refundItems = "";
var refundValue = 0.0;
for (var r = 0; r < d; r++) {
var item, item_f, value;
item = params[i]["refunds"][r]["reason"];
value = params[i]["refunds"][r]["total"];
refundValue += parseFloat(value);
item_f = value +" - "+ item;
refundItems += item_f + ",\n";
}
a = container.push(refundValue); //Refunded value from order
a = container.push(parseFloat(container[10]) + refundValue); // Total minus refund
a = container.push(refundItems); //Refunded items from order
a = container.push(params[i]["id"]);
a = container.push(params[i]["date_created"]);
a = container.push(params[i]["date_modified"]);
a = container.push(params[i]["status"]);
a = container.push(params[i]["order_key"]);
var doc = SpreadsheetApp.getActiveSpreadsheet();
var temp = doc.getSheetByName(sheet_name);
temp.appendRow(container);
Logger.log(params[i]);
removeDuplicates(sheet_name);
}
}
function removeDuplicates(sheet_name) {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getSheetByName(sheet_name);
var data = sheet.getDataRange().getValues();
var newData = new Array();
for (i in data) {
var row = data[i];
/* TODO feature enhancement in de-duplication
var date_modified =row[row.length-2];
var order_key = row[row.length];
var existingDataSearchParam = order_key + "/" + date_modified;
*/
var duplicate = false;
for (j in newData) {
var rowNewData = newData[j];
var new_date_modified =rowNewData[rowNewData.length-2];
var new_order_key = rowNewData[rowNewData.length];
//var newDataSearchParam = new_order_key + "/" + new_date_modified; // TODO feature enhancement in de-duplication
if(row.join() == newData[j].join()) {
duplicate = true;
}
// TODO feature enhancement in de-duplication
/*if (existingDataSearchParam == newDataSearchParam){
duplicate = true;
}*/
}
if (!duplicate) {
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
I was trying to use "Google Apps Script Quickstart" to take attendance on Meet. This works well, but it does not identify the specific range for the startTime or endTime of a Meet, so I added the startTime.
With much support from stackoverflow users, the code developed allows teachers and other education staff to take attendance for a meet session within a specific time frame for a specific set of Meet codes.
Chrome extensions require adding things to your computer, demanding time to administer the data that is obtained, and can only be accessed for the purpose of taking attendance during the actual session.
var emailAddress = sheet[i+1][1]
var optionalArgs = {
startTime: formatStartDate(),
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
function formatStartDate(){
let date = new Date();
const offset = date.getTimezoneOffset();
let offsetDate = new Date(date.getTime() - (offset*60*1000));
return offsetDate.toISOString();
}
...
The full "Google Apps Script Quickstart" code plus the data above looks like this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Asistencia')
.addItem("Cursos", 'importCourses')
.addItem('Asistencia', 'checkAll')
.addToUi();
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
for (var i = 0; i < 2; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
if (meetCode == null) {
break;
}
else {
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
var DateValue = ss.getRange(1, 1).getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
startTime: formatStartDate(),
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName,
optionalArgs);
var activities = response.items;
if (activities == undefined) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace(/-/g, "");
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("A");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("");
}
function formatStartDate(){
let date = new Date();
const offset = date.getTimezoneOffset();
let offsetDate = new Date(date.getTime() - (offset*145*1000));
return offsetDate.toISOString();
}
I would be very appreciative of any input. Thank you;
I'm using the G Suite Developer Quickstart for a Meet attendance script
https://developers.google.com/gsuite/add-ons/editors/sheets/quickstart/attendance
Unfortunately, upon using the provided code, and having made adjustments, I either always get "Absent" or "Present" for all students (regardless of whether they were present or not) depending on if I use "null" of "false" in an if statement for activities for applicationName 'meet'.
Here is the code I'm using as a superuser:
...
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Opciones SG')
.addItem("Importar Curso", 'importCourses')
.addItem('Verificar Asistencia', 'checkAll')
.addToUi();
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list();
var courses = response.courses;
for (var i = 0; i < 1; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == false) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace("/-/g","")
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Absent");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Present");
}
...
For the correct functionality of the code, check for if (activities == null) or if (activities == undefined) - NOT if (activities == false)
If there are no response items (that is no items have been found for a given user and meeting code) - activities will be undefined, but not false.
if (activities == false) will never be fulfilled and if you use it - all participants will be marked as present
On the other hand, if activities == undefined for other reasons - for example because you introduced the wrong meet code (don't forget to replace 'Asistencia' through a valid meet code), all participants will be marked as absent
Note that the Reports API has some delay, so do not expect to retrieve participation data in live time.
If all participants are marked as absent - probably the data did not propagate yet, wait some time and try again.
Make sure you pass the correct meet code to the script. Your meet code should look something like xxx-xxxx-xxx.
The script needs to remove the - for correct functionality. For this please change in function getCleanCode(meetCode) the line return meetCode.replace("/-/g","") to return meetCode.replace(/-/g, "");
As for the question in your comment: here you can see all available Hangouts Meet Audit Activity Events that you can use for filtering instead of the meeting_code. Unfortunately the timestamp is not one of possible query parameters.
Here is the code we have been using for a few months now, no glitches:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Asistencia')
.addItem('Asistencia', 'checkAll')
.addItem('*Crear*', 'createNewSheets')
.addToUi();
}
function createNewSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("A:A");
var employeeObjects = employeeDataRange.getValues();
var template = ss.getSheetByName('Template');
for (var i=0; i < employeeObjects.length; i++) {
// Put the sheet you want to create in a variable
var sheet = ss.getSheetByName(employeeObjects[i]);
// Check if the sheet you want to create already exists. If so,
// log this and loop back. If not, create the new sheet.
if (sheet) {
Logger.log("Sheet " + employeeObjects[i] + "already exists");
} else {
template.copyTo(ss).setName(employeeObjects[i]);
}
}
return;
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
for (var i = 0; i < 2; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
//let today = new Date()
//var staringTime = new Date().toISOString();
// var DateValue = ss.getRange(1, 1).getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
startTime: formatStartDate(),
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == undefined) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace(/-/g, "");
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("A");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("");
}
function formatStartDate(){
let date = new Date();
const offset = date.getTimezoneOffset();
let offsetDate = new Date(date.getTime() - (offset*145*1000));
return offsetDate.toISOString();
}