Search This Blog

Loading...
Showing posts with label Tracking. Show all posts
Showing posts with label Tracking. Show all posts

Tuesday, February 19, 2008

Universal Package Tracking in ColdFusion

A while back, I was in need of a universal package tracking tool that is not specific to the carrier. Almost every carrier provides some kind of an API but it can be a pain to set up each one separately. Fortunately, I found a nifty tracking tool which is universal and can be leveraged through RSS. It works with UPS, FedEx, USPS, or DHL/AirBorne without having to specify which carrier you need. Instead, it determines the carrier from the tracking number.

You can check it out at http://isnoop.net/tracking and the some details can be found at http://isnoop.net/blog/?p=19.

While this might not be good commercial solution, it is still usable for personal or a small site. The code below shows how to leverage this with ColdFusion. The Ajax/JavaScript implementation relies on Prototype.js. You can see it in action at http://blog.tech-cats.net/examples/universalPackageTracking.cfm and download it from http://blog.tech-cats.net/examples/universalPackageTracking.txt

The code is well documented and should be easy to read/understand:



<cfsetting enablecfoutputonly="yes">
<!--- Setup default parameters and constants --->

<!--- Is this call to the page from javascript (Ajax) --->
<cfparam name="url.isAjaxCall" default="false" />

<!--- The message to display while loading --->
<cfset loadingMessage = "Loading..." />

<!--- Default tracking number as a url variable (trackingNumber) --->
<cfparam name="url.trackingNumber" default="" />
<!---
 Tracking page url: very nice free tracking for all carriers that you can
 call to get an rss feed generated based on your tracking number as in
 'http://isnoop.net/tracking/index.php?t=85642012466&rss=1'.

 If you call it without setting the 'rss' variable as in:
 'http://isnoop.net/tracking/index.php?t=85642012466' you can see a nice
 google map of where in route your package is.
 
 This service works for UPS, FedEx, USPS, or DHL/AirBorne without having
 to specify the carrier as it determins it from the tracking number.
 --->
<cfparam name="trackingPageUrl" default="http://isnoop.net/tracking/index.php" />

<!---
Function:  parseRss

Arguments:

rssData  string (The string of rss xml retrieved with cfhttp)
debugMode boolean

Return Value:
An array of structures containing the parsed rss feed. Example:
array[1]
 link - the link from the rss item
 title - the title from the rss feed
 description - the description from the rss feed

Description:
Parses the RSS feed passed in --->
<cffunction name="parseRss" returntype="array" output="true" hint="Parses the RSS feed passed in">
 <cfargument name="rssData" type="string" required="true">
 <cfargument name="debugMode" type="string" required="false">

 <!--- Set default variables --->
 <cfset var xmlData = "">
 <cfset var result = arrayNew(1)>
 <cfset var x = "">
 <cfset var items = "">
 <cfset var xPath = "">
 <cfset var node = "">

 <cftry>
  <!--- Parse the data as xml --->
  <cfset xmlData = xmlParse(arguments.rssData)>

  <!--- Create xpath search string based on the xml root name --->
  <cfif xmlData.xmlRoot.xmlName is "rss">
   <cfset xPath = "//item">
  <cfelse>
   <cfset xPath = "//:item">
  </cfif>

  <!--- Get all the xml nodes matching the xpath search string --->
  <cfset items = xmlSearch(xmlData, xPath)>

  <!--- Loop through the found xml nodes and build an array of structures --->
  <cfloop index="i" from="1" to="#arrayLen(items)#">
   <cfset node = structNew()>
   <cfset node.link = items[i].link.xmlText>
   <cfset node.title = items[i].title.xmlText>
   <cfset node.description = items[i].description.xmlText>

   <cfset result[arrayLen(result) + 1] = duplicate(node)>
  </cfloop>
  <cfcatch>
  </cfcatch>
 </cftry>

 <cfreturn result>
</cffunction>

<cfoutput>
<!---
If this is an ajax call, get the tracking results trackingPageUrl specified above --->
<cfif url.isAjaxCall and url.trackingNumber neq ''>
 <cfhttp method="get" url="#trackingPageUrl#" result="test" charset="windows-1252">
  <!--- Set the 'rss' url variable --->
  <cfhttpparam name="rss" type="url" value="1" />
  <!--- Set the tracking url variable --->
  <cfhttpparam name="t" type="url" value="#url.trackingNumber#" />
 </cfhttp>

 <!--- Parse the rss feed from the contents returned by cfhttp --->
 <cfset rssFeed = parseRss(test.filecontent) />

 <cfif arraylen(rssFeed) gt 0>
  <div id="rssItem">
   <span id="description">Tracking data for tracking number
   '<span id="trackingNumber">#url.trackingNumber#</span>'</span>
  </div>
  <br />
  <!--- Loop through the contents of the rss feed and display them --->
  <cfloop index="i" from="1" to="#arrayLen(rssFeed)#">
  <div id="rssItem">
   <span id="description">#rssFeed[i].description.replaceall("Package update on ", "")#</span>
   
  </div>
  </cfloop>
 <cfelse>
  <div id="rssItem">
   <span id="description">No tracking data found for tracking number
   '<span id="trackingNumber">#url.trackingNumber#</span>'</span>
  </div>
 </cfif>
