Skip to content

Instantly share code, notes, and snippets.

@aishfenton
Created September 5, 2011 04:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aishfenton/1194096 to your computer and use it in GitHub Desktop.
Save aishfenton/1194096 to your computer and use it in GitHub Desktop.
vWorkApp Javascript Script
// ------------------------------------------------------------------------------------------------------------------//
// vWorkApp Core Library
// ------------------------------------------------------------------------------------------------------------------//
var vWorkAppScript = vWorkAppScript || {};
vWorkAppScript.host = "api.vworkapp.com";
vWorkAppScript.apiToken = "PUT YOUR API KEY HERE"
(function() {
// CONFIGURATION
// ------------------------------------------------------------------------------------------------------------------//
//required fields
var _spreadsheet;
var apiToken;
var host;
//optional fields
var recordsPerPage = 100;
var dataDecorator = null;
// API HELPER
// ------------------------------------------------------------------------------------------------------------------//
this.loadData = function(url){
var result = this.loadDataPage(url,1);
return [Xml.parse(result, true)];
}
this.loadPagedData = function(url){
var page = 1;
var results = [];
var result = this.loadDataPage(url,1);
var xmlDoc = Xml.parse(result, true);
var pages = parseInt(xmlDoc.getElement().getAttribute("total_pages").getValue());
results.push(xmlDoc);
for(var i = 2; i <= pages; i++) {
results.push(Xml.parse(this.loadDataPage(url,i), true));
}
return results;
}
this.loadDataPage = function(url, page) {
var parameters = this.generateRequestHeader("get");
var requestURL = url + "&per_page="+recordsPerPage+"&page=" + page;
var result;
try {
result = UrlFetchApp.fetch(requestURL, parameters).getContentText();
} catch(e){
Logger.log("I/O Failure: "+e);
Browser.msgBox("We are sorry but we are unable to process your request at this time. Please try again later.");
}
return result;
}
this.postAsJSON = function(url, payload, method) {
if (method == null)
method = 'post';
var args = this.generateRequestHeader(
method, {
payload: JSON.stringify(payload),
contentType: "application/json; charset=UTF-8"
}
);
UrlFetchApp.fetch(url, args);
};
// URL HELPER
// ------------------------------------------------------------------------------------------------------------------//
this.buildURL = function(resource, ext, identifier, useThirdParty){
var str = resource + ((identifier == null) ? "" : "/"+identifier) + ((ext == null) ? "" : ext);
return "https://" + this.host + "/2.0/"+ str + "?api_key=" + this.apiToken + ((useThirdParty) ? "&use_third_party_id=true" : '');
}
this.explodeURLParameters = function(params){
var s = '';
var value;
for(var key in params){
value = params[key];
key = escape(key); value = escape(value);
var kvp = key+"="+value;
var r = new RegExp("(&|\\?)"+key+"=[^\&]*");
s = s.replace(r,"$1"+kvp);
if(!RegExp.$1) {s += '&' + kvp;};
}
return s;
}
this.generateRequestHeader = function(method, arguments){
if (arguments == null) arguments = {};
arguments['method'] = method;
arguments['headers'] = { "Accept-Encoding": "gzip,deflate" }
return arguments;
}
// JOBS
// ------------------------------------------------------------------------------------------------------------------//
this.loadJobByIdentifier = function(jobId, isThirdParty){
var url = this.buildURL("jobs",".xml",jobId,isThirdParty);
return this.loadData(url);
};
this.loadJobsForParameters = function(params){
var url = this.buildURL("jobs",".xml") + this.explodeURLParameters(params);
return this.loadData(url);
}
this.loadJobsForList = function(list,useThirdParty){
var jobsAsXML = [];
for (var idIterator = 0; idIterator < list.length; idIterator++) {
jobsAsXML.push(this.loadJobByIdentifier(list[idIterator].trim(),useThirdParty));
}
return jobsAsXML;
}
this.createJob = function(customerName, templateName, duration, steps, custom_fields) {
var payload = {
job: {
customer_name: customerName,
template_name: templateName,
planned_duration: duration,
steps: steps,
custom_fields: custom_fields
}
};
var url = this.buildURL('jobs');
this.postAsJSON(url, payload);
}
this.updateJob = function(jobAsJSON){
var job = {};
for (var property in jobAsJSON){
job[vWorkAppUtils.toUnderscore(property)] = jobAsJSON[property];
}
var payload = { job: job };
var url = this.buildURL('jobs','.xml',job.id);
this.postAsJSON(url, payload, 'put');
}
this.jobFlattenCustomFields = function(job){
// WARNING - this method will clone job and return all custom fields as root elements keyed on name
var jobClone = {}; var property;
for(property in job){
jobClone[property] = job[property];
}
for(property in job.customFields){
jobClone[this.normalizeHeader(job.customFields[property].name)] = job.customFields[property].value;
}
return jobClone;
}
this.jobRebuildCustomFields = function(job,stdHeaders,cfHeaders){
// WARNING - this method assumes that anything not in stdHeaders is a custom field keyed on name
var normalizedStdHeaders = vWorkAppScript.normalizeHeaders(stdHeaders);
var normalizedCfHeaders = vWorkAppScript.normalizeHeaders(cfHeaders);
var jobClone = {}; var customFields = []; var property;
for(property in job){
if (normalizedStdHeaders.indexOf(property) == -1){
if (normalizedCfHeaders.indexOf(property) == -1)
continue;
customFields.push({
name:cfHeaders[normalizedCfHeaders.indexOf(property)],
value:job[property]
});
continue;
}
jobClone[property] = job[property];
}
jobClone.customFields = customFields;
return jobClone;
}
// PROOF OF DELIVERY
// ------------------------------------------------------------------------------------------------------------------//
this.getProofOfDeliveryURL = function(jobId){
return "https://" + this.host + "/2.0/jobs/" + jobId + "/proof_of_delivery.png?api_key=" + this.apiToken;
}
// CUSTOM FIELDS
// ------------------------------------------------------------------------------------------------------------------//
// DATA PARSERS
// ------------------------------------------------------------------------------------------------------------------//
this.jobsXMLtoJSON = function(jobsXML){
var jobXMLArray = jobsXML.jobs.getElements("job");
if (jobXMLArray.length == 0) return {};
var jobsData = []; var jobData;
for(var i = 0; i < jobXMLArray.length; i++) {
jobXML = jobXMLArray[i];
jobsData.push(this.jobXMLtoJSON(jobXML));
}
return jobsData;
}
this.jobXMLtoJSON = function(jobXML){
if(jobXML.getElement('id') == null)
return {};
var jobData = {
id: jobXML.getElement('id').getText(),
jobName: jobXML.getElement('template_name').getText(),
customerName: jobXML.getElement('customer_name').getText(),
workerName: jobXML.getElement('worker_name').getText(),
proofOfDelivery: (jobXML.getElement('has_pod').getText() == "true") ? this.getProofOfDeliveryURL(jobXML.getElement('id').getText()) : "",
steps: this.stepsXMLtoJSON(jobXML.getElement('steps')),
customFields: this.customFieldXMLtoJSON(jobXML.getElement('custom_fields')),
progressState: jobXML.getElement('progress_state').getText(),
actualDuration: jobXML.getElement('actual_duration').getText(),
plannedDuration: jobXML.getElement('planned_duration').getText(),
plannedStartAt: jobXML.getElement('planned_start_at').getText(),
plannedEndAt: jobXML.getElement('planned_end_at').getText(),
actualStartAt: jobXML.getElement('actual_start_at').getText()
}
if (this.dataDecorator != null)
this.dataDecorator.run("job", jobData);
return jobData;
}
this.customFieldXMLtoJSON = function(customFieldNode){
var cfXMLArray = customFieldNode.getElements("custom_field");
var customFieldsData = []; var customFieldData;
for(var c = 0; c < cfXMLArray.length; c++) {
var cfXML = cfXMLArray[c];
customFieldData = {
name: cfXML.name.getText(),
value: cfXML.value.getText(),
type: cfXML.type.getText()
}
customFieldsData.push(customFieldData);
}
return customFieldsData;
}
this.stepsXMLtoJSON = function(stepNode){
var stepsXMLArray = stepNode.getElements("step");
var stepsData = []; var stepData;
for(var s = 0; s < stepsXMLArray.length; s++) {
var stepXML = stepsXMLArray[s];
stepData = {
id: stepXML.id.getText(),
name: stepXML.name.getText(),
completedAt: stepXML.completed_at.getText(),
location: this.locationXMLtoJSON(stepXML.location)
}
stepsData.push(stepData);
}
return stepsData;
}
this.locationXMLtoJSON = function(locationNode){
return {
id: locationNode.id.getText(),
formattedAddress: locationNode.formatted_address.getText(),
lat: locationNode.lat.getText(),
lng: locationNode.lng.getText()
}
}
// GEOUTILS
// ------------------------------------------------------------------------------------------------------------------//
this.geocodeAddress = function(address) {
var geocoder = Maps.newGeocoder();
try {
var geoJSON = geocoder.geocode(address);
return geoJSON.results[0].geometry.location;
} catch(e) {
Logger.log("Geocoding Failed: Address is " + address);
return { lat: '', lng: '' };
}
}
}).apply(vWorkAppScript);
// vWorkAppUitls - vWorkApp utility library
// ------------------------------------------------------------------------------------------------------------------//
// ------------------------------------------------------------------------------------------------------------------//
var vWorkAppUtils = vWorkAppUtils || {};
(function() {
this.ISODateString = function(d){
function pad(n){ return n < 10 ? '0'+ n : n }
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'Z'
}
this.xmlDateToJavascriptDate = function(xmlDate) {
var re = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?(Z|([+-])([0-9]{2}):([0-9]{2}))?$/;
var match = xmlDate.match(re);
if (!match)
return null;
var all = match[0];
var year = match[1]; var month = match[2] - 1; var day = match[3];
var hour = match[4]; var minute = match[5]; var second = match[6];
var milli = match[7];
var z_or_offset = match[8]; var offset_sign = match[9];
var offset_hour = match[10]; var offset_minute = match[11];
if (offset_sign) {
var direction = (offset_sign == "+" ? 1 : -1);
hour = parseInt(hour) + parseInt(offset_hour) * direction;
minute = parseInt(minute) + parseInt(offset_minute) * direction;
}
var utcDate = Date.UTC(year, month, day, hour, minute, second, (milli || 0));
return new Date(utcDate);
}
this.toUnderscore = function(str){
str = str.substr(0,1).toLowerCase() + str.substr(1,str.length);
return str.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
}
this.secondsToTime = function(seconds){
var h = Math.floor(seconds / (60 * 60));
var divisor_for_minutes = seconds % (60 * 60);
var m = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var s = Math.ceil(divisor_for_seconds);
return h + "h "+m+"m "+s+"s";
}
}).apply(vWorkAppUtils);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment