Access your Nike+ run data using C# and .NET

Here’s a really simple way to use your Nike+ run data on your own website using C#.

Firstly, get your Nike+ ID and test out your feed by pointing your browser to the Nike+ run list API URL as demonstrated below. You can find your unique ID using the instructions listed in this post.  It’s worth noting that older accounts have a numeric ID, while newer subscribers have a GUID-like string like mine.

http://nikerunning.nike.com/nikeplus/v1/services/widget/get_public_run_list.jsp?userID=14fa3afc-8687-47ef-9899-75e8d090284d

If, after requesting this URL, you can see your run data in XML format, the ‘share more’ setting is already enabled in Nike+.  If not, you either have the wrong ID or you need to make your profile data public. You can do this within the privacy options within the Nike+ web application.

Here’s a C# class I wrote to read the XML data into a strongly typed object called RunViewModel, consisting of ‘summary’ and ‘run list’ objects:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Xml;using System.Xml.Linq;using System.Net;namespace com.kewney.examples.nikeplus{        public class RunViewModel    {        public List Runs { get; set; }        public Summary Summary { get; set; }    }    public class Run    {        public DateTime startTime { get; set; }        public float distance { get; set; }        public int duration { get; set; }        public DateTime syncTime { get; set; }        public float calories { get; set; }        public string name { get; set; }        public string description { get; set; }        public string howFelt { get; set; }        public string weather { get; set; }        public string terrain { get; set; }        public string intensity { get; set; }        public string gpxId { get; set; }        public string equipmentType { get; set; }        public string durationDesc        {            get            {                return TimeSpan.FromSeconds(duration / 1000).ToReadableString();            }            set { }        }             }    public class Summary    {        public int runs { get; set; }        public float distance { get; set; }        public int runDuration { get; set; }        public float calories { get; set; }        public int duration { get; set; }    }    public static class RunMethods    {        private static string url = "http://nikerunning.nike.com/nikeplus/v1/services/widget/get_public_run_list.jsp";        ///         /// Public method to obtain run data        ///         /// Nike+ ID        /// Run view model, containing run list and summary        public static RunViewModel GetRuns(string id)        {            return new RunViewModel            {                Runs = GetRunList(id),                Summary = GetSummaryInfo(id)            };        }        private static List GetRunList(string id)        {            XmlReaderSettings xrs = new XmlReaderSettings();            xrs.XmlResolver = new XmlUrlResolver();            xrs.ValidationType = ValidationType.DTD;            try            {                using (XmlReader xtr = XmlTextReader.Create(string.Format("{0}?userID={1}", url, id), xrs))                {                    XDocument xd = XDocument.Load(xtr);                    return (from entry in xd.Descendants().Where(x => x.Name == "run")                            select new Run                            {                                startTime = (DateTime)entry.Element("startTime"),                                distance = (float)entry.Element("distance"),                                duration = (int)entry.Element("duration"),                                syncTime = (DateTime)entry.Element("syncTime"),                                calories = (float)entry.Element("calories"),                                name = (string)entry.Element("name"),                                description = (string)entry.Element("description"),                                howFelt = (string)entry.Element("howFelt"),                                weather = (string)entry.Element("weather"),                                terrain = (string)entry.Element("terrain"),                                intensity = (string)entry.Element("intensity"),                                gpxId = (string)entry.Element("gpxId"),                                equipmentType = (string)entry.Element("equipmentType")                            }).OrderByDescending(x => x.startTime).ToList();                    xtr.Close();                }            }            catch (WebException ex)            {                return new List();            }        }        private static Summary GetSummaryInfo(string id)        {            XmlReaderSettings xrs = new XmlReaderSettings();            xrs.XmlResolver = new XmlUrlResolver();            xrs.ValidationType = ValidationType.DTD;            try            {                using (XmlReader xtr = XmlTextReader.Create(string.Format("{0}?userID={1}", url, id), xrs))                {                    XDocument xd = XDocument.Load(xtr);                    return (from entry in xd.Descendants().Descendants("runListSummary")                            select new Summary                            {                                runs = (int)entry.Element("runs"),                                distance = (float)entry.Element("distance"),                                runDuration = (int)entry.Element("runDuration"),                                calories = (float)entry.Element("calories"),                                duration = (int)entry.Element("duration")                            }).FirstOrDefault();                    xtr.Close();                }            }            catch (WebException ex)            {                return new Summary();            }        }    }}

