Category: foursquare

Foursquare–part 4–conclusions

Part 1: beginning using API: http://msprogrammer.serviciipeweb.ro/2014/01/27/foursquarepart-1-of-4/

Part 2: Trying PC version: http://msprogrammer.serviciipeweb.ro/2014/02/16/foursquarepart-2-of-4/

Part 3: Improving PC version: http://msprogrammer.serviciipeweb.ro/2014/02/24/foursquarepart-3-of-4/

Part 4: conclusions( this blog post)

 

First conclusion : web is media of choice . The programmers makes API easier for connecting via Web, not via every device. ( although FourSquare have developed native classes  for IOS / Android )

Second conclusion :  with reasonable effort  you can make a version that runs on every platform ( ok, not trying yet Windows phone, but it should not be so different – requires the browser to login). And you will find some implementation that parses the Web Json.

Third  conclusion : Be aware of API changes and prepare for a sustainable effort if the platform is making breaking changes .Those can come from request (https://developer.foursquare.com/overview/versioning : << As of January 28, 2014, requests that do not include a v parameter will be rejected.  >> ) or from response ( http://msprogrammer.serviciipeweb.ro/2014/02/09/solving-no-parameterless-constructor-defined-for-type-of-system-string/ , transforming a string icon property into a class)

 

Final:

It is nice to play with a third party and get the data programmatically.   For example, with the program I just show , I save a text file with my daily activities and put on onedrive :

20140301 214229,Cinema City,
20140301 213600,AFI Palace Cotroceni,
20140301 203400,Ministerul Finanțelor Publice,
20140301 170706,casa,
20140301 163738,Carrefour,
20140301 151818,Gett’s Color Bar,
20140301 142525,Cărturești,
20140301 141331,AFI Palace Cotroceni,
20140301 133653,Catedrala Sfântul Iosif (St. Joseph’s Cathedral),
20140301 122913,World Class Health Academy Downtown,
20140301 120617,McDonald’s,
20140301 103729,Piața Romană,
20140301 095350,Dedeman,
20140301 075136,Dedeman,
20140301 053653,casa,

 

And relax 

Photo

 

 

Made by Andrei Ignat, http://msprogrammer.serviciipeweb.ro
Source code at https://github.com/ignatandrei/4SqDayHistory
View online at http://fsq.apphb.com

FourSquare–part 3 of 4

Last time I authenticated the user within FourSquare use by running the browser.But it is not so good because you have no control over what the user does.

So the application must authenticate within – and here comes the GUI with a WindowsForm( simple for me than WPF) and a WebBrowser control.

The code for the authentication is  simple. On the  <WebBrowser>_DocumentCompleted I verify that the url is the same with the URL that I use previously to retain the authentication for each user( http://fsq.apphb.com/ )

private void wb4Sq_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (e.Url.ToString().StartsWith(this.finalUrl))
            {
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
        }

 

 

On the main console thread I wait to the WebBrowser to finish on a separate thread:

 var th = new Thread(() =>
            {

                var clicker = new WebBrowser { ScriptErrorsSuppressed = true };


                clicker.Visible = true;
                clicker.DocumentCompleted += clicker_DocumentCompleted;
                clicker.Navigate(urlAuth);
                Application.Run();
                //while (clicker.ReadyState != WebBrowserReadyState.Complete)
                //{
                //    Application.DoEvents();
                //}
            });
            th.SetApartmentState(ApartmentState.STA);
            th.Start();
            int nrSecondsToWait = 5 * 60;
            while (!authenticated && nrSecondsToWait > 0)
            {
                Thread.Sleep(5000);
                nrSecondsToWait -= 5;
                Console.WriteLine("waiting" + nrSecondsToWait);

            }
            if (!authenticated)
            {
                throw new ArgumentException("not connected to foursquare");
            }

Practically is the same process as the previous, just the browser is within the application, not outside:

Next time I will make a short review of what I have done.
Source code at https://github.com/ignatandrei/4SqDayHistory

Foursquare–part 2 of 4

After successfully connecting to Foursquare with an Web application, the problem is what to don with a Desktop application. The most simple desktop application is a Console . The first idea that I have had is to start a browser, let the user pass his credentials and retrieve data from browser.

Something like this

string urlAuth = this.sharpSquare.GetAuthenticateUrl(urlSOA + &quot;home/redirect4sq/&quot; + this.thisRequest);
Process p = new Process();
p.StartInfo.FileName = getDefaultBrowser();
p.StartInfo.Arguments = urlAuth;
p.Start();


However, I have been not capable of taking data from the WebBrowser process. So the solution is this one:

The Console program open a web browser to the Foursquare web site with a redirect to http://fsq.apphb.com/ and GUID. The user enters his credentials to FourSquare site. Then Foursquare is redirecting to the http://fsq.apphb.com/  -and the http://fsq.apphb.com/ memorizes a GUID and his token. Then the Console reads from http://fsq.apphb.com/ the token ( has the GUID) and it is set.

 

 

The code looks like this:

public void AuthenticateNewWebBrowser()
        {
            string urlAuth = this.sharpSquare.GetAuthenticateUrl(urlSOA + "home/redirect4sq/" + this.thisRequest);
            Process p = new Process();
            p.StartInfo.FileName = getDefaultBrowser();
            p.StartInfo.Arguments = urlAuth;
            p.Start();
            Thread.Sleep(5000);
            AuthenticateToken();
        }
//retrieving from website
        public void AuthenticateToken()
        {
            var newUrl = urlSOA + "api/Values/ClientToken/" + this.thisRequest;
            var code = new WebClient().DownloadString(newUrl).Replace("\"", "");
            SetAccessCode(code);

        }

Next time I will do code for solving same problem – but from inside the program( without needing to launch a new Process)

Solving “No parameterless constructor defined for type of ‘System.String’.”

TL;DR

If you have the problem

No parameterless constructor defined for type of ‘System.String’!

just add the DeserializeObject and InterceptError functions , add this 2 lines:

var obj = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json); //json parameter is the string content of the api
<your object> = DeserializeObject(obj, typeof(<your object type>)) as <your object type>; 

watch the debug window and you will see the problems marked with !!!
( See also video at http://youtu.be/7oZ37pnSNtM)

Long story:

I wanted to do a tutorial for obtaining data from foursquare from Web , Desktop and mobile applications. I have made some research first and I have discovered the API and a library for obtaining data.

Unfortunately, foursquare changed his API and , when deserializing, the application was giving  

“No parameterless constructor defined for type of ‘System.String’.

 

at this line

fourSquareResponse = new JavaScriptSerializer().Deserialize<FourSquareSingleResponse<T>>(json);

I have made a custom code to see where the errors are. 

I do not want to explain the code, just look at it and then I will show how to use:

/// <summary>
        /// TODO: make this add to global errors to return to the caller
/// </summary>
        /// <param name="message"></param>
        private void InterceptError(string message)
        {
            var cc = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(message);
            Console.ForegroundColor = cc;
            Debug.WriteLine(message);
        }
        private object DeserializeObject(Dictionary<string, object> dictItem, Type tip)
        {
            
            var newInstance = Activator.CreateInstance(tip);
            var isDictionary = (tip.GetInterface("IDictionary") != null);
            PropertyInfo p = null;
            foreach (var k in dictItem.Keys)
            {
                var ser=  new JavaScriptSerializer().Serialize(dictItem[k]);

                
                Type tipP = null;
                if (isDictionary)
                {
                    tipP = tip.GetGenericArguments()[1];
                }
                else
                {
                    
                    try
                    {
                        p = tip.GetProperty(k);
                        if (p == null)
                            throw new ArgumentNullException(k);
                    }
                    catch (Exception)
                    {
                        //Console.WriteLine(ex.Message);                        
                        InterceptError("missing property:" + k + " to class" + tip.FullName + " value:" + ser);
                        continue;
                    }
                    tipP = p.PropertyType;
                }
                
                
                if (tipP.IsClass && tipP.FullName != typeof(string).FullName)
                {
                    
                    dynamic val = null;
                    try
                    {
                        val = new JavaScriptSerializer().Deserialize(ser, tipP);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("error for class:" + k + ex.Message);
                        IList arr = dictItem[k] as object[];
                        if (arr == null)
                        {
                            arr = dictItem[k] as ArrayList;
                        }
                        if (arr == null)
                        {                            
                            var t1 = dictItem[k] as Dictionary<string, object>;
                            if (t1 == null)
                            {
                                InterceptError("Not a dictionary, not an array - please contact ignatandrei@yahoo.com for " + k);
                            }

                            val = DeserializeObject(dictItem[k] as Dictionary<string, object>, tipP);
                            
                        }
                        else
                        {
                            val = Activator.CreateInstance(tipP);
                            var tipGen=tipP.GetGenericArguments()[0];
                            foreach (var obj in arr)
                            {
                                val.Add((dynamic)DeserializeObject(obj as Dictionary<string, object>, tipGen));
                            }
                        }
                        

                    }

                    if (isDictionary)
                    {
                        ((IDictionary)newInstance).Add(k, Convert.ChangeType(val, tipP));
                    }
                    else
                    {
                        p.SetValue(newInstance, Convert.ChangeType(val, tipP), null);

                    }
                }
                else//simple int , string, 
                {
                    try
                    {
                        if (isDictionary)
                        {
                            ((IDictionary)newInstance).Add(k, Convert.ChangeType(dictItem[k], tipP));
                        }
                        else
                        {
                            p.SetValue(newInstance, Convert.ChangeType(dictItem[k], tipP), null);
                        }
                    }
                    catch (Exception ex)
                    {
                        InterceptError("!!!not a simple property " + k + " from " + tip + " value:" + ser);
                    }
                }
            }
            return newInstance;
        }

Now the original code that throws the error looks this way:

try
            {
                fourSquareResponse = new JavaScriptSerializer().Deserialize<FourSquareSingleResponse<T>>(json);  //json parameter is the string content of the api
            }
            catch (Exception ex)
            {
                var obj = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);  //json parameter is the string content of the api
                fourSquareResponse = DeserializeObject(obj, typeof(FourSquareSingleResponse<T>)) as FourSquareSingleResponse<T>; 
            }

When looking to the debug window, you will see:

!!!not a simple property icon from FourSquare.SharpSquare.Entities.Category value:{"prefix":"https://ss1.4sqi.net/img/categories_v2/building/home_","suffix":".png"}

 

If looking to the source code, you will see

public string icon
        {
            get;
            set;
        }

And in the foursquare api you will see

 

    • icon: {
      • prefix: "https://ss1.4sqi.net/img/categories_v2/travel/trainstation_"
      • suffix: ".png"

      }

  •  

    Now it is clear : icon is not a string, is a class!
    So just add a class with required properties , modify the property from string to this class and it is all solved!
    ( for me, the class is:

       public class Icon
       {
           public string prefix { get; set; }
           public string suffix { get; set; }
    
        }
    

    and the definition will be:

    public Icon icon
            {
                get;
                set;
            }
    

    Moral of the story:

    When obtaining data via HTTP API, just be sure that you can handle modifications!

    Foursquare–part 1 of 4

    I have decided to log my daily routine from FourSquare .  I decided to make a Web application and a desktop application ( console, to be easiear) .

    As a good application, 4Sq have an API at https://developer.foursquare.com/ . I suggest you to start with conecting to 4Sq, https://developer.foursquare.com/overview/auth .

    For WebServers , you will redirect the user to  an URL from 4Sq.

    https://foursquare.com/oauth2/authenticate
        ?client_id=YOUR_CLIENT_ID
        &response_type=code
        &redirect_uri=YOUR_REGISTERED_REDIRECT_URI

    The user gives his accept to use your application and 4Sq redirects the browser back to YOUR_REGISTERED_REDIRECT_URI

    Then , with the code, you can ask for an access token and then you can search 4Sq API.

    I have searched also a 4Sq application in .NET   – and I found this one : https://github.com/ignatandrei/SharpSquare

    Ok, so for implementing with ASP.NET MVC it is relatively simple – I must make an action that can answer to YOUR_REGISTERED_REDIRECT_URI and then ask for user checklist for the previous day – rather simple:

     public ActionResult oldWay(string id)
            {
                var c = new conect4Sq();
                c.SetAccessCode(id);
                var data = c.CheckinsYesterday();
                return View("~/Views/Home/ShowCheckins.cshtml",data);//TODO: use T4MVC
    
            }
    

    You can see the application in work at http://fsq.apphb.com/  – just press the first “learn more” from the left.

    You will find  the source code at https://github.com/ignatandrei/4SqDayHistory

    Problem solved !

    Now the real problem is to do the same thing from a DESKTOP application ( or Console, to be easier) – but this will be in the second part next week!