Google Drive spreadsheet script issue - javascript

I copied this function from somewhere on the web. My goal is to import the body of specifically labeled emails in my gmail account to a google spreadsheet. While I'm not completely inept when it comes to coding, I'm not familiar with this stuff.
Some details that may be relevant: Each of these Emails I am trying to import are NOT conversations, they are a single email received, no response and no earlier messages in the thread. I want the entire body of each Email placed in a single cell in the spreadsheet.
As it is, only the subject is being placed in my spreadsheet. How can I get it to bring the body, as well? I feel like it's being placed in the array, but when looping the setValue it gets skipped.
Much love, folks!
Code:
function getMessagesWithLabel() {
var destArray = new Array();
var threads = GmailApp.getUserLabelByName('abc').getThreads(1,10);
for(var n in threads){
var msg = threads[n].getMessages();
var destArrayRow = new Array();
destArrayRow.push('thread has '+threads[n].getMessageCount()+' messages');
for(var m in msg){
destArrayRow.push(msg[m].getSubject());
}
destArray.push(destArrayRow);
}
Logger.log(destArray);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
if(ss.getLastRow()==0){sh.getRange(1,1).setValue('getMessagesWithLabel() RESULTS')};
sh.getRange(ss.getLastRow()+1,1,destArray.length,destArray[0].length).setValues(destArray)
}

You're not employing .getBody() anywhere in your script? In a case like this I'd store each message as an object with subject and body as separate values:
var msg = threads[n].getMessages();
var contentArray = [];
for(var i = 0; i < msg.length; i++){
var obj = {
subject: msg[i].getSubject(),
body: msg[i].getBody()
};
contentArray.push(obj);
}
These values can then be iterated through using the following notation:
console.log(contentArray[0].subject);
console.log(contentArray[0].body);

Related

Exception: Document is missing (perhaps it was deleted, or you don't have read access?)

I'm working on a project that take "profiles" stored in a Google Sheet, makes a unique Google Doc for each profile, and then updates the unique Google Doc with any new information when you push a button on the Google Sheet.
I have some other automations built into my original code, but I simplified most of it to what's pertinent to the error I'm getting, which is this:
Exception: Document is missing (perhaps it was deleted, or you don't have read access?
It happens on Line 52 of my script in the fileUpdate funtion. Here's the appropriate line for reference:
var file = DocumentApp.openById(fileName);
And this is the rest of my code:
function manageFiles() {
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
for (var i = 1; i < arrayL; i++) {
if (array[i][arrayW-2] == "") {
//Collect the data from the current sheet.
//Create the document and retrieve some information from it.
var docTitle = array[i , 0]
var doc = DocumentApp.create(docTitle);
var docBody = doc.getBody();
var docLink = doc.getUrl();
//Use a for function to collect the unique data from each cell in the row.
docBody.insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[i][arrayW-j];
if (colName !== "Filed?") {
docBody.insertParagraph(0 , colName+": "+data);
}
}
//Insert a hyperlink to the file in the cell containing the SID
sheet.getRange(i+1 , 1).setFormula('=HYPERLINK("'+docLink+'", "'+SID+'")');
//Insert a checkbox and check it.
sheet.getRange(i+1 , arrayW-1).insertCheckboxes();
sheet.getRange(i+1 , arrayW-1).setFormula('=TRUE');
}
else if (array[i][arrayW-2] !== "") {
updateFiles(i);
}
}
sheet.getRange(1 , arrayW).setValue('Last Update: '+date);
}
//Note: I hate how cluttered updateFiles is. I'm going to clean it up later.
function fileUpdate(rowNum) {
//now you do the whole thing over again from createFiles()
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
//Collect the data from the current sheet.
var fileName = array[rowNum][0];
var file = DocumentApp.openById(fileName);
//retrieve the body of the document and clear the text, making it blank.
file.getBody().setText("");
//Use a for function to collect the the unique date from every non-blank cell in the row.
file.getBody().insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[rowNum][arrayW-j];
file.getBody().insertParagraph(0 , colName+": "+data);
}
}
If you'd like to take a look at my sample spreadsheet, you can see it here. I suggest you make a copy though, because you won't have permissions to the Google Docs my script created.
I've looked at some other forums with this same error and tried several of the prescribed solutions (signing out of other Google Accounts, clearing my cookies, completing the URL with a backslash, widening permissions to everyone with the link), but to no avail.
**Note to anyone offended by my janky code or formatting: I'm self-taught, so I do apologize if my work is difficult to read.
The problem (in the updated code attached to your sheet) comes from your URL
Side Note:
In your initial question, you define DocumentApp.openById(fileName);
I assume your realized that this is not correct, since you updated
your code to DocumentApp.openByUrl(docURL);, so I will discuss the
problem of the latter in the following.
The URLs in your sheet are of the form
https://docs.google.com/open?id=1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w
while DocumentApp.openByUrl expects a URL of form
https://docs.google.com/document/d/1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w/
Just adding a / is not enough!
Either create the expected URL manually, or - much easier / use the method DocumentApp.openById(id) instead.
For this, you can extract the id from your URL as following:
var id = docURL.split("https://docs.google.com/open?id=")[1];
var file = DocumentApp.openById(id)

