API:Picture of the day viewer
![]() | This page is part of theMediaWiki Action APIdocumentation. |
Overview
[edit]In this tutorial, you will learn how to build aWikipedia:Picture of the dayviewer using theMediaWiki Action API.
This tutorial will teach you how to do this using:
- Python 3andFlaskframework
- MediaWiki Action API'sImages andImageinfo 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 usesPython 3. You can download the latest Python version from here:
See thePython beginner's guidefor further instructions on installing Python on different operating systems.
Setting up Flask
[edit]Pipis apackage managerthat you can use to install Flask:pip install flask
.
If you don't have Pip already, install it fromthe official Pip website.
Step 2: Create a simple Flask application
[edit]If you have everything successfully installed, place the following script inapp.py
,inside your project folder:$HOME/picture-of-the-day-viewer/app.py
.
When you run it (usingflask run
orpython app.py
), it should display "Hello world" athttp://localhost:5000/:
#!/usr/bin/python3
fromflaskimportFlask
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 afeatured imagedisplayed 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'sdate
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 pagefor more information on theindex.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
fromdatetimeimportdate
fromflaskimportFlask,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 toHTTP requests. To do so, you will importthe Python Requests library.
Next add two new variables:SESSION = requests.Session()
andENDPOINT = "https://en.wikipedia.org/w/api.php"
.
You will use theSESSION
object to make requests to theENDPOINT
URL.
In a new function,fetch_potd()
,callAPI:Images to request the picture embedded within a protected POTD page (example).
From this call, use the picture's filename to callAPI: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 byISO standardformat, 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
}
returnimage_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"]
returnimage_url
Finally, alterindex()
to callfetch_potd()
.
Importrender_template
fromflask
,and makeindex()
returnrender_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 basicHTML 5boilerplate and a few elements toindex.html
.
Make sure to save it to a directory within your app, named/templates
.
<!DOCTYPE html>
<htmllang="en">
<metacharset="utf-8">
<title>Picture of the Day</title>
<linkrel="stylesheet"href="/static/style.css">
<main>
<h1>Picture of the day:</h1>
<divclass="card">
<divclass="potd">
<h2>{{ data.filename }}</h2>
<ahref="{{ data.image_page_url }}"target="blank">
<figure>
<imgsrc="{{ data.image_src }}">
<figcaption>View on Wikipedia</figcaption>
</figure>
</a>
</div>
<divclass="date-container">
<divclass="current-date">{{ data.date.strftime( "%d %B %Y" ) }}</div>
</div>
</div>
</main>
Making it interactive
[edit]Add a<form>
element toindex.html
,and give it the following submit button inputs:BackandNext.When either button is selected, the form will submit a POST request, and the selected value will be passed back toapp.py
.
This will allow users to browse through the Picture of the Day archives.
Next, updateapp.py
with achange_date()
function, for setting the date presented to the user.
Also extend the/
route to handle POST requests from the form.
To allowapp.py
to read the POST request message, import Flask'sRequest
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
fromdatetimeimportdate,timedelta
fromflaskimportFlask,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 '/'
"""
ifrequest.method=="POST":
change_date()
data=fetch_potd(CURRENT_DATE)
returnrender_template("index.html",data=data)
defchange_date():
"""
Changes current date in response to input from the web form
"""
globalCURRENT_DATE
user_input=request.form["change_date"]
new_date=CURRENT_DATE
last_date=date.today()
first_date=date(year=2004,month=5,day=14)
ifuser_input=="← Back":
new_date=new_date-timedelta(days=1)
elifuser_input=="Next →":
new_date=new_date+timedelta(days=1)
ifnew_date>last_dateornew_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
}
returnimage_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"]
returnimage_url
if__name__=="__main__":
APP.run()
|
$HOME/picture-of-the-day-viewer/templates/index.html |
---|
<!DOCTYPE html>
<htmllang="en">
<metacharset="utf-8">
<title>Picture of the Day</title>
<linkrel="stylesheet"href="/static/style.css">
<main>
<h1>Picture of the day:</h1>
<divclass="card">
<divclass="potd">
<h2>{{ data.filename }}</h2>
<ahref="{{ data.image_page_url }}"target="blank">
<figure>
<imgsrc="{{ data.image_src }}">
<figcaption>View on Wikipedia</figcaption>
</figure>
</a>
</div>
<divclass="date-container">
<timeclass="current-date">{{ data.date.strftime( "%d %B %Y" ) }}</time>
<divclass="date-navigator">
<formaction="/"method="POST">
{% if data.date.strftime( "%d %B %Y" ) == "14 May 2004" %}
<inputtype="submit"name="change_date"value="← Back"disabled>
{% else %}
<inputtype="submit"name="change_date"value="← Back">
{% endif %}
{% if data.date == data.date.today() %}
<inputtype="submit"name="change_date"value="Next →"disabled>
{% else %}
<inputtype="submit"name="change_date"value="Next →">
{% endif %}
</form>
</div>
</div>
</div>
</main>
|
Styling your app
[edit]Flask uses a directory, namedstatic,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 theWikimedia 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 thiscode samples repository.
- Learn some ways to add descriptive text from thePicture of the daypage:
- API:Search provides
snippets
which can be used as a link preview action=cirrusdump
can be used to obtain the whole parsed text from articles on Wikis that have theCirrussearch 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