HTPP POST to Google Forms or Alternative - post

I have a google form setup that emails me upon a manual submission when somebody fills it out (new lead) and transfers the information to a Google spreadsheet. Easy enough to figure that out.
However, now I'm trying to figure out how to send the same information information contained within a url string and automatically POST that information to the form. Or find a company who offers that ability, via an api or other means. So far I've tested out jotform and a few others. The information passed along fine, but it doesn't auto populate the fields. I assume it's because it doesn't know that x=y due to the fields being named differently. I've found a ton of documentation about pre-populating the forms, but not much about filling out a form every time a new POST url is generated.
URL looks like the following
VARhttps://script.google.com/macros/s/XXXXXXXXXXXXXXXX/exec?/--A--
first_name--B--/--A--last_name--B--/--A--address1--B--/--A--city--B--/--A--
state--B--/--A--postal_code--B--/--A--phone_number--B--/--A--date_of_birth--
B--/--A--email--B--
Information passed is as follows
https://website
here.com/Pamela/Urne/123+Test+Street/Henderson/TX/75652/281XXXXXX/1974-01-
01/test0101cw#test.com
The script I'm testing out
// original from: http://mashe.hawksey.info/2014/07/google-sheets-as-a-database-insert-with-apps-script-using-postget-methods-with-ajax-example/
// original gist: https://gist.github.com/willpatera/ee41ae374d3c9839c2d6
function doGet(e){
return handleResponse(e);
}
// Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
I get a success message after accessing the url, but all information listed in the spreadsheet is "Undefined."
That's as far as I got so far. If somebody knows an easier solution or can point me in the right direction I'd appreciate it.

Related

How to apply deduplication for an array returned in Zapier Code output

I have a Zapier Code block that does fetch for JSON array and the preprocess this data. I cannot use Zapier Webhook with polling, because I need to process the data a bit.
Zapier Webhook offers deduplication feature, by having id parameter associated with the items returned in an array from the URL endpoint. How can I achieve the same for Zapier Code? Currently, my zap is trying to process and trigger on the same data twice. This leads to the error that Zapier tries to send out the same tweet twice, every time the Code is triggered.
Here is mock data returned by my Code:
output = [{id: 1, name: "foo"}, {id: 2, name: "bar"}]
Currently, without deduplication, I am getting this email and having my zap disabled:
Your Zap named XXX was just stopped. This happened because our systems detected this Zap posted a duplicate tweet, which is against Twitter's Terms Of Service.
You can use storage by Zapier to achieve this. the ideal flow will be :
Trigger
Storage by Zapier [Get Value (use storage key = lastItemId) ]
Code By Zapier (Filter array return only those record that has id greater than the lastItemId)
Storage By Zapier (Set Value) : update lastItemId with the last item processed by Code By Zapier step
You can also use StoreClient in place of the Storage By zapier, but always update a existing key lastItemId and compare id of the record with ```lastItemId`` and at the end update StoreCLient key (lastItemId)
Based on the answer from Kishor Patidar, here is my code. I am adding the example code, here is too some time to figure it out. Specifically, in my case, the items cannot be processed in the order of appearance (no running counter primary keys) and also there are some limitations how far in the future Zapier can schedule actions (you can delay only up to one month).
The store also has a limitation of 500 keys.
// We need store for deduplication
// https://zapier.com/help/create/code-webhooks/store-data-from-code-steps-with-storeclient
// https://www.uuidgenerator.net/
var store = StoreClient('xxx');
// Where do we get our JSON data
const url = "https://xxx";
// Call FB public backend to get scheduled battles
const resp = await fetch(url);
const data = await resp.json();
let processed = [];
for(let entry of data.entries) {
console.log("Processing entry", entry.id);
// Filter out events with a non-wanted prize type
if(entry.prizes[0].type != "mytype") {
continue;
}
// Zapier can only delay tweets for one month
// As the code fires every 30 minutes,
// we are only interested scheduling tweets that happen
// very soon
const when = Date.parse(entry.startDate);
const now = Date.now();
if(!when) {
throw new Error("startDate missing");
}
if(when > now + 24 * 3600 * 1000) {
// Milliseconds not going to happen for next 24h
console.log("Too soon to schedule", entry.id, entry.startDate, now);
continue;
} else {
console.log("Starting to schedule", entry.id, entry.startDate, now);
}
const key = "myprefix_" + entry.id;
// Do manual deduplication
// https://stackoverflow.com/questions/64893057/how-to-apply-deduplication-for-an-array-returned-in-zapier-code-output
const existing = await store.get(key);
if(existing) {
// Already processed
console.log("Entry already processed", entry.id);
continue;
}
// Calculate some parameters on entry based on nested arrays
// and such
entry.startTimeFormat = "HH:mm";
// Generate URL for the tweet
entry.signUpURL = `https://xxx/${entry.id}`;
processed.push(entry);
// Do not tweet this entry twice,
// by setting a marker flag for it in store
await store.set(key, "deduplicated");
}
output = processed;