Here’s how to use the data:

RunListViewModel objMyRuns = BlogMVC.Models.RunMethods.GetRuns("14fa3afc-8687-47ef-9899-75e8d090284d")

From here, you can look through your run list and use the objMyRuns.Summary.Item to access the summary data

If you’re wondering what the string extension ToReadableString() looks like, here it is:

 public static string ToReadableString(this TimeSpan span)    {        string formatted = string.Format("{0}{1}{2}{3}",            span.Days > 0 ? string.Format("{0:0}d ", span.Days) : string.Empty,            span.Hours > 0 ? string.Format("{0:0}h ", span.Hours) : string.Empty,            span.Minutes > 0 ? string.Format("{0:0}m ", span.Minutes) : string.Empty,            span.Seconds > 0 ? string.Format("{0:0}s", span.Seconds) : string.Empty);        if (formatted.EndsWith(", ")) formatted = formatted.Substring(0, formatted.Length - 2);        return formatted;    } 

Have fun and here is an example of it in use.

Additional Information: More URLs from the Nike+ API

I haven’t included any tutorial information on the other functionality of the other API URLs, but have listed them below for you to test.

URL Description
https://secure-nikeplus.nike.com/nikeplus/v1/services/widget/generate_pin.jhtml?login=&password= Authenticates the user and places a cookie on their system for later use.
http://secure-nikeplus.nike.com/nikeplus/v1/services/app/get_user_data.jhtml Obtains data about the user (e.g. name, gender, country)
https://secure-nikeplus.nike.com/nikeplus/v1/services/app/goal_list.jhtml List the goals of the selected user
https://secure-nikeplus.nike.com/nikeplus/v1/services/app/run_list.jhtml Lists the users runs and statistics, as used in this post
https://secure-nikeplus.nike.com/nikeplus/v1/services/app/get_run.jhtml?id= A detailed view of the run, including numbers from the run list
https://secure-nikeplus.nike.com/nikeplus/v1/services/widget/get_challenges_for_user.jhtml Lists the user’s challenges
https://secure-nikeplus.nike.com/nikeplus/v1/services/app/personal_records.jhtml Lists the user’s personal records

Abstract classes vs. interfaces in C#

Introduction

Functionality in abstract classes and interfaces in C# is fairly similar, however use of each has supporting and opposing arguments. This article discusses the theoretical aspects of these programming concepts backed up with a working example in C# at the end of this post.

There are three areas to consider when deciding which to implement for a specific purpose within your project:

1. Inheritance

Simply put, a class can only inherit from one other class. If you inherit from an abstract class, more can be achieved. Conversely, a class can implement an unlimited number of interfaces as well as still being able to inherit from a base class.

2. Structure

Interfaces offer a greater degree of design flexibility because they can be used by any class regardless of type.

3. Versioning

As mentioned, an abstract class can contain interfaces and also have the ability to inherit from base classes. This makes version control an awful lot easier.

Changing an interface will result in all classes which depend on it breaking. Once this has been used, its contents and functionality is permanant. Code based on interfaces can only be improved or extended by added new interfaces.


Scenarios where an abstract class is suitable

  • The class you’re developing will be distributed to many other developers or clients or you’re creating an API.
  • You wish to create a common base class for a family of types.
  • You want to provide a default behaviour

Scenarios where an interface is suitable

  • You’re creating a class which will be used in an internal project which won’t be shared with a large number of developers or used in an API
  • You’re looking to provide polymorphic behaviour without subclassing or you want a specific type to display multiple behaviours.

If you are looking for a more detailed answer to this question, what is difference between abstract class and interface, then visit this link for further reading.


Example

