Network Q is the used car brand of Vauxhall that offers the consumer piece of mind when buying a used car.  Vauxhall, a UK based marque owned by General Motors (GM) allows Vauxhall dealers to list their Network Q qualifying vehicles on their website.  In my previous post: Third Party Stock/Inventory Data Feeds I talked about how important and how much choice there is now when displaying your vehicles online, with the Network Q website, its free for dealers to list their vehicles.  This integration saves the dealer manually uploading the vehicles one by one though the admin area and can be fully automated by taking the stock from their DMS (Dealer Management System) such as Kerridge/ADP, Pinnacle etc.

The vehicles need to undergo a set of multi point checks and meet some other requirements such as age and mileage.

Vauxhall use a company called Datafirst who are based in France to manage the Network Q website.  Datafirst offer a webservice API (Application Programming Interface) for you to upload the stock.  Unlike most third party websites that accept feeds the API the Datafirst provide allows the updates to be made in real time.  From a developer’s perspective this is far better to work with than CSV (Comma Separated Values) file.  The biggest problem with CSV files is that they normally get transferred via FTP (File Transfer Protocol) which makes it difficult to determine whether the transfer succeeded or not.

The Datafirst API is session based and a number of calls need to be made in a particular order.

The main decision you need to make is how the webservice gets called.  Normally I would have built an SSIS (SQL Server Integration Services) package to deal with this by utilising the webservice and xml tasks.  But due to a number of reasons, such as deployment etc. I opted for a simple console application that is run from Windows scheduled task manager.

The 4 steps required are:

  1. Initialise Session
  2. Send Vehicle Data
  3. Send Vehicle Photos
  4. Finalise Session

Something I recommend is that you put good error checking at every stage plus a catch all to catch unhandled errors.  Is not uncommon to find that Datafirst’s service is unavailable from time to time. The error checking will alert you by email so you can rerun or notify someone of the failure.

I do this by adding the following piece of code:

   1: AppDomain currentDomain = AppDomain.CurrentDomain;
   2: currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
   3:  
   4: static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
   5:         {
   6:             StringBuilder sb = new StringBuilder();
   7:             
   8:             Exception ex = ((Exception)(args.ExceptionObject));
   9:             sb.Append("****** Unhandled Exception ******<br/><br/>");
  10:             sb.Append("<br/>ExceptionType: " + ex.GetType().FullName);
  11:             sb.Append("<br/>HelpLine: " + ex.HelpLink);
  12:             sb.Append("<br/>Message: " + ex.Message);
  13:             sb.Append("<br/>Source: " + ex.Source);
  14:             sb.Append("<br/>StackTrace: " + ex.StackTrace);
  15:             sb.Append("<br/>TargetSite: " + ex.TargetSite);
  16:  
  17:             Exception ie = ex;
  18:             while (!((ex.InnerException == null)))
  19:             {
  20:                 ie = ie.InnerException;
  21:                 sb.Append("<br/><br/>****** Inner Exception ******<br/><br/>");
  22:                 sb.Append("<br/>ExceptionType: " + ie.GetType().Name);
  23:                 sb.Append("<br/>HelpLine: " + ie.HelpLink);
  24:                 sb.Append("<br/>Message: " + ie.Message);
  25:                 sb.Append("<br/>Source: " + ie.Source);
  26:                 sb.Append("<br/>StackTrace: " + ie.StackTrace);
  27:                 sb.Append("<br/>TargetSite: " + ie.TargetSite);
  28:             }
  29:  
  30:             Utilities.SendEmail(Utilities.Preferences.ErrorEmails, sb.ToString());
  31:         }

The next thing to do is set up the objects we need to pass over.  First off we create a base class with only one method that serialises the object.

   1: public abstract class RequestBase
   2:     {
   3:         public string SerialiseToString()
   4:         {
   5:             XmlSerializer xs = new XmlSerializer(this.GetType());
   6:             XmlWriterSettings xws = new XmlWriterSettings();
   7:             xws.Encoding = new UTF8Encoding(false);
   8:             string xml = string.Empty;
   9:  
  10:             using (MemoryStream ms = new MemoryStream())
  11:             {
  12:                 using (XmlWriter xw = XmlWriter.Create(ms, xws))
  13:                 {
  14:                     xs.Serialize(xw, this);
  15:                     xw.Close();
  16:                 }
  17:                 xml = Encoding.UTF8.GetString(ms.ToArray());
  18:             }
  19:  
  20:             return xml;
  21:         }
  22:     }

 

Then the objects, here is the vehicle request object that takes a collection of vehicles.

   1: [Serializable]
   2: [XmlRoot("REQUEST")]
   3: public class VehicleRequest : RequestBase
   4: {
   5:     [XmlAttribute("NAME")]
   6:     public string NAME { get; set; }
   7:     [XmlAttribute("SESSID")]
   8:     public string SESSID { get; set; }
   9:  
  10:     [XmlArrayAttribute("UVSTOCK")]
  11:     [XmlArrayItemAttribute("USEDVEH")]
  12:     public List<Vehicle> Vehicles { get; set; }  
  13: }