Removing descriptor from Returned Value

I am working inside Zapier with their javascript code action. I have built the following code to match and return values based on form submission data and now need to remove the "Text:" identifier at the beginning to make it more human readable. Important to note, the RegEx lookup including the identifier of /text:\s is necessary to only grab the selected values of the checkbox list from the form response data.
var svcs = inputData.addrSvcs.match(/text:\s(NCOA|Return Service Requested|Address Service Requested|None)/gi);
return svcs.map(function(svc) {
if (svcs) {
console.log(svcs);
return {value: svcs};
}});
//returns text: NCOA, text: Return Service Requested, text: Address Service Requested, text: None
For anyone looking to do something similar with Code in Zapier, the following seems to give the output I needed.
var addrSvc = inputData.addrSvcs.match(/text:\s(NCOA|Return Service Requested|Address Service Requested|None)/gi);
return addrSvc.map(function(svc) {
if (addrSvc) {
var originalTxt = addrSvc.join(',');
var newText = originalTxt.replace(/text:\s/gi,'')
return {addrServices: newText};
}});

UPS Tracking On Google Sheet does not work [duplicate]

This question already has answers here:
Scraping data to Google Sheets from a website that uses JavaScript
(2 answers)
Closed last month.
I used following formula get UPS live tracking feed and it works fine until yesterday. I think UPS has updated their site and this formula does not work anymore. Any idea or suggestions for how to get the tracking update from UPS?
=Index(IMPORTXML("https://wwwapps.ups.com/WebTracking/track?track=yes&trackNums="&A1,"//*[#id='tt_spStatus']"),1)
Now I am getting an error:
Imported content is empty
UPS updated this webpage. It no longer returns tracking information in the initial page response. It now instead makes a separate AJAX request to retrieve the information after the page is loaded.
Use this formula instead with the URL updated to a different page on their site that returns tracking information in the initial page response:
=Index(IMPORTXML("https://wwwapps.ups.com/tracking/tracking.cgi?tracknum="&A1,"//*[#id='tt_spStatus']"),1)
With Delivery Time and status where A1 has tracking code.
=index(IMPORTXML("https://wwwapps.ups.com/tracking/tracking.cgi?tracknum="&A1,"//*[contains(#class,'ups-group')]"),2)
Tracking with a few extra details (Where A1 is tracking no.):
=iferror(
IMPORTXML(
"https://wwwapps.ups.com/tracking/tracking.cgi?tracknum="&$A1,"//*[#id='tt_spStatus']")&iferror(": "&substitute(index(index(IMPORTHTML("https://wwwapps.ups.com/tracking/tracking.cgi?tracknum="&$A1,"table"),2),1),", United States",""),""
),
"--"
)
Result for package ready to ship: "Order Processed: Ready for UPS"
Result for in-transit parcel: "Picked Up: Atlanta, GA"
None of these work for me in 2020 but here's what does:
Add this function in script editor:
function IMPORTJSON(url,xpath){
try{
// /rates/EUR
var res = UrlFetchApp.fetch(url);
var content = res.getContentText();
var json = JSON.parse(content);
var patharray = xpath.split("/");
//Logger.log(patharray);
for(var i=0;i<patharray.length;i++){
json = json[patharray[i]];
}
//Logger.log(typeof(json));
if(typeof(json) === "undefined"){
return "Node Not Available";
} else if(typeof(json) === "object"){
var tempArr = [];
for(var obj in json){
tempArr.push([obj,json[obj]]);
}
return tempArr;
} else if(typeof(json) !== "object") {
return json;
}
}
catch(err){
return "Error getting data";
}
}
This formula will output transit status in your cell:
=IMPORTJSON(join("","http://shipit-api.herokuapp.com/api/carriers/ups/",A1),"activities/0/details")
I don't need the other details, so I made another cell that has a link to the ups tracking page if any other users need more information:
=HYPERLINK("https://www.ups.com/track?loc=en_US&tracknum="&A1&"&requester=WT/trackdetails)")

