User:Martyav/Apps/Tutorial

This tutorial will teach you how to use the Mediawiki Action API to build a web app in Flask, a Python framework. Specifically, the app will display the current Wikipedia Picture of the day.

A complete version of the app is available online: Download the code from Github

Although this tutorial provides examples walking you through most of the code, it is good to have knowledge of the following items before you begin:


 * Python 3
 * Flask
 * Jinja
 * HTML
 * CSS
 * JSON

Setting up Python
This tutorial uses Python 3. You can download the latest Python version from here:


 * Python for Windows 7, 8, and 10
 * Python for Mac OS X

If your operating system is Windows XP, a Linux distro, or something else, see the Python beginner's guide for further instructions on installation.

Setting up Flask
Pip is a package manager that should have come with your Python installation. If you don't have it already, install it from the official Pip website. Once you've got it, open your command line interface of choice and run

Hello world in Flask
If you have everything successfully installed, the following script should display "Hello world" inside your web browser, at http://localhost:5000/:

hello.py

Picture of the day viewer
Now that we have everything set up and know Flask is working, we can start writing our code.

Picture of the day, or POTD, is a featured image displayed on the home page of Wikipedia. We'll be hitting an endpoint containing a wiki template that changes every day, and using the data we find there to get at the image. Then, we'll use this data to render a web page.

Getting today's date
The first order of business is simply knowing what day it is. Because POTD updates daily, we need today's date to access the archives and get at a stable version of the correct picture. To do this, we're going to import the class.

Create a file, and name it app.py. Go into, and add this line near the top of the file:

Underneath, define a function, named index. We'll be using this function to render the web page soon.

Inside, add a variable containing the current date in a formatted string:

The call to  gives us a date string in the format YYYY-mm-dd, which is exactly the format of all dates listed within the Wikipedia POTD archives.

app.py

Adding Flask to app.py
Import the following from the  library: ,  , and, of course,.

Look back at the  function in. It contains some additional boilerplate that we need to get our app working.

allows us to call our app later and run it. tells our app which route to listen to, and what functions are associated with that route. triggers the app to fire up a local server, which we can use to pass data or render a web page.

After you add the code from  to , we'll be ready to make our API calls and do something with them.

app.py

Making the API calls
The Action API works by sending back data in response to a HTTP request. We should import the Python Requests library, as it will make our lives easier.

Add the following line to the top of your file:

Despite the name, this library is distinct from our earlier import,, which is simply a class that allows Flask to communicate back and forth with the web page it is serving.

Wikipedia hosts the current Picture of the Day in several different places. We'll access the protected Picture of the Day page to get at the most stable version of the image in the archives.

Define a new function,. This function will call on API:Images to request the picture embedded within the protected Picture of the Day page, for any given day. Then, it'll use the filename from API:Images, to call a helper function that uses API:Imageinfo to retrieve the image's source address. Define this helper function,.

Finally, alter  to call. Make  return. We'll be discussing the meaning of this call in the next section.

app.py

Displaying the page
The call to  generates markup for a web page that contains the information passed along in. Flask uses a directory named templates to hold files that contain some dynamic elements.

Template directory
Create a new directory, and name it templates. Add a new file to it, and name it index.html.

Flask templates mostly contain HTML markup, but they also use Jinja to render the dynamic elements. Jinja markup looks like this --  -- and is used to inject Python variables or expressions into our page.

Add some basic HTML 5 boilerplate to, and a few elements. Amongst our HTML scaffold is Jinja syntax indicating where the data from app.py will go:

templates/index.html

Static directory
Flask uses another directory, named static, to contain any helper files that stay the same throughout the lifecycle of the app. On the same level as  and , create a new directory, and name it static. Inside this directory, create a new file, and name it style.css.

We'll be using some colors and visual motifs based on the Wikimedia Style Guide.

static/style.css

Making it interactive
works by responding to GET and POST requests along a designated route. If we add a form to our page, we can use the POST requests from it to allow users to browse through the Picture of the Day archives.

Go back to. Inside, add another div, for our new control scheme. This will have the class of, and will contain our form. The form will have submit button inputs, each with a designated value. When either button is selected, the form will submit a POST request, and the selected value will be passed back to.

The archives start on 14 May 2004, and range forward to the current date. Jinja allows us to add conditional formatting to our page, so that if the next date is out of range, the button can be automatically disabled. However, from inside, we don't have full access to the   class. We need a bit of a hack: because we can't create a separate date object to compare against, we must make a comparison against an existing date and its method.

templates/index.html

Since we've added new HTML elements, we need to style them.

Go to your css file, and add the following:

static/style.css

Altering app.py
The first thing we need to do is pull out the date variable from index, so that it is accessible to the rest of the file, and can be updated by other functions. Create a new variable in the modular scope, and put it under our constants:

App.py

We want to go backward or forward in time, so we also need to accurately add or subtract from current_date. Python's  allows us to do just that. Add it to our datetime imports:

Define functions for incrementing and decrementing  with  :

App.py

Now, our  function. If the next date is beyond the valid date range, it should not return anything:

App.py

Routing is generally how Flask knows which methods to call in response to events. Update our "/" route to handle POST requests:

If "/" receives a GET or POST request,  is called. We already have a code path for GET --  renders the page. Let's create another code path for the POST from our form:

App.py

Adding a description
How do we get at the nice descriptive text accompanying the Picture of the Day on the Wikipedia page? This is actually trickier than it seems. The core modules of the Action API don't provide a plaintext version of article or template content. The response always contains markup and needs to be parsed to remove it. If you're not up to parsing text from the latest edit, via API:Revisions, there are two ways of getting around this.

API:Search can be used to grab a brief text snippet off the page. This snippet is always parsed. Unfortunately, when it comes to Picture of the Day, the text in the snippet may look a little strange -- for example, it may begin in the middle of a sentence. However, since snippets are part of the core functionality of the Action API, you don't have to worry about KeyErrors when attempting to access the text.

The code for accessing the snippet would look like this:

App.py

If a wiki has the CirrusSearch extension to API:Search, you can access the text of an article via. A query to do so would look like this:.

The results are of better and more consistent quality than from API:Search snippets, but not all wikis have the CirrusSearch extension installed.