Marcos Vainer Loeff

Developing the Wikipedia Data Reference - Part 1

Blog Post created by Marcos Vainer Loeff Employee on Aug 10, 2015

Introduction

 

Wikipedia as you know is a free online encyclopedia. Depending on which type of application you are developing, integrating with it may have a lot of benefits. Last year, we have developed a web site that shows the weather information from different cities of the world. The time-series data was stored of course on the PI System. A cool feature we have added was to show the city description from wikipedia. I am sure you can find out other use cases. The question I will try to answer is how to integrate it with the PI System.

 

On this first part I will show you how to develop a .NET library to get data from Wikipedia. On the second part, we are going to develop an AF custom data reference for this use case.

 

The source code package of the first part can be download here.

 

MediaWiki Web API

 

As Wikipedia is actually a Media Wiki installation, it provides direct, high-level access to the data contained in its databases. Clients can log into a wiki, get data, post changes automatically by making HTTP requests.

 

The API provides a large number of options. This is what we want to do:

 

  1. Get the title of the wiki page found.
  2. Get the first paragraph of the wiki page.
  3. Get the thumbnail of the wiki page.
  4. Get the coordinates of the wiki page. This will only work if the wiki page is about something physical which has a fixed position like a tourist attraction.

 

Here in Sao Paulo (Brazil), there is a famous park called Ibirapuera Park. Let's use it on the following examples:

 

You can get the title using the URL: https://en.wikipedia.org/w/api.php?action=query&format=json&titles=Ibirapuera%20Park. This will return a JSON response:

 

 {"batchcomplete":"","query":{"pages":{"3368541":{"pageid":3368541,"ns":0,"title":"Ibirapuera Park"}}}}

 

This means that it has found a wiki page whose pageid is 3368 and whose title is "Ibirapuera Park". Getting the title is the first step to get the other information: first paragraph, thumbnail and coordinates.

 

Using the title, we have the URL address of the wiki page: http://en.wikipedia.org/wiki/ + "encoded title". This results on "http://en.wikipedia.org/wiki/Ibirapuera%20Park".Using HtmlAgilityPack you can extract the first paragraph. I will show you how to do that on the next section.

 

In order to get the thumbnail of the wiki page, you should access the following URL: https://en.wikipedia.org/w/api.php?action=query&titles=Ibirapuera%20Park&prop=pageimages&format=json&pithumbsize=100. You can specify the format of the response and the size of the thumbnail by setting up the proper query strings. The JSON response would be:

 

{"batchcomplete":"","query":{"pages":{"3368541":{"pageid":3368541,"ns":0,"title":"Ibirapuera Park","thumbnail":
{"source":"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Oscar_Niemeyer%2COca%2C_Bienal%2C_Audit%C3%B3rio.jpg/100px-Oscar_Niemeyer%2COca%2C_Bienal%2C_Audit%C3%B3rio.jpg","width":100,"height":50},"pageimage":"Oscar_Niemeyer,Oca,_Bienal,_Audit\u00f3rio.jpg"}}}}

 

If you access the source attribute, it will show the image below:

 

100px-Oscar_Niemeyer%2COca%2C_Bienal%2C_Audit%C3%B3rio.jpg

 

 

Finally, in order to have access to the geolocation of the Ibirapuera Park, you can access the following URL:  http://en.wikipedia.org/w/api.php?action=query&prop=coordinates&format=json&colimit=10&exportnowrap=&iwurl=&titles=Ibirapuera%20Park.

 

This will result on the following JSON response:

 

{"batchcomplete":"","query":{"pages":{"3368541":{"pageid":3368541,"ns":0,"title":"Ibirapuera Park","coordinates":[{"lat":-23.5883,"lon":-46.6589,"primary":"","globe":"earth"}]}}}}

 

We can conclude that according to Wikipedia, the Ibirapuera Park in São Paulo is located with a latitude = -23.5883 and longitude = -46.6589.

Developing our library

 

