Saturday 21 December 2019

Drinking from the Flask part 2

Drinking from the Flask with .Net

Background

This small project will integrate Python, Flask, JSON and .Net.

Task 1: Set up a JSON end point using Python and Flask
Task 2:  .Net application to read data from the end point
Task 3: Expand end points on the Python server for details
Task 4: Select and search for details via JSON calls.

A first sip from the Flask

This assumes you have a suitable version of Visual Studio installed, and an internet connection (plus you have a working JSON source from the previous post).

Ordering the drink

For simplicity we shall start with a Console application.

Create a new .Net Framework Console application (is it me or does Microsoft continue to add steps that add no value to their dialogues?). I called mine JSONTestProgram.

Leaving the Console program until later, I then created another project (a .Net Framework Class) in the solution called JSONClient. I renamed the class to JSONGetData. I then added an additional class file to the project called JSONSubscribers.cs.

The JSONGetData.cs file contains the following code (remember to use the URL of your Flask project).

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
//using System

namespace JSONClient
{
    /// <summary>
    /// This class is used to get JSON data from an endpoint
    /// </summary>
    public class JSONGetData
    {
        /// <summary>
        /// URL for getting the sunscribers information from the JSON source
        /// </summary>
        public static readonly string URLGetSubscribers = "https:// <yourusername>.pythonanywhere.com/subscribers";
        
        static HttpClient client = new HttpClient();
        /// <summary>
        /// Asynchronous function that obtains data from the URLSubscribers endpoint 
        /// and returns a dictionary of email addresses and subscription ids
        /// </summary>
        /// <returns>A dictionary with key and value of type string</returns>
        public static async Task<Dictionary<string,string>> GetSubscribers()
        {
            string content;
            Dictionary<string, string> dictSubscribers=null;
            using (HttpResponseMessage response = await client.GetAsync(URLGetSubscribers, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
            {
                if (response.IsSuccessStatusCode)
                {
                    dictSubscribers = new Dictionary<string, string>();
                    content = await response.Content.ReadAsStringAsync();
                    Rootobject x = Newtonsoft.Json.JsonConvert.DeserializeObject<Rootobject>(content);
                    foreach (JSONClient.Subscriber subscriber in x.Subscribers)
                    {
                        dictSubscribers.Add(subscriber.subscriber.emailaddress, subscriber.subscriber.subscriptionid.ToString());
                    }
                }
            }  
            return dictSubscribers;
        }
    }
}
You need to add a reference to the Newtonsoft.Json package via Edit/Manage NuGet Packages.

A static instance of the HTTPClient is used in this case for simplicity (only one URL is in use).

The asynchronous task GetSubscribers will return a Dictionary of email and subscriber ids.
The GetSubscribers code first creates the dictionary to be returned.
An HTTPResponseMessage is created and initialised by an asynchronous call to the URLGetSubscribers URL.
If the response contains a status success code, the dictionary to be returned is initialised.
The content of the response is read into a string and then deserialized using the NewtonSoft component and a target class (more of which anon).
For each subscriber in the object, the email address and subscription ID is extracted and added to the dictionary.
If the response does not contain a status success code, the dictionary is returned uninitialized.

JSON data cannot be read in the same way as XML data (there is no Document Object Model equivalent), so you generally need to know the format or schema of the JSON.

This is where the JSONSubscribers.cs file comes in.
Visual Studio has added a feature which allows you to use an Edit/Paste Special command to paste text as either XML or JSON, converting the text being pasted into the required classes. When you do this, it is generally a good idea to paste it into a dedicated file - then when (if) you change the JSON file, you can just scrub and re-paste it.
This is the result in the current JSONSubscribers.cs file.

namespace JSONClient
{

    public class Rootobject
    {
        public Subscriber[] Subscribers { get; set; }
    }

    public class Subscriber
    {
        public Subscriber1 subscriber { get; set; }
    }

    public class Subscriber1
    {
        public string emailaddress { get; set; }
        public int subscriptionid { get; set; }
    }

}

The root object is used as the overall class for the conversion. The element names within the classes may not match up with the recommended naming scheme in C#, it is possible to tell it to use one name in the source and generate another in the object but that is beyond this simple example.
So, how do we call this code?

The Console program

The most complicated part of the Main method is the RunAsync().GetAwaiter().GetResult() call.
It is a lazy (and liable to deadlocks) way of calling an asynchronous call synchronously. Not really for production code.
You will need to add references to the Newtonsoft.Json package via Manage NuGet packages menu item. You also need to reference the JSONClient project.

class Program
    {
        static void Main(string[] args)
        {
            RunAsync().GetAwaiter().GetResult();
            Console.WriteLine("Press any key");
            Console.ReadKey();
        }
        static async Task RunAsync()
        {
            Dictionary<string, string> subscribers = new Dictionary<string, string>();
            subscribers = await JSONClient.JSONGetData.GetSubscribers();
            foreach (string key in subscribers.Keys)
            {
                Console.WriteLine($"{key}: {subscribers[key]}");
            }

        }
    }

The RunAysnc task calls the GetSubscribers code to obtain the Dictionary and then writes the email address and subscriber id (key - value) to the Console.
When the Console program runs, the result is as follows:
email_0@tec.com: 0
email_1@tec.com: 1
email_2@tec.com: 2
email_3@tec.com: 3
email_4@tec.com: 4
Press any key

Building a Windows Form program

This is slightly more complicated, as there are a lot of threads that can get tangled (for more details see the references).
Add a new Windows Forms project to your solution - in my case I called it JSONTestProgram.

Construct your form similar to this one (it has a label, a text box and a second button used ina later example).


The form comprises a status bar at the bottom and a tool bar at the top. There is a Docked panel in the middle with a Docked SplitContainer inside.
One of the split panels has a ListBox called lbSubscribers (which again is Docked).
The Toolbar has a number of buttons, but only the one marked Load is currently active.
The project needs to have references to the project  JSONClient and a NuGet reference to NewtonSoft.JSON.

Code wise, click on the Load Subscribers button and note the control's name.
Add:

static Dictionary<string, string> subscribers =new Dictionary<string, string>();
inside the Program class (we will be using it later).
You also need to replace the existing method generated when you clicked on the button with:

        private async void toolStripButton2_Click(object sender, EventArgs e)
        {
            subscribers = await JSONClient.JSONGetData.GetSubscribers();
            foreach (string key in subscribers.Keys)
            {
                lbSubscribers.Items.Add(key);
            }
        }

Change the tool strip button name to match the button on your form. Visual Studio will grumble about the method name, which is annoying as it created it.

That is the first part completed. Now change this to be the Start Up project for the solution and run it.
Click on the Load Subscribers button and the list of subscribers generated by your Flask project will be displayed.




References