Google Spreadsheet to pdf with watermark in Google script - google-apps-script

I want to convert the spreadsheet with a watermark/background image, and send + save the generated pdf
The converting to pdf worked, but I don't know if/how you can put an image to the generated pdf.
This is what i got now:
function ExportAndSent(subject, filename, email) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var message = "A message";
var tempSpreadsheet = SpreadsheetApp.create(filename);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet = ss.getActiveSheet();
sheet.copyTo(tempSpreadsheet);
tempSpreadsheet.deleteActiveSheet();
var pdf = DriveApp.getFileById(tempSpreadsheet.getId()).getAs(MimeType.PDF);
var pdfBytes = pdf.getBytes();
var attach = {fileName:(filename + ".pdf"),content:pdfBytes, mimeType:MimeType.PDF};
// Here we need to put a watermark
// Send and export
MailApp.sendEmail(email, subject, message, {attachments:[attach]});
DriveApp.createFile(pdf);
// Delete Temporary
DriveApp.getFileById(tempSpreadsheet.getId()).setTrashed(true);
}

It looks like the spreadsheet is already converted to a PDF. A third party service can be used to add a watermark to open the PDF and add a watermark to it. PDF WebAPI is a free service you can use. Below I put together a function that can take in a PDF and add a watermark to it. The watermark is hardcoded to "watermark.jpg" currently, but that can be changed. From looking at the code above, the function should be called between the following lines:
var pdf = DriveApp.getFileById(tempSpreadsheet.getId()).getAs(MimeType.PDF);
var pdfBytes = pdf.getBytes();
The "pdf" variable should be used as input, and "pdfBytes", can take the return value of appendWatermark().
You will need to sign up for a free PDF WebAPI account to get an ID and key.
function appendWatermark(inputPDF) {
var fileBlob = inputPDF.copyBlob();
var decorationData = [{
"watermarkSettings": {
"opacity": 50,
"source": {
"file": "watermark.jpg",
"page": 1
},
"scale": {
"type": "relative",
"percent": 90
},
"location": "top",
"rotation": "0"
}
}];
var applicationData = {
"id": "",
"key": ""
};
var payload = {
"application": JSON.stringify(applicationData),
"input": fileBlob,
"decorationData": JSON.stringify(decorationData),
"resource": DriveApp.getFilesByName("watermark.jpg").next().getBlob()
};
var options = {
"method": "post",
"payload": payload
};
var response = UrlFetchApp.fetch("https://pdfprocess.datalogics.com/api/actions/decorate/document", options);
return response.getBytes;
}

Related

update google sheet script with another script