Google script that already fetches email infos but needs attachment name

CONTEXT
I'm using a script to fetch emails that I found here (from Niclas, I think): fetching emails script
I've adapted it to my needs and it works very well!
WHAT I'VE TESTED
I saw getDescription() from the Google script Class Attachment but couldn't get it to work and I'm not even sure if that's the way to go
WHAT I WOULD WANT
In addition, I would like to fetch the attachments filenames since It's a distinctive mark on each email
Any help would me very appreciated. Thanks in advance
Modifications:
Do the following modifications to the answer of the post you mentioned in your question:
Add these lines:
var attachments = messages[maxIndex].getAttachments();
var attNames = attachments.map(att=>att.getName());
and modify this one:
ss.appendRow([from, cc, time, sub,...attNames ,'https://mail.google.com/mail/u/0/#inbox/'+mId])
Solution:
function myFunction() {
// Use sheet
var ss = SpreadsheetApp.getActiveSheet();
// Gmail query
var query = "label:support -label:trash -label:support-done -from:me";
// Search in Gmail, bind to array
var threads = GmailApp.search(query);
// Loop through query results
for (var i = 0; i < threads.length; i++)
{
// Get messages in thread, add to array
var messages = threads[i].getMessages();
// Used to find max index in array
var max = messages[0];
var maxIndex = 0;
// Loop through array to find maxIndexD = most recent mail
for (var j = 0; j < messages.length; j++) {
if (messages[j] > max) {
maxIndex = j;
max = messages[j];
}
}
// Find data
var mId = messages[maxIndex].getId() // ID used to create mail link
var from = messages[maxIndex].getFrom();
var cc = messages[maxIndex].getCc();
var time = threads[i].getLastMessageDate()
var sub = messages[maxIndex].getSubject();
var attachments = messages[maxIndex].getAttachments();
var attNames = attachments.map(att=>att.getName());
// Write data to sheet
ss.appendRow([from, cc, time, sub,...attNames ,'https://mail.google.com/mail/u/0/#inbox/'+mId])
}
}
Don't forget to change the value of query to your needs.
References:
Class GmailMessage
map()
Rest parameters
You have to enable V8 runtime to be able to use the snippet.

Save a json/csv file (fetched from an API) to a spreadsheet document without using a loop - Google Apps Script

