Uploading Google Spreadsheet to a Google Site - google-apps-script

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, ...

Related

This script does not populate sheet after parsing retrieved data

I hope this is well explained. First of all, sorry because my coding background is zero and I am just trying to "fix" a previously written script.
Problem The script does not populate sheet after parsing retrieved data if the function is triggered by timer and the sheet is not open in my browser .
The script works OK if run it manually while sheet is open.
Problem details:
When I open the sheet the cells are stuck showing "Loading" and after a short time, data is written.
Expected behavior is to get the data written no matter if I don't open the sheet.
Additional info: This is how I manually run the function
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [
{name: "Manual Push Report", functionName: "runTool"}
];
sheet.addMenu("PageSpeed Menu", entries);
}
Additional info: I set the triggers with Google Apps Script GUI See the trigger
Before posting the script code, you can see how the cells look in the sheet:
Script code
function runTool() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Results");
var rows = activeSheet.getLastRow();
for(var i=3; i <= rows; i++){
var workingCell = activeSheet.getRange(i, 2).getValue();
var stuff = "=runCheck"
if(workingCell != ""){
activeSheet.getRange(i, 3).setFormulaR1C1(stuff + "(R[0]C[-1])");
}
}
}
// URL check //
function runCheck(Url) {
var key = "XXXX Google PageSpeed API Key";
var strategy = "desktop"
var serviceUrl = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=" + Url + "&key=" + key + "&strategy=" + strategy +"";
var array = [];
var response = UrlFetchApp.fetch(serviceUrl);
if (response.getResponseCode() == 200) {
var content = JSON.parse(response.getContentText());
if ((content != null) && (content["lighthouseResult"] != null)) {
if (content["captchaResult"]) {
var score = content["lighthouseResult"]["categories"]["performance"]["score"];
} else {
var score = "An error occured";
}
}
array.push([score,"complete"]);
Utilities.sleep(1000);
return array;
}
}
You can try the code using the sheet below with a valid Pagespeed API key.
You only need to add a Trigger and wait for it's execution while the sheet is not open in your browser
https://docs.google.com/spreadsheets/d/1ED2u3bKpS0vaJdlCwsLOrZTp5U0_T8nZkmFHVluNvKY/copy
I suggest you to change your algorithm. Instead of using a custom function to call UrlFetchApp, do that call in the function called by a time-driven trigger.
You could keep your runCheck as is, just replace
activeSheet.getRange(i, 3).setFormulaR1C1(stuff + "(R[0]C[-1])");
by
activeSheet.getRange(i, 3, 1, 2).setValues(runCheck(url));
NOTE
Custom functions are calculated when the spreadsheet is opened and when its arguments changes while the spreadsheet is open.
Related
Cache custom function result between spreadsheet opens

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

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!

Google slides auto update links /tables linked from google sheet