Script to send email to specific cell within a column every time the cell next to it gets updated

I hope this haven't been asked before I'm trying to do what the title says.
In this particular example I got email list on column A, and I want that email address to receive a notification when the cell next to it gets updates to "Yes"
So far I got this:
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for(var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("sendNotification")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onEdit()
.create();
};
/**
*
*/
function sendNotification(e) {
if("B2" == e.range.getA1Notation() ) {
if(e.value == "Yes") {
var recipients = "IDK what to put in here";
var subject = "There's an update to your request";
var body = "We resolved your issue!";
MailApp.sendEmail(recipients, subject, body);
}
}
}
I have edited the code with comments to explain what I changed, you were on the right track. OnEditTriggers to send Email is usually frowned upon, generally not a good practice. However, in your case you still have control on when to send an email, so improvement over just flat out emailing on every Edit.
FYI you dont need Initialize() to setup the manual triggers you can set it up manually: https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_manually
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
// The below code would delete all the triggers in your project not just the one you setup through this code.
for(var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("sendNotification")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onEdit()
.create();
};
function sendNotification(e) {
//if("B2" == e.range.getA1Notation() ) {
//This will only check is the range is B2 cell, if you want to check for column B. use getColumn
if(e.range.getColumn() == 2){
if(e.value == "Yes") {
//var recipients = "xxx#gmail.com"; //Email Address of the indented Recipents
// In your case email would be the value in cell A2 or cell in Column A corresponding to the one edited in Col B
// you can get that value like so
var recipients = e.source.getActiveSheet().getRange(e.range.getRow(),1).getValue()
Logger.log(recipients)
var subject = "There's an update to your request";
var body = "We resolved your issue!";
MailApp.sendEmail(recipients, subject, body);
}
}
}
New functions:
e.range.getColumn()/getRow() to get the specific column or row. So that way you working with numbers and not string. Easy to manipulate numbers with arithmetic compared to strings.
e.source.getActiveSheet() gives you sheet object of where the change happened. You use e.source.getActiveSheet().getName() to get the name of the sheet and use it to make sure email is only fired when an edit happens on a specific sheet.

Script does not trigger on FormSubmit: Remove Duplicates in Google Sheet of Google Form responses based on column

I am trying to remove older duplicate form responses based on a column using the following code.
The credit for the code goes to: http://www.jacorre.com/tutorial/remove-duplicate-rows-google-spreadsheets/
The code in my script is:
function removeDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet(),
responses = ss.getSheetByName('Name of Source Sheet'),
range = responses.getDataRange(),
numRows = range.getNumRows()-1,
data = range.getValues(),
columnHeadings = [data[0]],
newData = [];
for (var i=numRows; i>0; i--) {
var row = data[i],
duplicate = false;
for (var j in newData) {
if (row[4] == newData[j][4]) {
duplicate = true;
// [4] is the column number from the 1st column. the above would be 1 + 4 = 5th column
}
}
if (!duplicate) {
newData.push(row);
}
}
var final = ss.getSheetByName('Name of Destination Sheet');
if (!final) {
var final = ss.insertSheet('Name of Destination Sheet');
}
final.clearContents();
final.getRange(1,1,1,columnHeadings[0].length).setFontWeight('bold').setValues(columnHeadings);
final.getRange(2, 1, newData.length, newData[0].length).setValues(newData);
}
This has been set to trigger on Form Submit. It works well on new form submissions.
However, when an existing response is edited using 'Form Edit URL' from: https://webapps.stackexchange.com/questions/89551/show-url-used-to-edit-responses-from-a-google-form-in-a-google-spreadsheet-by-us/89566 the values are not updated into the new sheet.
But if the function is run manually the updated row is updated to the new sheet.
How can I sort this problem? Any help will be appreciated. Thank you.
From my own answer posted at Web Applications SE.
I just did a test and found that the on form submit event it's not
being triggered when a response is edited.
I'm not sure if the on form submit trigger is working as intended, if
the above is due to a bug or to a glitch. To be sure, post an issue to
the Google Apps Script Issue
Tracker.
As a workaround, instead of using the on form submit event, use
another way to run your script, like a time-drive trigger.
References
Custom menus in Google Apps - Google Apps Script Guides
Simple or installable triggers - Google Apps Script Guides
Google Apps Script Support

Resources