I have a folder with approximately 140 google sheets and a main standalone google script that I call with a library script from each sheet but need to add/update all of the scripts attached to each google sheet script. Currently the only way I have found is to open each sheet script and add the library script save and move on but 140 sheets takes a long time. I know all my sheets that i need scripts adding or updating are in one folder so thinking I could use something like this to loop through all the gsheets but can't find away to edit the scripts from here...
function scriptupdate() {
var folder = DriveApp.getFolderById('FOLDER CONTAINING THE GSHEETS ID');
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log("File Name is "+file.getName());
Logger.log("File ID is "+file.getId());
}
}
I'm not sure if what I'm trying to do is possible but trying to save a lot of time if this is doable but really appreciate any help and guidance offered
Answer
You can create and update projects and its content using the projects resource methods. As these have no built-in service in Apps Script they must be called with urlFetchApp.fetch.
When writing functions, remember to put them between grave accents (`). You can use the key combination Ctrl + Shift + I to fix all the indentation and spacing in the whole script file.
Code
function scriptupdate() {
// GET FILES
var folder = DriveApp.getFolderById('FOLDER CONTAINING THE GSHEETS ID');
var files = folder.getFiles();
while (files.hasNext()) {
// GET FILE INFORMATION
var file = files.next();
var name = file.getName()
var fileId = file.getId()
// CREATE CONTAINER-BOUND SCRIPT
var url = 'https://script.googleapis.com/v1/projects'
var formData = {
'title': name,
'parentId': fileId
};
var options = {
method: 'post',
headers: { Authorization: "Bearer " + ScriptApp.getOAuthToken() },
payload: formData,
muteHttpExceptions: true
};
var res = JSON.parse(UrlFetchApp.fetch(url, options).getContentText())
var scriptId = res["scriptId"]
// UPDATE PROJECT CONTENT
var url = 'https://script.googleapis.com/v1/projects/' + scriptId + '/content';
var formData = {
"files": [
{
"name": "appsscript",
"type": "JSON",
"source": `{
"timeZone": "America/New_York",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}`
},
{
"name": "main",
"source":
`function main() {
console.log('hello world')
}`,
"type": "SERVER_JS"
}
]
}
var options = {
method: 'put',
contentType: 'application/json',
payload: JSON.stringify(formData),
headers: { Authorization: "Bearer " + ScriptApp.getOAuthToken() },
muteHttpExceptions: true
};
var res = UrlFetchApp.fetch(url, options).getContentText()
console.log(res)
}
}
Reference
projects
projects: create
projects: updateContent
urlFetchApp.fetch

Changing json file creation destination from dropbox to google drive

i need to have this code have the create the json file inside of google drive instead of drop box
I have experimented with a few things but i am not quite knowledgeable enough to make this change, i would show less code but i do not know where the problem lies
function main(spreadsheet_id) {
var json_dict = fillDict(spreadsheet_id)
var json_obj = convertDictToJSON(json_dict)
sendJSONToDropbox(json_obj)
};
function sendJSONToDropbox(json_object) {
var timestamp = timeStamp()
var parameters = {
"path": "/json_jobs/rhino" + "-" + timestamp + ".json",
"mode": "add", // do not overwrite
"autorename": true,
"mute": false // notify Dropbox client
};
var headers = {
"Content-Type": "application/octet-stream",
'Authorization': 'Bearer ' + 'dropbox_access_token',
"Dropbox-API-Arg": JSON.stringify(parameters)
};
var options = {
"method": "POST",
"headers": headers,
"payload": json_object
};
var API_url = "https://content.dropboxapi.com/2/files/upload";
var response = JSON.parse(UrlFetchApp.fetch(API_url, options).getContentText());
Using the DriveApp class, it is not too difficult:
function sendJSONToGDrive(json_object) {
var timestamp = timeStamp();
var fileName = "rhino" + "-" + timestamp + ".json";
var fileContent = JSON.stringify(json_object);
var myFolder = DriveApp.getFolderById('YOUR_FOLDER_ID');
myFolder.createFile(fileName, fileContent, 'application/json');
}
You just have to replace YOUR_FOLDER_ID for your actual destination folder's id.
For more information on how to use the Drive API from Google Apps Script, I recommend you check out the following link: https://developers.google.com/apps-script/reference/drive

can't open Slack dialog through google apps scripts

I'm trying to use google apps scripts and slack to automate my work. And I wish to enter some text with the Slack dialog to modify my google spreadsheet with google apps scripts. However, with the below code, I can't open a Dialog via Slack-API's Slash command. Is my code have some problems?
function doPost(e){
var params = e.parameter;
var token = params.token;
var text = params.text;
var trigger_id = params.trigger_id;
var slackUrl = ["https://slack.com/api/dialog.open"];
if (token == "[token from slack]"){
var dialog = {
"token": "[OAuth Token]",
"trigger_id":trigger_id,
"dialog":{
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
}
};
var options = {
'method' : 'POST',
'contentType': 'application/json',
'payload' : dialog};
UrlFetchApp.fetch(slackUrl, options);
}
else{
var res = {"text":"failed token verification!"}
return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}}
How about this modification?
Modification points :
Use a string to "url" of "UrlFetchApp.fetch(url, params)".
Use JSON.stringify() for dialog of the object dialog.
'contentType': 'application/json', is not required.
Modified script :
function doPost(e) {
var params = e.parameter;
var token = params.token;
var text = params.text;
var trigger_id = params.trigger_id;
var slackUrl = "https://slack.com/api/dialog.open";
if (token == "[token from slack]"){ // Please input this.
var dialog = {
"token": "[OAuth Token]", // Please input this.
"trigger_id": trigger_id,
"dialog": JSON.stringify({
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
})
}
var options = {
'method' : 'post',
'payload' : dialog,
};
UrlFetchApp.fetch(slackUrl, options);
}
else{
var res = {"text":"failed token verification!"}
return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput(); // Important
}
Note :
When there are no exceptions within the dialog submission, your app must respond with 200 OK with an empty body. This will complete the dialog.
When it uses dialog, it returns the empty body using ContentService.createTextOutput() for above, because the status code cannot be customized by Google Apps Script. When the empty body is not returned, the error occurs.
This modified script supposes that your setting for using Slack dialog has already been done.
If you modified your script, please redeploy Web Apps as a new version. By this, the script of the latest version is reflected to Web Apps.
References :
UrlFetchApp.fetch()
In my environment, I confirmed that this modified script works. But if this didn't work, I'm sorry.

Extract Keenio Data into google spreadsheet

I am currently using ImportJSON to import Sendgrid Email with data Keenio Extraction Query API URL by calling the ImportJSON function in a Google Spreadsheet cell of Sheet DATA.
=ImportJSON("https://api.keen.io/3.0/projects/"& PROJECT_KEY & "/queries/extraction?api_key=" & API_KEY & "&event_collection=" & EVT_COL & "&timezone=" & TIMEZONE & "&latest=" & LATEST & "&property_names..........", PTDATA!$AB$1)
In Sheet PTDATA, in the last column cell i am setting a random number for ImportJSON to recalculate. The function runs on Spreadsheet open event. I have also added a custom menu to call the ReCalcCell custom function.
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('IMPORT DATA')
.addItem('KEENIO DATA', 'ReCalcCell')
.addToUi();
}
function ReCalcCell(){
var min = Math.ceil(0);
var max = Math.floor(9999);
var randomNum = Math.floor(Math.random() * (max - min + 1)) + min
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("PTDATA");
sh.getRange("$AB$1").setValue(randomNum);
}
PTDATA sheet has specific column header names for which i want to pull the data from DATA sheet. Towards the right of these columns, i have other calculation columns which work on these specific columns.
Since the columns in DATA sheet always appear in a random / shuffled order, i had to write a small custom function GCL which takes in a header name and returns its datarange address from DATA sheet as a string.
function GCL(header,dummy) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
var headings = sheet.getRange(1, 1, 1, sheet.getLastColumn()); // get the range representing the whole sheet
var width = headings.getWidth();
var lrow = sheet.getLastRow();
// search every cell in row 1 from A1 till the last column
for (var i = 1; i <= width; i++) {
var data = headings.getCell(1,i).getValue();
if (data == header) {
return ((sheet.getSheetName() + "!" + columnToLetter(i)+"2:" + columnToLetter(i) + lrow).toString()); // return the column range if we find it
break; // exit when found
}
}
return(-1); // return -1 if it doesn't exist
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Then i use the custom function GCL in each specific column to get it's datarange. Once data is populated, the PDATA sheet is used to create different Pivots for reporting purposes.
=ARRAYFORMULA(INDIRECT(GCL(A1,$AB$1)))
The problems i am facing is that though the ImportJSON data populates the DATA sheet:
DATA Sheet:
The columns appear shuffled everytime, so my calculation columns cannot calculate as the references go away. This renders the pivots useless! To counter this issue, i had to create the PDATA sheet to pull in specific columns using the custom function GCL.
The custom function GCL does not always refresh and most of the time shows #Ref error.
PDATA Sheet:
BTW, my JSON output from Keenio looks like this:
{
"result":
[
{
"sg_event_id": "92-OndRfTs6fZjNdHWzLBw",
"timestamp": 1529618395,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:59:55.000Z",
"created_at": "2018-06-21T22:00:28.532Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 38,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}, {
"sg_event_id": "bjMlfsSfRyuXEVy8LndsYA",
"timestamp": 1529618349,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:59:09.000Z",
"created_at": "2018-06-21T21:59:39.491Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 36,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}, {
"sg_event_id": "fru_s2s1RtueuqBMNoIoTg",
"timestamp": 1529618255,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:57:35.000Z",
"created_at": "2018-06-21T21:58:20.374Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 29,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}
]
}
My questions are:
Is there a way to parse the JSON result without use of ImportJSON, which has to be entered as a custom function in a cell that also depends on recalculation? ImportJSON sometimes doesn't work properly.
How can this code be refactored or optimized so that it can always return data to PDATA sheet columns?
Is there a better way of accomplishing what i want without resorting to custom functions like GCL in the PDATA Sheet or ImportJSON in DATA sheet?
How about this sample script? This script parses the values retrieved from API using UrlFetchApp and put them to the sheet "DATA". You can run this at the menu of spreadsheet. Before you run this, please put the endpoint.
Sample script :
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('IMPORT DATA')
.addItem('KEENIO DATA', 'ReCalcCell')
.addItem('main', 'main')
.addToUi();
}
function main() {
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText(); // Modified
var values = JSON.parse(res);
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, e.url_offset.index, e.url_offset.type, e.sg_message_id, e.email]});
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
sheet.getRange(sheet.getLastRow() + 1, 1, putData.length, putData[0].length).setValues(putData);
}
Note :
When you use this, please put the endpoint including your token to url.
I confirmed this script using the JSON object in your question. So if the structure of the object is changed, it is required to also modify the script. Please be careful this.
Reference :
UrlFetchApp.fetch()
If I misunderstand about your issue, please tell me. I would like to modify it.
Edit 1 :
Pattern 1 :
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, JSON.parse(e["url_offset"]).index, JSON.parse(e["url_offset"]).type, e.sg_message_id, e.email]});
Pattern 2 :
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, e["url_offset"].index, e["url_offset"].type, e.sg_message_id, e.email]});
Edit 2 :
Could you please run this script and provide the values of the created file? Of course, please remove the personal information. But please don't modify the structure of the object. If you cannot do it, I would like to think of other ways.
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText();
DriveApp.createFile("sample.txt", res, MimeType.PLAIN_TEXT)
Edit 3 :
Please copy and paste this script in your script editor, run myFunction(). Then, please show the values of file. When you run this function, please confirm whether there are NOT the same function name in your project.
function myFunction() {
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText();
DriveApp.createFile("sample.txt", res, MimeType.PLAIN_TEXT)
}
Edit 4 :
Please copy and paste this script in your script editor, run myFunction2(). Then, please show the results. When you run this function, please confirm whether there are NOT the same function name in your project.
Please confirm whether the keys and values of keen and url_offset are retrieved.
function myFunction2() {
var url = "###";
var res = UrlFetchApp.fetch(url).getContentText();
var values = JSON.parse(res);
for (var key in values.result[0]) {
Logger.log("key: %s, value: %s", key, values.result[0][key])
if (typeof values.result[0][key] == "object") {
for (var dkey in values.result[0][key]) {
Logger.log("key: %s, dkey: %s, value: %s", key, dkey, values.result[0][key][dkey])
}
}
}
}
Edit 5 :
Please copy and paste this script in your script editor, run myFunction3(). Then, please show the results. When you run this function, please confirm whether there are NOT the same function name in your project.
function myFunction3() {
var url = "###"; // Please set this.
var res = UrlFetchApp.fetch(url).getContentText();
var values = JSON.parse(res);
var obj = [];
for (var i = 0; i < values.result.length; i++) {
var temp = {};
var v = values.result[i];
for (var key in v) {
temp[key.replace(/_/g, "")] = v[key];
if (typeof v[key] == "object") {
for (var dkey in v[key]) {
temp[key.replace(/_/g, "") + dkey.replace(/_/g, "")] = v[key][dkey];
}
}
}
obj.push(temp);
}
var putData = obj.map(function(e) {return [e.useragent, e.sgeventid, e.timestamp, e.ip, e.url, e.event, e.keentimestamp, e.keencreatedat, e.keenid, e.urloffsetindex, e.urloffsettype, e.sgmessageid, e.email]});
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
sheet.getRange(sheet.getLastRow() + 1, 1, putData.length, putData[0].length).setValues(putData);
}
Looking at what you are doing here, it might be much easier to design your spreadsheet in an "append-only" format with a Zapier integration.
Zapier is able to handle SendGrid events directly, and append those events to your spreadsheet, if that is what you want.
And then you can have your "calculation columns" on a separate Sheet in the spreadsheet.
Just an idea.

Filtering a Pivot Table in Google Scripts

This is my first post here and I am new to coding. I have been tasked with creating an automated report which will send a google form submitter a graph to help them monitor their production versus their daily goal. To do this I am using the new developer Google sheets script to refresh a pivot table. I found this code online, and it works great, however, I want to add a line which will filter based to the unique submitter's data. Here is the code I have so far:
function updatePivotTable() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var pivotTableSheetName = "Lunch Chart";
var pivotTableSheetId = ss.getSheetByName(pivotTableSheetName).getSheetId();
var fields = "sheets(properties.sheetId,data.rowData.values.pivotTable)";
var sheets = Sheets.Spreadsheets.get(ss.getId(), {fields: fields}).sheets;
for (var i in sheets) {
if (sheets[i].properties.sheetId == pivotTableSheetId) {
var pivotTableParams = sheets[i].data[0].rowData[0].values[0].pivotTable;
break;
}
}
// Update source range:
pivotTableParams.source.endRowIndex = 40;
// Send back the updated params
var request = {
"updateCells": {
"rows": {
"values": [{
"pivotTable": pivotTableParams
}]
},
"start": {
"sheetId": pivotTableSheetId
},
"fields": "pivotTable"
}
};
Sheets.Spreadsheets.batchUpdate({'requests': [request]}, ss.getId());
}
Is this possible? Where would I add in the filter piece? I found this on the google developer site, but I am very new to coding so I don't really know where to put it or how to make it conditional. https://developers.google.com/sheets/api/reference/rest/v4/FilterCriteria
Thank you!
I am not sure if this is still relevant to you but you can add a filter using following piece of code-
"criteria": {
<col_index>: {"visibleValues": <filter criteria>},
<col_index>: {"visibleValues": <filter criteria>},
I've had the same problem and didn't find an easy explanation or code. Here is what I've done and it works:
function updatePivotTable() {
var ss = SpreadsheetApp.openById(SHEET_ID);
var pivotTableSheetName = "Pivot";
var pivotTableSheetId = ss.getSheetByName(pivotTableSheetName).getSheetId();
var fields = "sheets(properties.sheetId,data.rowData.values.pivotTable)";
var sheets = Sheets.Spreadsheets.get(ss.getId(), {fields: fields}).sheets;
for (var i in sheets) {
if (sheets[i].properties.sheetId == pivotTableSheetId) {
var pivotTableParams = sheets[i].data[0].rowData[0].values[0].pivotTable;
break;
}
}
// Update source range:
pivotTableParams.source.endRowIndex = 111;
pivotTableParams.criteria = { 3: {"visibleValues": ["foo"]}};
// Send back the updated params
var request = {
"updateCells": {
"rows": {
"values": [{
"pivotTable": pivotTableParams
}]
},
"start": {
"sheetId": pivotTableSheetId
},
"fields": "pivotTable",
}
};
Sheets.Spreadsheets.batchUpdate({'requests': [request]}, ss.getId());
}
So basically you need to pass the criteria as a pivotTableParams where the key of the object is the index of the column that you want to query and the value should be passed as another object with the format {"visibleValues": ["foo"]}.