I can fetch the data using two different urls depending whether I want to file to be csv or json.
CSV edition
In the case of the csv url I do the following:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("data")
get_url="https://opendata.rdw.nl/resource/m9d7-ebf2.csv?
datum_tenaamstelling=20200404%20&$limit=20000";
var result_data = UrlFetchApp.fetch(get_url);
var raw_data = result_data.getContentText()
Logger.log(raw_data);
I end up with a string that I can not store it to the spreadsheet file without using a loop.
JSON edition
Using the json url, I am doing the following:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("data")
get_url="https://opendata.rdw.nl/resource/m9d7-ebf2.json?
datum_tenaamstelling=20200404%20&$limit=20000";
var result_data = UrlFetchApp.fetch(get_url, options);
var raw_data = result_data.getContentText()
var json_list = JSON.parse(raw_data)
for(var i = 0; i < json_list.length; i++) {
var obj = json_list[i];
Logger.log(obj.merk);
sheet.appendRow([obj.kenteken,
obj.merk,
obj.catalogusprijs,
obj.datum_eerste_afgifte_nederland,
obj.datum_eerste_toelating,
obj.datum_tenaamstelling
]);
}
Which again I end up using an iteration which is not practical if I want to store 20000 rows to the spreadsheet. After sometime it stops and returns a runtime error.
Is there any way I can directly copy the data to the spreadsheet file?
For example to do something like that:
sheet.getRange(1, 1,raw_data.length,raw_data[0].length).setValues(raw_data);
How to store specific from a JSON object response
Looping through all data and call the method sheet.appendRow() during each iteration would indeed be slow and inefficient due to the number of calls - see Apps Script Best Practices.
A significantly more efficient way to do it, would be to create an array to store the data of interest before writing it a single time with setValues().
The sample below still incorporates a for loop, however given that no calls to external services are made during each iteration, the whole script took only around 10 s to execute and write 12582 rows of data into the sheet.
Sample:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("data")
get_url="https://opendata.rdw.nl/resource/m9d7-ebf2.json?datum_tenaamstelling=20200404%20&$limit=20000";
var result_data = UrlFetchApp.fetch(get_url);
var raw_data = result_data.getContentText()
var json_list = JSON.parse(raw_data)
var array = [];
for(var i = 0; i < json_list.length; i++) {
var obj = json_list[i];
array.push([obj.kenteken,
obj.merk,
obj.catalogusprijs,
obj.datum_eerste_afgifte_nederland,
obj.datum_eerste_toelating,
obj.datum_tenaamstelling
]);
}
sheet.getRange(1, 1,array.length,array[0].length).setValues(array);
}

Looking for one Google Script App for 4 sheet tabs to produce one json

Ok...not sure how to do this. Right now I 4 sheets and 4 scripts for each sheet producing 4 json feeds. What I am trying to experiment with is having one script that will produce 1 json that I can use in a web page and just call the type of class. They are all formatted the same with columns etc.
Here is the Google Script App code I have.
function doGet(e){
// Change Spread Sheet url
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/SpreadsheetID/edit#gid=0");
// Sheet Name, Change Sheet to whatever name you used to name your sheet
var sheet = ss.getSheetByName("Class Sheet 1");
return getClasses(sheet);
}
function getClasses(sheet){
var dataObj = {};
var dataArray = [];
// collecting data starting from 2nd Row , 1st column to last row and last column
var rows = sheet.getRange(2,1,sheet.getLastRow()-1, sheet.getLastColumn()).sort([{column: 1, ascending: true}, 1]).getValues();
for(var i = 0, l= rows.length; i<l ; i++){
var dataRow = rows[i];
var record = {};
record['Name'] = dataRow[0];
record['Note'] = dataRow[1];
record['Address'] = dataRow[2];
record['StreetAddress'] = dataRow[3];
record['City'] = dataRow[4];
record['State'] = dataRow[5];
record['ZipCode'] = dataRow[6];
record['ContactName'] = dataRow[7];
record['EMailAddress'] = dataRow[8];
record['CustomerServicePhone'] = dataRow[9];
dataArray.push(record);
}
dataObj = dataArray;
var result = JSON.stringify(dataObj);
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.JSON);
}
Scratching my head on this a little bit....I'm sure its something simple and I am probably over thinking things, but any help would be appreciated.
Possible Solution:
The e object in your doGet(e) provides a way to send parameters to your script. You can access different sheets with different url parameters. You can then easily get the requested SheetName through e.parameter. Use
https://script.google.com/.../exec?sheet=ClassSheet1 //for ClassSheet1
https://script.google.com/.../exec?sheet=ClassSheet2 //for ClassSheet2
Code.gs:
function doGet(e){
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/SpreadsheetID/edit#gid=0");
var sheetName = e.parameter.sheet;
var sheet = ss.getSheetByName(sheetName);
return getClasses(sheet);
}
You can also provide UI in your web-app to select a sheet.