Now that you have an idea about how to query against Wikipedia API, let's create a new project which is Class Library on Visual Studio. Rename the default .cs file to WikipediaWebService.cs.

 

Add two Nuget packages using the following commands:

 

  • Install-package HtmlAgilityPack
  • Install-package Newtonsoft.Json

 

We have some methods that query wikipedia. Most of them gets the title first and then makes another request to get the desired information. Two methods are available for getting data of the web: GetStringResponse() and GetJsonResponse(). Both have the url as an input. GetJsonResponse() calls GetStringResponse() and then converts the result to a dynamic object through some methods provided by the Newtonsoft.Json library.

 

Concerning getting the description of the first paragraph, I am taking a different approach than accessing the API. We transfer the HTML string using the GetStringResponse() and then loads it on the Html Agility Pack library. This library, which enables you to find selected nodes, was used on one of my blog posts Creating an AF Elements Tree using web page content. In our case, the first paragraph is a div nose whose class name is "mw-content-ltr".I have taken the StripHTML() static method from the web in order to convert the HTML node to a text. And this is how we get the description of the wiki page. You can find similar results by accessing the API directly but I wanted to show you a different approach.

 

As this is just an example, I am not handling the exceptions properly. If I receive an exception it will return a null or an empty object depending on each situation.

 

Two additional class for coordinates and image properties shall be created:

 

    public class Coordinate
    {
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }

    public class Image
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string ThumbnailUrl { get; set; }
        public string ImageUrl{ get; set; }
        public string UserName { get; set; }
    }

 

The code snippet forthe WikipediaWebService class is below.

 

 

using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;




namespace WikipediaDR
{
    public class WikipediaWebService
    {


        private static string GetWikipediaTitle(string query, out string wikiPageId)
        {
            wikiPageId = string.Empty;
            string title = string.Empty;
            try
            {
                string url = "http://en.wikipedia.org/w/api.php?action=query&format=json&titles=" + HttpUtility.UrlPathEncode(query.Trim());
                dynamic webResponse = GetJsonResponse(url);
                JObject wikiPage = webResponse["query"]["pages"];
                wikiPageId = wikiPage.Properties().First().Name;
                title = webResponse["query"]["pages"][wikiPageId]["title"];
            }
            catch (Exception)
            {
                return string.Empty;
            }
            return title;
        }


        public static string GetWikipediaDescription(string query, int numberOfParagraphs = 1)
        {
            string wikipediaDescription = string.Empty;
            string wikiPageId = string.Empty;
            try
            {
                string title = GetWikipediaTitle(query, out wikiPageId);
                string url = "http://en.wikipedia.org/wiki/" + HttpUtility.UrlPathEncode(title);
                string webStringResponse = GetStringResponse(url);
                var doc = new HtmlAgilityPack.HtmlDocument();
                doc.LoadHtml(webStringResponse);


                HtmlNode root = doc.DocumentNode;
                HtmlNodeCollection TableNodeCollection = root.SelectNodes("//div[contains(@class,'mw-content-ltr')]").First().SelectNodes("p"); ;


                wikipediaDescription = StripHTML(TableNodeCollection[0].OuterHtml.ToString());
                for (int i = 1; i < numberOfParagraphs - 1; i++)
                {
                    wikipediaDescription += "\n" + StripHTML(TableNodeCollection[i].OuterHtml.ToString());
                }
            }
            catch (Exception)
            {
                return string.Empty;
            }
            return wikipediaDescription;
        }






        public static string GetWikipageThumbnail(string query, int widthPixel)
        {
            string wikiPageId = string.Empty;
            string wikipageThumbnailUrl = string.Empty;
            try
            {
                string title = GetWikipediaTitle(query, out wikiPageId);
                string url = "http://en.wikipedia.org/w/api.php?action=query&titles=" + HttpUtility.UrlPathEncode(title) + "&prop=pageimages&format=json&pithumbsize=" + widthPixel.ToString();
                dynamic webResponse = GetJsonResponse(url);
                wikipageThumbnailUrl = webResponse["query"]["pages"][wikiPageId]["thumbnail"]["source"].ToString();
            }
            catch (Exception)
            {
                return string.Empty;
            }
            return wikipageThumbnailUrl;
        }