Here is an example using code.  I’ve created a Product abstract class and an IProduct interface.  I’ve commented throughout the code and included a test class at the end. 

In the test class, the DisplayPrice method when using the abstract class displays the product price for staff, applying no markup, whereas it shows the full retail price when the customer interface is called. 

The Abstract Class ‘Product’

namespace com.kewney.examples.abintex{    ///     /// Summary for Product    ///     public abstract class Product    {        ///         /// Define Fields        ///         protected int pId;        protected String pName;        protected String pDescription;        protected decimal pCost;        ///         /// properties        ///         public abstract int ID        {            get { }            set { }        }        public abstract String ProductName        {            get { }            set { }        }        public abstract String ProductDescription        {            get { }            set { }        }        public abstract decimal ProductBuyPrice        {            get { }            set { }        }        //Example Method        public String Locate()        {            return String.Format("Product {0}: {1} located", pId, pName);        }        //The price will be different when displayed to the consumer as this will have margin added, so let's delegate the price calculation to each implementation        public abstract decimal DisplayPrice();    }}

The Interface ‘IProduct’

namespace com.kewney.examples.abintex{    ///     /// Summary for IProduct    ///     public interface IProduct    {        int ID        {            get { }            set { }        }        String ProductName        {            get { }            set { }        }        String ProductDescription        {            get { }            set { }        }        decimal ProductBuyPrice        {            get { }            set { }        }        String Locate();        decimal DisplayPrice();    }}

Inherited Objects #1: Product_Staff

namespace com.kewney.examples.abintex{    ///     /// Summary for Product_Staff    /// Note: In this example, the DisplayPrice is calculated using the abstract class    ///         public class Product_Staff : Product // Inheriting from the abstract class Product    {        //It's inheriting from the abstract class therefore no properties or fields should be present here        public Product_Staff() { }        public override int ID        {            get            {                return pId;            }            set            {                pId = value;            }        }                public override String ProductName        {            get            {                return pName;            }            set            {                pName = value;            }        }        public override String ProductDescription        {            get            {                return pDescription;            }            set            {                pDescription = value;            }        }        public override decimal ProductBuyPrice        {            get            {                return pCost;            }            set            {                pCost = value;            }        }        //Locate method implemented in the abstract class        public new String Locate()        {            return base.Locate();        }              //This abstract method that is different for staff than for customers so I'm overriding it here        public override decimal DisplayPrice()        {            return String.Format("Product {0} price for staff is {1}, calculated via abstract class", base.pName, (base.pCost * 1));        }    }}

Inherited Objects #2: Product_Customer

namespace com.kewney.examples.abintex{    ///     /// Summary for Product_Customer    /// Note: In this example, the DisplayPrice is calculated using the abstract class    ///     public class Product_Customer : IProduct // Inheriting from the interface IProduct    {        //Properties and fields defined here        protected int pId;        protected String pName;        protected String pDescription;        protected decimal pCost;        public Product_Customer()        {            //            // TODO: Add constructor here            //        }        public int ID        {            get            {                return pId;            }            set            {                pId = value;            }        }        public String ProductName        {            get            {                return pName;            }            set            {                pName = value;            }        }        public String ProductDescription        {            get            {                return pDescription;            }            set            {                pDescription = value;            }        }        public decimal ProductBuyPrice        {            get            {                return pCost;            }            set            {                pCost = value;            }        }        //N.B Locate is completed within the object this time        public String Locate()        {            return String.Format("Product {0} found", pName);        }        //This abstract method that is different for staff than for customers so I'm overriding it here        public override decimal DisplayPrice()        {            return String.Format("Product {0} price for staff is {1}, calculated via interface", base.pName, (base.pCost * 2));        }    }}

Example to test Interface and Abstract Class

namespace com.kewney.examples.abintex{    public class TestAbstractAndInterface    {        private void TestInterface() // As Customer        {            IProduct prod;            Product_Customer prod = new Product_Customer();            prod = emp1;            prod.ID = 1;            prod.ProductName = "simfree.net Account";            prod.ProductDescription = "the easiest, most cost effective way to use text messaging in business";            prod.ProductBuyPrice = 24.00;            //call the DisplayPrice method            MessageBox.Show(prod.DisplayPrice().ToString());        }        private void TestAbstract() // As Staff        {            Product prod;            prod = new Product_Staff();                        prod.ID = 1;            prod.ProductName = "simfree.net Account";            prod.Product Description = "the easiest, most cost effective way to use text messaging in business";            prod.ProductBuyPrice = 24.00;            //call the DisplayPrice method            MessageBox.Show(prod.DisplayPrice().ToString());        }    }}

If you’re looking for more information or are interested in a view from a developer taking a SOLID perspective, check out Pradeep’s post here.

There’s a fine line between a handshake and holding hands

I was searching for a new TV and came across this photo on Samsung’s flickr site. It got me wondering, at what point does a handshake become holding hands?

Samsung Hand Holding

 

Installing Windows Home Server 2011 on HP MediaSmart EX470 or EX475

This article provides steps to assist you with the installation of Windows Home Server 2011 on an HP MediaSmart EX470 or EX475. Please note that the minimum RAM requirement of this OS is 2GB.

There are several useful guides out there explaining how to install Windows Home Server 2011 on an HP MediaSmart using a flash drive; including Sean Daniel’s in-depth post here. However, if you have a desktop PC handy, this is an alternative method which lets you to prepare the drive in another system and avoid the USB approach altogether. Since the MediaSmart doesn’t have a display, I found this method preferable.

What you’ll need:

  • A desktop system with a SATA drive connector
  • Windows Home Server 2011 installation CD

How to do it:

  • Remove the hard disk from your MediaSmart and connect it to your desktop PC’s SATA connector. Make sure the drive is greater than 160GB in capacity.
  • Insert your Windows Home Server 2011 DVD and boot the PC
  • Use the appropriate key to enter boot device selection and select the CD/DVD drive
  • Once the installer has loaded, select “New Install” and proceed with the default options.
  • Agree to the EULA and start the installation.
  • Switch off this PC during the first reboot – Very important.
  • Remove this drive from your desktop PC and place it into the MediaSmart server in the bottom bay.
  • Boot the server and wait between 5 and 15 minutes
  • Check your “Network” view on your local PC and the MediaSmart should appear as “SERVER”
  • If you’re using Windows 7, right click SERVER and select “Go to administration website”, alternatively, navigate your browser to http://server/connect
  • Follow the rest of the installation instructions until your server is ready to use.

Additional notes:

Make sure your disable the SMI USB Disk in your computer management > diagnostics > devices or else you’ll be harassed with “low disk space” warnings.

Quick Fix: Unable to open image attachments in Outlook 2007/2010

If you receive an “unable to open document” error when attempting to open a .TIF file in Microsoft Outlook 2007 or 2010, it may be due to a limitation on the number of documents Windows can save in the “Temporary Internet Files/OLKE” folder. 

The limit for this folder seems to be 100 files, so after downloading your 100th attachment, you will be presented with this error message until you purge the contents of the folder.

In our case, the contents of the folder looked like this (as it was a fax mailbox):

fax.tif(97), fax.tif(98) and fax.tif(99)

In order to resolve this issue, you need to go and delete some of these files using the following steps:

  • Find the location of the folder in which these files are stored by navigating to the registry (Start, Run, RegEdit) and finding the following key:  HKEY_CURRENT_USER > Software > Microsoft > Office > 14.0 > Outlook > Security > Value Name: OutlookSecureTempFolder > Value Type: REG_SZ > Value Data: Temporary Internet Files
  • Copy the value to an explorer window, delete all files in the folder.
     For Example: ‘C:Documents and Settingsuser nameLOCAL SettingsTemporary Internet FilesOLK18F’

N.B: The exact registry location will vary depending on your version of Windows. See below for an example on Windows 7.

Outlook 2010 Temporary File Location

Once you’ve deleted these files, restart Outlook and the problem should be resolved.

If you need more help, the official Microsoft knowledgebase article covering this issue can be found here.