Use Google Apps Script functions in project

I am very new to Google Apps Scripts and am curious how I can use functions created in my own project. For example, I have a script bound to a spreadsheet with just one function:
function addOrder(title, content) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow([ Date(), title, content]);
}
It simply takes 2 arguments and adds a row to the spreadsheet with that data. I have deployed it as a web app, but I'm not sure how to use this function in an environment like JSFiddle. Any help is appreciated.
Thanks
Spreadsheet bound scripts run server side and the SpreadsheetApp.getActiveSheet() method you are using will only work in the context of a spreadsheet bound script since it's the only case where the script actually "sees" an active spreadsheet. When you deploy this as a webapp you will have to tell the script which spreadsheet it must look at using for example the SpreadsheetApp.openById('spreadsheet ID') method.
But even doing so will not allow for using such a code outside of Google environment (as in JS fiddle for example) since SpreadsheetApp is specific to Google Apps service.
You have to remember that Google Apps Script is based on JavaScript but is not "plain" JavaScript , it uses a lot of specific services that work only in relation with Google Apps.
edit to answer your comment below :
the code used in the spreadsheet to work as a data server goes like this : (this is deployed as a webapp without user interface. It runs as a service
function doGet(e) {
if(e.parameter.mode==null){return ContentService.createTextOutput("error, wrong request").setMimeType(ContentService.MimeType.TEXT)};
var mode = e.parameter.mode;
var value = e.parameter.value;
var ss = SpreadsheetApp.openById('1yad5sZZt-X6bIftpR--OSyf3VZWf3Jxx8UJBhh7Arwg');
var sh = ss.getSheets()[0];
if(mode=='read'){
var sheetValues = sh.getDataRange().getValues();// get data from sheet
var valToReturn = ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);
return valToReturn;// send it as JSon string
}
if(mode=='write'){
var val = Utilities.base64Decode(value,Utilities.Charset.UTF_8);// decode base64 and get an array of numbers
Logger.log(val);// see it !
var stringVal = ''; // create an empty string
for(var n in val){
stringVal += String.fromCharCode(val[n]);// add each character in turn
}
var sheetValues = JSON.parse(stringVal);// convert the string into an object (2D array)
Logger.log(sheetValues);// check result
sh.getRange(1,1,sheetValues.length,sheetValues[0].length).setValues(sheetValues);// update the sheet
return ContentService.createTextOutput(JSON.stringify(sheetValues)).setMimeType(ContentService.MimeType.JSON);// send back the result as a string
}
return ContentService.createTextOutput('error').setMimeType(ContentService.MimeType.TEXT);// in case mode is not 'read' nor 'write'... should not happen !
}
you can call this service by its url + parameters and it will get / set values in the spreadsheet. This is a basic example but it works nicely.
below it the webapp code of the Ui that uses this service in this spreadsheet
var stylePanel = {'padding':'50px', 'background':'#FFA'};
var styleButton = {'padding':'5px', 'border-radius':'5px', 'borderWidth':'1px', 'borderColor':'#DDD','fontSize':'12pt'};
var styleTextItalic = {'fontSize':'12pt','fontStyle':'italic','fontFamily':'arial,sans-serif','color':'#F00'};
var styleTextNormal = {'fontSize':'12pt','fontStyle':'normal','fontFamily':'arial,sans-serif','color':'#00F'};
var styleLabel = {'fontSize':'12pt','color':'#F00'};
var url = 'https://script.google.com/macros/s/AKfycbwPioVjYMSrmhKnJOaF2GG83dnstLWI7isU9SF1vxPV8td-g9E7/exec';
var numRow = 21;// the number of rows in the grid = number of rows in the SS + 1
;
function doGet() {
var app = UiApp.createApplication().setTitle('url_fetch_demo');
var panel = app.createVerticalPanel().setStyleAttributes(stylePanel);
var headers = ['Field Name','Your answer'];// grid title
var grid = app.createGrid(numRow+2,2);// create the grid with right size
var wait = app.createImage('https://dl.dropboxusercontent.com/u/211279/loading3T.gif').setId('wait').setVisible(false);// get a spinner image in animated gif
var handlerWrite = app.createServerHandler('writeSheet').addCallbackElement(grid);// 2 handlers for the buttons
var handlerRead = app.createServerHandler('readSheet').addCallbackElement(grid);
var Chandler = app.createClientHandler().forTargets(wait).setVisible(true);// a client handler for the spinner
var buttonWrite = app.createButton('Write to Sheet',handlerWrite).addClickHandler(Chandler).setStyleAttributes(styleButton);
var buttonRead = app.createButton('Read from Sheet',handlerRead).addClickHandler(Chandler).setStyleAttributes(styleButton);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){ // create all the textBoxes with names & IDs
var textBox = app.createTextBox().setText('no value').setName('text'+n+'-'+m).setId('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
//if(m==0){textBox.setEnabled(false)};// prevent writing to left column (optional)
grid.setWidget(n,m,textBox);// place widgets
}
}
grid.setWidget(numRow,0,buttonRead).setWidget(numRow,1,buttonWrite).setWidget(numRow+1,1,wait) // place buttons
.setWidget(0,0,app.createLabel(headers[0]).setStyleAttributes(styleLabel)) // and headers
.setWidget(0,1,app.createLabel(headers[1]).setStyleAttributes(styleLabel));
app.add(panel.add(grid));
return app; // show Ui
}
function writeSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);// spinner will be hidden when fct returns
var dataArrayImage = [];// an array to get typed values
for(var n=1 ; n < numRow ; n++){
var row=[];
for(var m=0 ; m < 2 ; m++){
row.push(e.parameter['text'+n+'-'+m]); // get every value in every "cell"
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextItalic);// update "cells" style
//textBox.setText('written value = '+e.parameter['text'+n+'-'+m]);// rewrite to the cells - not usefull but serves to check while debugging
}
dataArrayImage.push(row);// store one row(=2cells)
}
var UiValues = JSON.stringify(dataArrayImage);// stringfy the array
var newValues = url+'?mode=write&value='+Utilities.base64Encode(UiValues,Utilities.Charset.UTF_8);// add to url & parameters+ encode in pure ASCII characters
Logger.log(newValues);// check in logger
var check = UrlFetchApp.fetch(newValues).getContent();// get back the result
Logger.log(check);// check result = newValues sent back in bytes format
return app;//update Ui
}
function readSheet(e){
var app = UiApp.getActiveApplication();
app.getElementById('wait').setVisible(false);
var returnedValue = UrlFetchApp.fetch(url+'?mode=read').getContentText();// get data from server
Logger.log(returnedValue);// check values
var sheetValues = JSON.parse(returnedValue);
for(var n=1 ; n < numRow ; n++){
for(var m=0 ; m < 2 ; m++){
var textBox = app.getElementById('text'+n+'-'+m).setStyleAttributes(styleTextNormal);
textBox.setText(sheetValues[n-1][m]);// iterate and update cells values
}
}
return app;// update Ui
}

Categories

Resources