<cfelse>
 <!--- This is not an ajax call, so display a form for the user to enter a tracking number --->
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript" src="/js/prototype.js"></script>
 <style type="text/css">
 span##trackingNumber { font-weight: 700; }
 div##statusContainer { position:absolute;left:0px;top:0px;width:100%;height:10px; }
 div##statusMessageContainer { position:absolute;background-color:##000000;color:white;width:70px;font-family:Arial, Helvetica, sans-serif;padding:2px;left:0px; }
 </style>
 </head>
 <body>
 Browser Bookmarklet:
 <a href="javascript:(function(){var url='http://#cgi.http_host##cgi.script_name#';searchString=prompt('Enter your tracking number:','');searchString=((searchString==null)?'':searchString.replace(/^(\s+)?(.*?)(\s+)?$/gi,'$2'));var params='?isAjaxCall=true&t=';if(searchString!=''){if(location.href.indexOf(url)==-1){var win=window.open(url+params+escape(searchString));}else{location.href=url+params+escape(searchString);}}})();">
 Track Packages
 </a>
 <br /><br />
 <form id="trackingForm" name="trackingForm" method="get" action="#cgi.script_name#" class="ajaxForm">
 <input type="hidden" id="isAjaxCall" name="isAjaxCall" value="true" />
 <input type="text" id="trackingNumber" name="trackingNumber" value="1Z04WF350314328154" />
 <input type="submit" id="getTrackingResults" name="getTrackingResults" value="Track" />
 </form>
 <br />
 <!-- Results container that will be updated with the results of the request --->
 <div id="resultsContainer" class="ajaxContent"></div>
 <br />
 <!-- Status container that will be display durring processing --->
 <div id="statusContainer" style="display: none;" class="ajaxStatus">
  <div id="statusMessageContainer">Loading...</div>
 </div>
 <script language="javascript" type="text/javascript">
 var trackingForm = Class.create();
 trackingForm.prototype = {
  ajaxContainerElement: 'div',
  formID: '',
  ajaxUrl: '',
  resultsContainer: '',
  /*
  Function: initialize
  Description: Performs various intiliazion tasks for the form
  */
  initialize: function() {
   var ajaxFormsList = $$('form.ajaxForm');
   var ajaxContainersList = $$(this.ajaxContainerElement + '.ajaxContent');

   if (ajaxFormsList.length > 0 && ajaxContainersList.length > 0) {
    this.formID = ajaxFormsList[0].id;
    this.ajaxUrl = $(this.formID).action;
    this.resultsContainer = ajaxContainersList[0].id;

    // Tie the submit event to the submitForm function
    $(this.formID).observe('submit', this.submitForm.bind(this));

    // Reset the form
    $(this.formID).reset();

    // Activate the first element on the form
    $(this.formID).findFirstElement().activate();
   }
  },
  /*
  Function: submitForm
  Description: Submits the form
  */
  submitForm: function(event) {
   // Serialize the form parameters to pass them along as part of the form submission
   var params = $(this.formID).serialize(true);

   // Check if the tracking number is empty
   if (!params.trackingNumber.empty()) {
    // Disable the form
    $(this.formID).disable();

    // Make an ajax request passing it the serialized form    
    new Ajax.Updater(
     $(this.resultsContainer),
     this.ajaxUrl,
     {
     method: 'get',
     parameters: params,
     onFailure: this.reportError.bindAsEventListener(this),
     onSuccess: this.processResults.bindAsEventListener(this),
     evalScripts: true
     }
    );
   }

   // Prevent the form from being submitted
   Event.stop(event);
  },
  /*
  Function: processResults
  Description: Processes the server results
  */
  processResults: function() {
   // Enable the form
   $(this.formID).enable();
 
   // Reset the form
   $(this.formID).reset();

   // Activate the first element on the form
   $(this.formID).findFirstElement().activate();
  },
  reportError: function(request){}
 };

 Event.observe(window, 'load', function() {
  var ajaxStatusContainersList = $$('div.ajaxStatus');
  var ajaxStatusContainer = '';

  // Create an instance of the form object defined above
  trackingFormInstance = new trackingForm();

  if (ajaxStatusContainersList.length > 0) {
   ajaxStatusContainer = $$('div.ajaxStatus')[0].id;

   Ajax.Responders.register({
    onCreate: function() {
     $(ajaxStatusContainer).show();
    },
    onComplete: function() {
     $(ajaxStatusContainer).hide();
    }
   });
  }
 });
 </script>
 </body>
 </html>