        public static Image GetWikipediaImageProperties(string imageTitle)
        {
            Image image = new Image();
            image.Title = imageTitle;
            string wikiPageId = string.Empty;
            string wikipageThumbnailUrl = string.Empty;
            try
            {
                string url = "http://en.wikipedia.org//w/api.php?action=query&prop=imageinfo&format=json&iiprop=user%7Curl&iilimit=10&iiurlwidth=100&iiurlheight=100&exportnowrap=&iwurl=&titles=" + HttpUtility.UrlPathEncode(imageTitle);
                dynamic webResponse = GetJsonResponse(url);
                image.UserName = webResponse["query"]["pages"]["-1"]["imageinfo"][0]["user"].ToString().ToLower();
                image.ImageUrl = webResponse["query"]["pages"]["-1"]["imageinfo"][0]["url"].ToString().ToLower();
                image.Description = webResponse["query"]["pages"]["-1"]["imageinfo"][0]["descriptionurl"].ToString().ToLower();
                image.ThumbnailUrl = webResponse["query"]["pages"]["-1"]["imageinfo"][0]["thumburl"].ToString().ToLower();
            }
            catch (Exception)
            {
                return null;
            }


            return image;
        }




        public static Coordinate GetWikipediaCoordinates(string query)
        {
            Coordinate coordinate = new Coordinate();
            string wikiPageId = string.Empty;
            string wikipageThumbnailUrl = string.Empty;


            try
            {
                string title = GetWikipediaTitle(query, out wikiPageId);
                string url = "http://en.wikipedia.org/w/api.php?action=query&prop=coordinates&format=json&colimit=10&exportnowrap=&iwurl=&titles=" + HttpUtility.UrlPathEncode(title);
                dynamic webResponse = GetJsonResponse(url);
                string lat = webResponse["query"]["pages"][wikiPageId]["coordinates"][0]["lat"].ToString();
                string lon = webResponse["query"]["pages"][wikiPageId]["coordinates"][0]["lon"].ToString();


                coordinate.Latitude = Convert.ToDouble(lat);
                coordinate.Longitude = Convert.ToDouble(lon);


            }
            catch (Exception)
            {
                return null;
            }
            return coordinate;
        }




        public static List<string> GetWikipageImagesTitles(string query)
        {
            List<string> imagesTitles = new List<string>();
            string wikiPageId = string.Empty;
            string wikipageThumbnailUrl = string.Empty;
            try
            {
                string title = GetWikipediaTitle(query, out wikiPageId);
                string url = "http://en.wikipedia.org/w/api.php?action=query&titles=" + HttpUtility.UrlPathEncode(title) + "&prop=images&imlimit=20&format=json";
                dynamic webResponse = GetJsonResponse(url);
                foreach (var image in webResponse["query"]["pages"][wikiPageId]["images"])
                {
                    imagesTitles.Add(image.title.Value.ToString());
                }
            }
            catch (Exception)
            {
   
            }
            return imagesTitles;
        }








        private static dynamic GetJsonResponse(string url)
        {
            return JObject.Parse(GetStringResponse(url));
        }


        private static string GetStringResponse(string url)
        {
            WebRequest request = WebRequest.Create(url);
            WebResponse response = request.GetResponse();
            StreamReader sw = new StreamReader(response.GetResponseStream());
            return sw.ReadToEnd().Replace("/**/", string.Empty);
        }