I have a Google Slides presentation that has some linked cells/table to data in Google Sheets. Currently, I have to manually click each linked cell/table to update values.
I need a script for Google Slides that would auto-update / batch-update / refresh these links, so that the values/tables get auto-updated. Is that possible?
Both yes and no.
Charts
Yes! those can be batch updated:
function onOpen() {
SlidesApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Update Charts')
.addItem("Update now !!!!!", 'refreshCharts').addToUi();
}
function refreshCharts(){
var gotSlides = SlidesApp.getActivePresentation().getSlides();
for (var i = 0; i < gotSlides.length; i++) {
var slide = gotSlides[i];
var sheetsCharts = slide.getSheetsCharts();
for (var k = 0; k < sheetsCharts.length; k++) {
var shChart = sheetsCharts[k];
shChart.refresh();
}
}
}
Source: https://stackoverflow.com/a/48254442/
Shapes/Tables
No: https://issuetracker.google.com/issues/64027131
Update From Google 24/5/2019
https://gsuiteupdates.googleblog.com/2019/05/bulk-update-docs-slides.html
Has anyone gotten this to work?
function refreshCharts(){
var gotSlides = SlidesApp.getActivePresentation().getSlides();
for (var i = 0; i < gotSlides.length; i++) {
var slide = gotSlides[i];
var sheetsCharts = slide.getSheetsCharts();
for (var k = 0; k < sheetsCharts.length; k++) {
var shChart = sheetsCharts[k];
shChart.refresh();
}
}
}
I just get this error message
"
Unable to refresh chart. Please verify that the chart is a valid chart in Google Sheets."
The following example setup assumes there are five slides with some linked charts:
first slide - main slide title/subtitle (no chart)
second slide - contains a chart linked from the chart of a pivot table that counts the R&D staff and what they planned to do from the Google Form responses sheet
third slide - contains a chart linked from the chart of a pivot table that counts the IME staff and what they planned to do from the Google Form responses sheet
fourth slide - contains a chart linked from the chart of a pivot table that counts the PMO staff and what they planned to do from the Google Form responses sheet
fifth slide - contains a chart linked from the chart of a pivot table that counts the total staff and what they planned to do from the Google Form responses sheet
The function below will update the slides with linked charts.
For it to work, it requires the Advanced Google Services: Google Slides API https://developers.google.com/slides/.
You can activate this advanced feature from your Google Apps Script IDE under Resources > Advanced Google Services... > Google Slides API. The API version is set to v1. If you don't enable it, the script will complain Slides is not defined at updateSlideCharts(...)
See the link below for more detail about RefreshSheetsChartRequest in Google Slides API v1: https://developers.google.com/slides/reference/rest/v1/presentations/request#RefreshSheetsChartRequest
function updateSlideCharts() {
var presentation = SlidesApp.openById(YOUR_SLIDE_ID); //you need to get this slide id from your slide URL
//if empty
if (presentation == null) throw new Error('Presentation was not found');
Logger.log("%s id = %s", presentation.getName(), presentation.getId());
var slides = presentation.getSlides();
if (slides == null) throw new Error('Slides were not found');
Logger.log("Total of slides in %s: %d", presentation.getName(), slides.length);
var presentationId = presentation.getId();
var presentationRndChartId = slides[1].getSheetsCharts()[0].getObjectId();
Logger.log("ObjectId of \"%s\": %s", slides[1].getSheetsCharts()[0].getTitle(), presentationRndChartId);
var presentationImeChartId = slides[2].getSheetsCharts()[0].getObjectId();
Logger.log("ObjectId of \"%s\": %s", slides[2].getSheetsCharts()[0].getTitle(), presentationImeChartId);
var presentationPmoChartId = slides[3].getSheetsCharts()[0].getObjectId();
Logger.log("ObjectId of \"%s\": %s", slides[3].getSheetsCharts()[0].getTitle(), presentationPmoChartId);
var presentationStaffChartId = slides[4].getSheetsCharts()[0].getObjectId();
Logger.log("ObjectId of \"%s\": %s", slides[4].getSheetsCharts()[0].getTitle(), presentationStaffChartId);
var requests = [{
refreshSheetsChart: {
objectId: presentationRndChartId
}
}];
// Execute the request.
var batchUpdateResponse = Slides.Presentations.batchUpdate({
requests: requests
}, presentationId);
Logger.log('Refreshed linked Sheets charts for \"%s\"', slides[1].getSheetsCharts()[0].getTitle());
requests = [{
refreshSheetsChart: {
objectId: presentationImeChartId
}
}];
// Execute the request.
batchUpdateResponse = Slides.Presentations.batchUpdate({
requests: requests
}, presentationId);
Logger.log('Refreshed linked Sheets charts for \"%s\"', slides[2].getSheetsCharts()[0].getTitle());
requests = [{
refreshSheetsChart: {
objectId: presentationPmoChartId
}
}];
// Execute the request.
batchUpdateResponse = Slides.Presentations.batchUpdate({
requests: requests
}, presentationId);
Logger.log('Refreshed linked Sheets charts for \"%s\"', slides[3].getSheetsCharts()[0].getTitle());
var requests = [{
refreshSheetsChart: {
objectId: presentationStaffChartId
}
}];
// Execute the request.
var batchUpdateResponse = Slides.Presentations.batchUpdate({
requests: requests
}, presentationId);
Logger.log('Refreshed linked Sheets charts for \"%s\"', slides[4].getSheetsCharts()[0].getTitle());
}
Linked Table
As of Aug 2021, there is still no .refresh() function for linked tables, but if your use case allows you to know the source spreadsheet and range at script writing, you can update a linked table by reading the text values, font colors, font styles, etc., from the spreadsheet and writing them to the table. Something like this:
function updateSheetsChart()
{
// The range on the source spreadsheet
var sourceRange = SpreadsheetApp.openById(SOURCE_SPREADSHEET_ID).getRange(SOURCE_TABLE_RANGE)
var source = {
'values': sourceRange.getDisplayValues(),
'backgrounds': sourceRange.getBackgrounds(),
'textStyles': sourceRange.getTextStyles(),
'fontColors': sourceRange.getFontColors()
}
// The linked table on the presentation
var table = SlidesApp.getActivePresentation().getPageElementById(SHEETS_TABLE_OBJECT_ID).asTable()
var columnCount = table.getNumColumns()
var rowCount = table.getNumRows()
for (var col = 0; col < columnCount; col++)
{
for (var row = 0; row < rowCount; row++)
{
var cell = table.getCell(row, col)
// Cell text
var cellText = cell.getText()
cellText.setText(source.values[row][col])
// Background color
cell.getFill().setSolidFill(source.backgrounds[row][col])
// Font style (bold)
var cellTextStyle = cellText.getTextStyle()
cellTextStyle.setBold(source.textStyles[row][col].isBold())
// Text color
cellTextStyle.setForegroundColor(source.fontColors[row][col])
}
}
}
You can add more lines for font size, italics, link URLs, etc. See the documentation for Range, Sheets TextStyle, and Slides TextStyle classes for the corresponding methods.
There are plenty of limitations: this won't copy full rich text (multiple colors or font styles in one cell), for example.
Also see this answer for a simpler workaround (but it doesn't transfer the cell formatting).

get the form attached to a spreasheet

I'm working with the "new" version of google SS.
I'd like to get the form attached to the spreadsheet I'm in, like this:
function findFormURL() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
return ss.getFormUrl();
}
However, this function does not work yet in the new version.
Anyway,it gives the URL of the form, which is interesting, but I'd like to have the form ID or object so I can then work with it, change some stuff etc. Is that possible ?
This is indeed annoying but there is a possible way to get around this missing feature using the drive search capabilities... I tested it with the code below and it worked.
I agree that this is far from ideal and requires to have a form that has the same unique name as your spreadsheet but it's better than nothing.
function getFormTest() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var nameToSearch = ss.getName();
Logger.log('title contains "'+nameToSearch+'"');
var forms = DriveApp.searchFiles('title contains "'+nameToSearch+'"');
while (forms.hasNext()){
var formDoc = forms.next();
Logger.log(formDoc.getMimeType());
if(formDoc.getMimeType()=='application/vnd.google-apps.form'){
break;
}
}
Logger.log('formDoc = '+formDoc);
var form = FormApp.openById(formDoc.getId());
var items = form.getItems();
for(var i in items){
Logger.log(items[i].getTitle()+' '+items[i].getType());
}
}