API:Picture of the day viewer
| MediaWiki Action API |
|---|
| Basics |
| Authentication |
| Accounts and Users |
| Page Operations |
|
| Search |
| Developer Utilities |
| Tutorials |
| v · d · e |
Overview
[edit ]In this tutorial, you will learn how to build a Wikipedia:Picture of the day viewer using the MediaWiki Action API.
This tutorial will teach you how to do this using:
- Python 3 and Flask framework
- MediaWiki Action API's Images and Imageinfo modules
A step-by-step process for building this application
[edit ]Step 1: Set up Python and Flask development environment
[edit ]Setting up Python
[edit ]This tutorial uses Python 3. You can download the latest Python version from here:
See the Python beginner's guide for further instructions on installing Python on different operating systems.
Setting up Flask
[edit ]Pip is a package manager that you can use to install Flask: pip install flask.
If you don't have Pip already, install it from the official Pip website.
Step 2: Create a simple Flask application
[edit ]If you have everything successfully installed, place the following script in app.py, inside your project folder: $HOME/picture-of-the-day-viewer/app.py.
When you run it (using flask run or python app.py), it should display "Hello world" at http://localhost:5000/:
#!/usr/bin/python3 fromflaskimport Flask APP = Flask(__name__) @APP.route("/") defhello(): return "Hello World!" if __name__ == "__main__": APP.run()
Step 3: Picture of the day viewer
[edit ]Now that you have everything set up, you can start writing code for the Picture of the Day viewer. Picture of the day, or POTD, is a featured image displayed daily on the home page of Wikipedia. You'll be getting the picture from a wiki template that changes every day.
Getting today's date
[edit ]Because POTD updates daily, you need today's date to access the archives and get at a stable version of the correct picture.
To do this, import Python's date class.
Next, define a new function, index().
Index() will render the web page and pass along any data associated with our API calls.
See #Displaying the page for more information on the index.html file we'll be using as a template.
For now, index() should contain a variable holding the current date.
We'll be using it soon to compose a query to access the POTD.
#!/usr/bin/python3 fromdatetimeimport date fromflaskimport Flask, render_template APP = Flask(__name__) @APP.route("/") defindex(): todays_date = date.today().isoformat() if __name__ == "__main__": APP.run()
Calling the Action API
[edit ]The Action API works by sending back data in response to HTTP requests. To do so, you will import the Python Requests library.
Next add two new variables: SESSION = requests.Session() and ENDPOINT = "https://en.wikipedia.org/w/api.php".
You will use the SESSION object to make requests to the ENDPOINT URL.
In a new function, fetch_potd(), call API:Images to request the picture embedded within a protected POTD page (example).
From this call, use the picture's filename to call API:Imageinfo , and retrieve the image's source url.
In this example, the second API call is handled in the helper function, fetch_image_src().
Wikipedia's archives lists dates by ISO standard format, for example: 2019年01月31日, for 31 January, 2019.
You can get the correct format by using the date method, isoformat().
deffetch_potd(cur_date): date_iso = cur_date.isoformat() title = "Template:POTD_protected/" + date_iso params = { "action": "query", "format": "json", "formatversion": "2", "prop": "images", "titles": title } response = SESSION.get(url = ENDPOINT, params = params) data = response.json() filename = data["query"]["pages"][0]["images"][0]["title"] image_page_url = "https://en.wikipedia.org/wiki/" + title image_data = { "filename": filename, "image_page_url": image_page_url, "image_src": fetch_image_src(filename), "date": cur_date } return image_data deffetch_image_src(filename): params = { "action": "query", "format": "json", "prop": "imageinfo", "iiprop": "url", "titles": filename } response = SESSION.get(url = ENDPOINT, params = params) data = response.json() page = next(iter(data["query"]["pages"].values())) image_info = page["imageinfo"][0] image_url = image_info["url"] return image_url
Finally, alter index() to call fetch_potd().
Import render_template from flask, and make index() return render_template("index.html", data=data).
Displaying the page
[edit ]Flask templates mostly contain HTML markup, but they also use Jinja to render dynamic content.
Jinja markup looks like this: {{ variable }}, and is used to inject Python variables or expressions into our basic page structure.
Add some basic HTML 5 boilerplate and a few elements to index.html.
Make sure to save it to a directory within your app, named /templates.
<!DOCTYPE html> <html lang="en"> <meta charset="utf-8"> <title>Picture of the Day</title> <link rel="stylesheet" href="/static/style.css"> <main> <h1>Picture of the day:</h1> <div class="card"> <div class="potd"> <h2>{{ data.filename }}</h2> <a href="{{ data.image_page_url }}" target="blank"> <figure> <img src="{{ data.image_src }}"> <figcaption>View on Wikipedia</figcaption> </figure> </a> </div> <div class = "date-container"> <div class = "current-date">{{ data.date.strftime("%d %B %Y") }}</div> </div> </div> </main>
Making it interactive
[edit ]Add a <form> element to index.html, and give it the following submit button inputs: Back and Next. When either button is selected, the form will submit a POST request, and the selected value will be passed back to app.py.
This will allow users to browse through the Picture of the Day archives.
Next, update app.py with a change_date() function, for setting the date presented to the user.
Also extend the / route to handle POST requests from the form.
To allow app.py to read the POST request message, import Flask's Request class.
The complete Python and HTML code:
| $HOME/picture-of-the-day-viewer/app.py |
|---|
""" app.py MediaWiki Action API Code Samples Fetches Wikipedia Picture of the Day (POTD) and displays it on a webpage. Also allows users to go backward or forward a date to view other POTD. MIT License """ #!/usr/bin/python3 fromdatetimeimport date, timedelta fromflaskimport Flask, render_template, request importrequests APP = Flask(__name__) SESSION = requests.Session() ENDPOINT = "https://en.wikipedia.org/w/api.php" CURRENT_DATE = date.today() @APP.route("/", methods=["GET", "POST"]) defindex(): """ Requests data from Action API via 'fetch_potd' function & renders it on the index page accessible at '/' """ if request.method == "POST": change_date() data = fetch_potd(CURRENT_DATE) return render_template("index.html", data=data) defchange_date(): """ Changes current date in response to input from the web form """ global CURRENT_DATE user_input = request.form["change_date"] new_date = CURRENT_DATE last_date = date.today() first_date = date(year=2004, month=5, day=14) if user_input == "← Back": new_date = new_date - timedelta(days=1) elif user_input == "Next →": new_date = new_date + timedelta(days=1) if new_date > last_date or new_date < first_date: return CURRENT_DATE = new_date deffetch_potd(cur_date): """ Returns image data related to the current POTD """ date_iso = cur_date.isoformat() title = "Template:POTD protected/" + date_iso params = { "action": "query", "format": "json", "formatversion": "2", "prop": "images", "titles": title } response = SESSION.get(url=ENDPOINT, params=params) data = response.json() filename = data["query"]["pages"][0]["images"][0]["title"] image_src = fetch_image_src(filename) image_page_url = "https://en.wikipedia.org/wiki/Template:POTD_protected/" + date_iso image_data = { "filename": filename, "image_src": image_src, "image_page_url": image_page_url, "date": cur_date } return image_data deffetch_image_src(filename): """ Returns the POTD's image url """ params = { "action": "query", "format": "json", "prop": "imageinfo", "iiprop": "url", "titles": filename } response = SESSION.get(url=ENDPOINT, params=params) data = response.json() page = next(iter(data["query"]["pages"].values())) image_info = page["imageinfo"][0] image_url = image_info["url"] return image_url if __name__ == "__main__": APP.run() |
| $HOME/picture-of-the-day-viewer/templates/index.html |
|---|
<!DOCTYPE html> <html lang="en"> <meta charset="utf-8"> <title>Picture of the Day</title> <link rel="stylesheet" href="/static/style.css"> <main> <h1>Picture of the day:</h1> <div class="card"> <div class="potd"> <h2>{{ data.filename }}</h2> <a href="{{ data.image_page_url }}" target="blank"> <figure> <img src="{{ data.image_src }}"> <figcaption>View on Wikipedia</figcaption> </figure> </a> </div> <div class="date-container"> <time class="current-date">{{ data.date.strftime("%d %B %Y") }}</time> <div class="date-navigator"> <form action="/" method="POST"> {% if data.date.strftime("%d %B %Y") == "14 May 2004" %} <input type="submit" name="change_date" value="← Back" disabled> {% else %} <input type="submit" name="change_date" value="← Back"> {% endif %} {% if data.date == data.date.today() %} <input type="submit" name="change_date" value="Next →" disabled> {% else %} <input type="submit" name="change_date" value="Next →"> {% endif %} </form> </div> </div> </div> </main> |
Styling your app
[edit ]Flask uses a directory, named static, to contain any helper files that stay the same throughout the lifecycle of the app.
This is a useful place to put any stylesheets or additional scripts.
Our stylesheet will be using some colors and visual motifs based on the Wikimedia Style Guide.
Place a CSS file in $HOME/picture-of-the-day-viewer/static/style.css.
| $HOME/picture-of-the-day-viewer/static/style.css |
|---|
html{ margin:0; padding:0; height:100vh; width:100vw; } body{ margin:0; background:#f8f9fa;/* light grey */ font-family:Arial,Helvetica,sans-serif; font-size:16px; } h1{ margin:0; padding:12px; background:#2a4b8d;/* dark blue */ color:#ffffff; } h2{ margin-top:8px; padding:12px; font-size:1em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; background:#f3f9ff;/* very light blue */ border:1pxsolid#2a4b8d;/* dark blue */ } a{ color:#3366cc;/* blue */ } p{ margin:8px; } .card{ position:relative; margin:auto; min-width:200px; max-width:67vw; height:90vh; background:#ffffff; border-radius:8px; box-shadow:3px6px10pxrgba(0,0,0,0.16); } .potd{ width:inherit; height:60vh; } figure{ width:100%; margin:auto; text-align:center; } figureimg{ display:block; margin:12pxauto; max-width:64vw; max-height:50vh; border:1pxsolid#3366cc;/* blue */ } figurea{ margin:8px; } .date-container{ display:block; position:absolute; bottom:0; left:0; right:0; text-align:center; font-weight:bold; } .current-date{ margin:16pxauto; font-size:2em; background:#ffffff; color:#72777d;/* grey */ } .date-navigator{ margin:16pxauto; font-size:2em; text-transform:uppercase; text-align:center; background:#3366cc;/* blue */ color:#ffffff; } .date-navigatorinput{ margin:8px; min-height:44px; width:45%; font-size:0.67em; font-weight:inherit; text-transform:none; background:#3366cc;/* blue */ color:inherit; border:1pxsolid#ffffff; box-shadow:3px6px10pxrgba(0,0,0,0.16); cursor:pointer; } .date-navigatorinput:hover{ background:#447FF5;/* light blue */ } .date-navigatorinput:active{ background:#2a4b8d;/* dark blue */ border:none; box-shadow:none; } .footer{ text-align:center; } .date-navigatorinput:disabled{ color:#c8cdff;/* grey */ border:1pxsolid#c8cdff;/* grey */ box-shadow:none; cursor:default; } .date-navigatorinput:disabled:hover{ background:#3366cc;/* blue */ } |
Next steps
[edit ]- Contribute a demo app that you have developed using the MediaWiki API to this code samples repository.
- Learn some ways to add descriptive text from the Picture of the day page:
- API:Search provides
snippetswhich can be used as a link preview action=cirrusdumpcan be used to obtain the whole parsed text from articles on Wikis that have the Cirrussearch extension installed (see example).
- API:Search provides
See also
[edit ]- API:Main page : Overview of the Action API
- API:FAQ : FAQ describing the Action API in more depth
- API:Images
- API:Imageinfo
- MediaWiki Action API Code Samples repo: Contains more demos and sample code
- More on Wikipedia and IFTTT