        private static string StripHTML(string source)
        {
            try
            {
                string result;


                // Remove HTML Development formatting
                // Replace line breaks with space
                // because browsers inserts space
                result = source.Replace("\r", " ");
                // Replace line breaks with space
                // because browsers inserts space
                result = result.Replace("\n", " ");
                // Remove step-formatting
                result = result.Replace("\t", string.Empty);
                // Remove repeating spaces because browsers ignore them
                result = System.Text.RegularExpressions.Regex.Replace(result,
                                                                      @"( )+", " ");


                // Remove the header (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*head([^>])*>", "<head>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*head( )*>)", "</head>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(<head>).*(</head>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // remove all scripts (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*script([^>])*>", "<script>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*script( )*>)", "</script>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                //result = System.Text.RegularExpressions.Regex.Replace(result,
                //         @"(<script>)([^(<script>\.</script>)])*(</script>)",
                //         string.Empty,
                //         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<script>).*(</script>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // remove all styles (prepare first by clearing attributes)
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*style([^>])*>", "<style>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"(<( )*(/)( )*style( )*>)", "</style>",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(<style>).*(</style>)", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // insert tabs in spaces of <td> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*td([^>])*>", "\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // insert line breaks in places of <BR> and <LI> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*br( )*>", "\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*li( )*>", "\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // insert line paragraphs (double line breaks) in place
                // if <P>, <DIV> and <TR> tags
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*div([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*tr([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<( )*p([^>])*>", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // Remove remaining tags like <a>, links, images,
                // comments etc - anything that's enclosed inside < >
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"<[^>]*>", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // replace special characters:
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @" ", " ",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&bull;", " * ",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&lsaquo;", "<",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&rsaquo;", ">",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&trade;", "(tm)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&frasl;", "/",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&lt;", "<",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&gt;", ">",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&copy;", "(c)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&reg;", "(r)",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove all others. More can be added, see
                // http://hotwired.lycos.com/webmonkey/reference/special_characters/
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         @"&(.{2,6});", string.Empty,
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // for testing
                //System.Text.RegularExpressions.Regex.Replace(result,
                //       this.txtRegex.Text,string.Empty,
                //       System.Text.RegularExpressions.RegexOptions.IgnoreCase);


                // make line breaking consistent
                result = result.Replace("\n", "\r");


                // Remove extra line breaks and tabs:
                // replace over 2 breaks with 2 and over 4 tabs with 4.
                // Prepare first to remove any whitespaces in between
                // the escaped characters and remove redundant tabs in between line breaks
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)( )+(\r)", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\t)( )+(\t)", "\t\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\t)( )+(\r)", "\t\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)( )+(\t)", "\r\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove redundant tabs
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)(\t)+(\r)", "\r\r",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Remove multiple tabs following a line break with just one tab
                result = System.Text.RegularExpressions.Regex.Replace(result,
                         "(\r)(\t)+", "\r\t",
                         System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                // Initial replacement target string for line breaks
                string breaks = "\r\r\r";
                // Initial replacement target string for tabs
                string tabs = "\t\t\t\t\t";
                for (int index = 0; index < result.Length; index++)
                {
                    result = result.Replace(breaks, "\r\r");
                    result = result.Replace(tabs, "\t\t\t\t");
                    breaks = breaks + "\r";
                    tabs = tabs + "\t";
                }


                // That's it.
                return result;
            }
            catch
            {
                return string.Empty;
            }
        }
    }
}

Unit Testing

 

We could test the WikipediaWebService class by create a new Console Application project. I will take a different approach and use Unit Testing instead whose libraries Visual Studio already provides.

 

According to Microsoft, the primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as you expect by defining TestMethods.

 

In order to create a new unit test project, right click on the solution --> Add --> Project.. You will find the "Unit Test Project" under the Test section within Visual C#.  Once the project whose name is WikipediaDR.Tests is created, you can define its method on the WikipediaWebServiceUnitTest.

 

 

 

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Threading.Tasks;


namespace WikipediaDR.Tests
{
    [TestClass]
    public class WikipediaWebServiceUnitTest
    {
        private string GetTestQuery()
        {
            return "Ibirapuera Park";
        }


        [TestMethod]
        public void WikipediaDescriptionTest()
        {
            string query = GetTestQuery();
            string wikipediaDescription = WikipediaWebService.GetWikipediaDescription(query, 1);
            Assert.IsTrue(wikipediaDescription.Length > 100);
        }


        [TestMethod]
        public void WikipediaThumbnailTest()
        {
            string query = GetTestQuery();
            string wikipageThumbnail = WikipediaWebService.GetWikipageThumbnail(query, 100);
            Assert.IsTrue(wikipageThumbnail.Contains(".org"));
            Assert.IsTrue(wikipageThumbnail.Contains("http"));
            Assert.IsTrue(wikipageThumbnail.Contains(".png") || wikipageThumbnail.Contains(".jpg"));
        }


        [TestMethod]
        public void WikipediaImagesTest()
        {
            string query = GetTestQuery();
            List<string> imagesTitleList = WikipediaWebService.GetWikipageImagesTitles(query);
            foreach (string image in imagesTitleList)
            {
                Assert.IsTrue(image.Contains("File"));
                Assert.IsTrue(image.ToLower().Contains(".png") || image.ToLower().Contains(".svg") || image.ToLower().Contains(".jpg"));
            }
        }
        [TestMethod]
        public void WikipediaCoordinateTest()
        {
            string query = GetTestQuery();
            Coordinate coordinate = WikipediaWebService.GetWikipediaCoordinates(query);
            Assert.IsNotNull(coordinate);
            Assert.IsNotNull(coordinate.Latitude);
            Assert.IsNotNull(coordinate.Longitude);
            Assert.IsTrue(coordinate.Latitude <= 90);
            Assert.IsTrue(coordinate.Latitude >= -90);
            Assert.IsTrue(coordinate.Longitude <= 180);
            Assert.IsTrue(coordinate.Longitude >= -180);


        }
        [TestMethod]
        public void WikipediaImagesPropertiesTest()
        {
            string query = GetTestQuery();
            List<string> imagesTitleList = WikipediaWebService.GetWikipageImagesTitles(query);
            Parallel.ForEach(imagesTitleList, imageTitle =>
            {
                Image image = WikipediaWebService.GetWikipediaImageProperties(imageTitle);
                if (image != null)
                {
                    Assert.IsNotNull(image);
                    Assert.IsTrue(image.Title == imageTitle);
                    Assert.IsTrue(image.Description.Length > 0);
                    Assert.IsTrue(image.UserName.Length > 0);


                    Assert.IsTrue(image.ImageUrl.Contains(".org"));
                    Assert.IsTrue(image.ImageUrl.Contains("http"));
                    Assert.IsTrue(image.ImageUrl.Contains(".png") || image.ImageUrl.Contains(".jpg") || image.ImageUrl.Contains(".svg"));


                    Assert.IsTrue(image.ThumbnailUrl.Contains(".org"));
                    Assert.IsTrue(image.ThumbnailUrl.Contains("http"));
                    Assert.IsTrue(image.ThumbnailUrl.Contains(".png") || image.ThumbnailUrl.Contains(".jpg") || image.ThumbnailUrl.Contains(".svg"));
                }
            });


        }
    }
}

 

Five unit tests were defined. When you run them, they will pass only if they don't throw any exception. Otherwise, they will fail. The Assert class provides many static methods to check if the objects behave the way you expect. Let's take an example:

 

Assert.IsTrue(image.ImageUrl.Contains(".org"));

 

if the image.ImageUrl property which is a string does not contain ".org" within, then it will throw an exception and the method will fail.

 

Clicking on "Run All" button on Test Explorer pane will run all the tests with a single click. Make sure they all pass their tests!

 

image.jpg

 

Conclusions

 

Our WikipediaWebService class is ready. If you have an input string, it will search for a wikipage and returns its title in case a wikipage is found. Other methods were added in order to get the thumbnail and image properties from the wikipage found. Finally, we have created a unit test class for testing our library.

 

The next part we will develop a custom AF data reference which will call the WikipediaWebService class in order to obtain the Wikipedia description. Stay tuned!

Attachments

Outcomes