Created
September 5, 2011 04:32
-
-
Save aishfenton/1194096 to your computer and use it in GitHub Desktop.
Revisions
-
Aish revised this gist
Oct 18, 2011 . 1 changed file with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,10 @@ // ------------------------------------------------------------------------------------------------------------------// // vWorkApp Core Library // ------------------------------------------------------------------------------------------------------------------// var vWorkAppScript = vWorkAppScript || {}; vWorkAppScript.host = "api.vworkapp.com"; vWorkAppScript.apiToken = "PUT YOUR API KEY HERE" (function() { -
Aish revised this gist
Oct 12, 2011 . 1 changed file with 1 addition and 71 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ // vWorkAppScript - vWorkApp core library // ------------------------------------------------------------------------------------------------------------------// // ------------------------------------------------------------------------------------------------------------------// @@ -294,81 +293,12 @@ vWorkAppScript._spreadsheet = spreadsheet; Logger.log("Geocoding Failed: Address is " + address); return { lat: '', lng: '' }; } } }).apply(vWorkAppScript); // vWorkAppUitls - vWorkApp utility library // ------------------------------------------------------------------------------------------------------------------// // ------------------------------------------------------------------------------------------------------------------// -
Aish revised this gist
Oct 12, 2011 . 1 changed file with 434 additions and 132 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,133 +1,435 @@ // vWorkAppScript - vWorkApp core library // ------------------------------------------------------------------------------------------------------------------// // ------------------------------------------------------------------------------------------------------------------// var vWorkAppScript = vWorkAppScript || {}; vWorkAppScript.host = "api.vworkapp.com"; vWorkAppScript.apiToken = configSheet.getRange(2,2).getValue().trim(); vWorkAppScript._spreadsheet = spreadsheet; (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: '' }; } } // SHEET DATA HANDLING // ------------------------------------------------------------------------------------------------------------------// this.formatJSONtoSheet = function(sheetName, objects, headersRange, rowOffset){ var sheet = this._spreadsheet.getSheetByName(sheetName); var headers = this.normalizeHeaders(headersRange.getValues()[0]); var data = []; for (var i = 0; i < objects.length; ++i) { var values = []; for (j = 0; j < headers.length; ++j) { var header = headers[j]; values.push( ((header.length > 0) && (objects[i][header])) ? objects[i][header] : ""); } data.push(values); } var destinationRange = sheet.getRange(rowOffset, headersRange.getColumnIndex(), objects.length, headers.length); destinationRange.setValues(data); } this.normalizeHeaders = function(headers){ var keys = []; for (var i = 0; i < headers.length; ++i) { keys.push(this.normalizeHeader(headers[i])); } return keys; } this.normalizeHeader = function(header) { var key = ""; var upperCase = false; for (var i = 0; i < header.length; ++i) { var letter = header[i]; if (letter == " " && key.length > 0) { upperCase = true; continue; } if (upperCase) { upperCase = false; key += letter.toUpperCase(); } else { key += letter.toLowerCase(); } } return key; } // SHEET XTRL // ------------------------------------------------------------------------------------------------------------------// this.emptySheet = function(sheetName){ this._spreadsheet.getSheetByName(sheetName).clear(); } this.clearRange = function(sheetName, startRow,startCol,endRow,endCol){ this._spreadsheet.getSheetByName(sheetName).getRange(startRow,startCol,endRow,endCol).clear(); } this.showSheetMessage = function(sheetName, message, displayRow, displayCol){ this._spreadsheet.getSheetByName(sheetName).getRange(displayRow, displayCol, 1, 1).setValue(message); } }).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); -
Aish created this gist
Sep 5, 2011 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,133 @@ var spreadSheetID = "0Aq3G_rdwnXT1dFdBZUo5V3BWTGJOdVJySFQxOE1KRHc"; var ss = SpreadsheetApp.getActiveSpreadsheet() var configSheet = ss.getSheetByName("Settings"); //var customFieldLimit = 20; var host = "api.vworkapp.com" // These must be defined in the Tools sheet var apiToken = configSheet.getRange(2,2).getValue().trim(); function createJobs() { var jobsSheet = ss.getActiveSheet(); var templateName = jobsSheet.getRange(1, 2).getValue() + ''; var stepCount = jobsSheet.getRange(2, 2).getValue(); var cfCount = jobsSheet.getRange(3, 2).getValue(); // Each Job var count = 0; var lastRow = jobsSheet.getLastRow(); for (row = 6; row <= lastRow; row++) { var customerName = jobsSheet.getRange(row, 1).getValue() + ''; var duration = (jobsSheet.getRange(row, 2).getValue() * 60) + ''; if (customerName == "" || duration == "") { break; } var when = new Date(jobsSheet.getRange(row, 8).getValue()).toISOString(); var who = jobsSheet.getRange(row, 8).getValue(); // Steps var steps = []; for (i=0; i < stepCount; i++) { var startCol = 3; var stepName = jobsSheet.getRange(5, startCol + i).getValue() + ''; var stepAddress = jobsSheet.getRange(row, startCol + i).getValue() + ''; var stepLocation = geocodeAddress(stepAddress); var stepData = { name: stepName, location: {formatted_address: stepAddress, lat: stepLocation.lat, lng: stepLocation.lng} }; steps.push(stepData); } // Custom Fields var fields = []; for (i=0; i < cfCount; i++) { var startCol = 3 + stepCount; var cfName = jobsSheet.getRange(5, startCol + i).getValue() + ''; var cfValue = jobsSheet.getRange(row, startCol + i).getValue() + ''; var cfData = { name: cfName, value: cfValue }; fields.push(cfData); } var vWorkApp = new vWorkAppAPI(); //vWorkApp.createJob(customerName, templateName, duration, steps, fields); count++; } Browser.msgBox("vWorkApp", count + " jobs imported!", Browser.Buttons.OK); function geocodeAddress(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: '' }; } } }; // ------ // General util // ------ String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,""); } // ------ // vWorkApp API Class // ------ vWorkAppAPI = function() { this.posthttp = function(url, payload) { Logger.log(JSON.stringify(payload)); var advancedArgs = { method: "post", payload: JSON.stringify(payload), contentType: "application/json; charset=UTF-8" }; Logger.log("Creating Job. Response was:" + UrlFetchApp.fetch(url, advancedArgs)); }; }; vWorkAppAPI.prototype = { // -- // Creates a job in vWorkApp. // // 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 = "https://" + host + "/2.0/jobs?api_key=" + apiToken; this.posthttp(url, payload); } }