</cfif>
</cfoutput>

Saturday, January 19, 2008

Kick Butt Project Management with Assembla

A few days ago I blogged about Tools for Better Project Management and Organization and this post offers an alternative application for project management. As I mentioned in my pervious post, I highly recommend RedMine as your web based project manager. RedMine is great if you want something that you have complete control over. With complete control however, come the pain of having to install it, configure it and worry about backups. So today, as a part of a project I am involved in, I set on a quest to find something that would be as good as RedMine but without the installation and configuration involved. After evaluating a few different web based project management application (Unfuddle, CodeSpaces, and a few other not worth mentioning), I tried Assembla. I had put in on my list to evaluate a while ago so it was not a new find but I am sorry I did not try it earlier. I am not going to get into much detail on the features but I have to say, the number of features is impressive. Best of all you get all this including 500mb of space for free! Here is a quick overview:

The start page:

Boyan Kostadinov- start -Assembla_1200795708018

The dashboard

Ensemble Video- index -Assembla_1200795731143 

Time entry

Ensemble Video- tasks -Assembla_1200795740736

Milestones

Ensemble Video- Milestones -Assembla_1200795878080

Online chat

Ensemble Video- chat -Assembla_1200795889986

Files repository

Ensemble Video- files -Assembla_1200795897143

Wiki

Ensemble Video- Space Home -Assembla_1200795904674

Trac/SVN integration

Ensemble Video- trac_tool -Assembla_1200795912877

Images repository

Ensemble Video- Images -Assembla_1200795923080

Blog integration with Mephisto

Ensemble Video- mephisto_tool -Assembla_1200795932439

Alerts

Ensemble Video- alerts -Assembla_1200795964174

Additional add-ons

Ensemble Video- edit -Assembla_1200795987596

Monday, January 14, 2008

Tools for Better Project Management and Organization

Organization and project management have turned into very important skills even for developers. With growing demand for automation and the spread of information, companies are consistently working on new ways to organize, restructure and make data accessible.

If you are a developer, at some point in your career, it is very likely that you have run into trouble with a project running over the perceived time frame and/or original specifications. Everyone in the programming word has heard the horror stories of never ending features and lack of project requirements. I would not be honest if I said this has not happened to me. C'est la vie, as the French would say but I am trying to learn something from it.

Over the past few months, I have started to use several tools to keep track of project requirements/bugs/features, log tasks I have worked on/accomplished, and billing related data (tasks, time, invoices, etc).

Project Management

After evaluating about 20 open source projects (thank you SourceForge), and some commercial products, I settled on using a open source project called RedMine. Here is the overview of RedMine's features directly from the web site:

  • Multiple projects support
  • Flexible role based access control.
  • Flexible issue tracking system
  • Gantt chart and calendar
  • News, documents & files management
  • Feeds & email notifications.
  • Per project wiki
  • Per project forums
  • Simple time tracking functionality
  • Custom fields for issues, projects and users
  • SCM integration (SVN, CVS, Mercurial, Bazaar and Darcs)
  • Multiple LDAP authentication support
  • User self-registration support
  • Multilingual support
  • Multiple databases support

You can see a full list of features plus screen shots at the Features page.

Task Logging

The nice folks at Lifehacker have put together an excellent little script for logging your daily tasks. The basics of it are:

  • You setup the script by editing the .vbs file, create a shortcut to it and setup a shortcut key
  • You press the shortcut key and an input box pops up
  • You enter the task/item you want to log and the script writes it with a date plus time to your log file

You can find the full details about it and how to set it up at Geek to Live: Quick-log your work day

Keeping Track of Billing Data

There is more than one tool for the job so choices highly depend on features, preference and price. For my need, I have found that a tool will need a least a minimum set of features:

  • Keep track of clients and projects
  • Log time and description per time spend on project
  • Generate invoices for selected time frame

Considering the above, I can recommend a small windows application called Billing Tracker. The pro version costs $89. I am sure there are plenty of free web based applications that fall in this category too.

Bonus - Keeping Track of your Daily Tasks

Even with all the above, keeping on track with your projects and life can be hard. Not everything falls into a project and sometimes you do not want to create the smallest task as part of your project tracking. On the other hand, if you don't write it down, you will forget it, I do.

So here is where a To-Do list comes in. There are many, many free, web based to-do applications. I use Remember The Milk. Some nice features are:

  • Offline access (with Google Gears)
  • Task categories, lists and locations
  • Reminders
  • Integration with GMail (only for Firefox with an extension) and Google Calendar
  • RSS of your tasks


Conclusion

No matter how you decide to go about it, most important than everything else is your discipline to track and log your work.