Once all the objects are specified and marked-up with the relevant attributes we need to send them over to the webservice.  Now, normally it would be easy to create a proxy class and call the webservice direct from code, but the way that Datafirst work is just to receive POX (Plain Old Xml) via HTTP requests and responses.  To send the data then we need to us the HttpWebRequest from the System.Net namespace like so:

   1: public static string PostRequest(string xml, string uri)
   2: {
   3:     Uri address = new Uri(uri);
   4:     string responseXml = string.Empty;
   5:  
   6:     HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
   7:     request.Method = "POST";
   8:     request.ContentType = "text/xml";
   9:  
  10:     byte[] byteData = UTF8Encoding.UTF8.GetBytes(xml.ToString());
  11:  
  12:     request.ContentLength = byteData.Length;
  13:  
  14:     using (Stream postStream = request.GetRequestStream())
  15:     {
  16:         postStream.Write(byteData, 0, byteData.Length);
  17:     }
  18:  
  19:     // Get response   
  20:     using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
  21:     {
  22:         StreamReader reader = new StreamReader(response.GetResponseStream());
  23:         responseXml = reader.ReadToEnd();
  24:     }
  25:     UpdateSession(responseXml);
  26:  
  27:     return responseXml;
  28: }

Now you might be wondering how do the images get transmitted.  Well, we need to encode the images in base64.

   1: internal static string ConvertImageToBase64(string path)
   2: {
   3:     byte[] byteArray = null;
   4:  
   5:     using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
   6:     {
   7:         byteArray = new byte[fs.Length];
   8:         fs.Position = 0;
   9:         fs.Read(byteArray, 0, (int)fs.Length);
  10:     }
  11:  
  12:     return Convert.ToBase64String(byteArray);
  13: }

 

It’s recommended due to the amount of vehicle images that can be transferred to only send over modified or new images, so you’ll need to update the database with a sent flag or similar.  Alternatively, what I did that works well is to set the creation time of image to some time in the past and the next time you look for the images only get images without that date.  This is extremely useful if this application was to be deployed to a computer within a dealership or you don’t want to mess around with databases.

So there you have it.  Not difficult but different to how other people do it, and in my opinion a lot better then the CSV/FTP model I tend to come across.

Automotive Analytics

There are a number of analytical software packages on there on the market and most of them offer an on-demand, Software-as-a-Service (SaaS) version.

For those who don't know, Google offer a free way of measuring activity on your website, called Google Analytics.  They offer this service for free to encourage you to advertise with them via there Pay-per-Click (PPC) advertising.

The one major downside (if you see it as one), is that Google can use the data you gather to help them evolve there products and services in the future.  If this doesn't bother you then I would suggest you get yourself set up.

Event Tracking

By default, Google Analytics mainly tracks page views and visitor information but sometimes you need more information about what’s happening on your website.  Currently in beta and therefore not fully available, Google have a new event tracking API (Application Programming Interface) that allows you to track custom events on your website.  For example, you may want to know how many users have watched all of video or how many have bombed out after just a few seconds, or how many users have searched for a certain make or model from your used car search.

Even though it’s in beta and the information isn’t available in the analytics I believe that Google are still recording the data you capture if you start using the API now.  This is how to implement it to track how many people are searching for each make and model.

First of all you need to make sure that you are using the latest analytics API, the JavaScript include is called ga.js not the urchin.js which is the older one.

Now lets assume you have something like this simple search:

UsedSearch

On the “Search Button” we need to add a little JavaScript to the onclick event:

   1: onclick="pageTracker._trackEvent('Search', 'Quick Search', Make.value + '|' + Model.value);"

A better way of attaching this code is unobtrusively via an attachevent.  Here is an example using JQuery:

   1: $("#SearchButton").click(function()
   2: {
   3:     pageTracker._trackEvent('Search', 'Quick Search', $('#Makes').val() + '|' + $('#Models').val());
   4: });

The above code simply call the _trackEvent function of the pageTraker object and passes in a few parameters:

  • category (required)  - The name you supply for the group of objects you want to track
  • action (required) - A string that is uniquely paired with each category, and commonly used to define the type of user interaction for the web object.
  • label (optional)  - An optional string to provide additional dimensions to the event data.
  • value (optional) - An integer that you can use to provide numerical data about the user event.

I’ve assigned a category of “Search” and the action of “Quick Search”; this allows us to categorise for advanced searches, new vehicle searches etc. I then specify the label that is the Make and Model values concatetated by a | character. e.g. Volkswagen|Golf.  I have omitted a value for this type of tracking.

I must note that this type of tracking isn’t new and is included in the more advanced enterprise analytics packages all of which come with hefty price tags compared to the freeness of Google.  In addition, this type of tracking would be easy to implement if you were to do your own custom bespoke solution.

Here is the link to the full Google documentation.

    Follow stevetayloruk on Twitter

    If we've worked or done business together then please connect to my LinkedIn. Connect to me on LinkedIn