API:Holidays viewer

From MediaWiki.org
Jump to navigation Jump to search

Overview[edit]

This tutorial covers how to create a demo app that fetches holidays and observances for a given date from Wikipedia, with an option to log in to add new holidays.

Download the code from GitHub Browse the app on Toolforge

The tools and technologies used to create the demo app are:

Step 1: Set up Python and Flask development environment[edit]

Python comes pre-installed on most Linux distributions. For other operating systems, see the Python beginner's guide for installation instructions.

Install Flask by running pip install flask. If you don't have pip, get it from the official Pip website.

Step 2: Create a simple Flask application[edit]

In your home directory, create a folder named holidays-viewer which will contain all the app's files. Inside the folder, create a file named app.py and place the following code in it:

#!/usr/bin/python3

from flask import Flask

APP = Flask(__name__)

@APP.route("/")
def list_holidays():
  return "Holidays and observances"

if __name__ == "__main__":
  APP.run()

Run the app using the command python app.py and open http://127.0.0.1:5000/ on your browser. You should see "Holidays and observances" displayed.

Step 3: Create the base layout[edit]

The app will have four pages: the homepage, a search page, a login page and an add page. Each page will have some common elements, so we need to create a base layout file called layout.html to contain these elements.

Note that we are using Bootstrap classes to apply a specific CSS style to an element, Materialize icons for the add, search and arrow-back icons, and Jinja to extend the base layout to other pages and to pass variables from Python to HTML.

Other pages will extend layout.html using the code below:

{% extends "layout.html" %}
{% block content %}
  <!--content for other pages-->
{% endblock %}

Step 4: List holidays[edit]

The root url of the app will trigger the list_holidays(...) function, which lists holidays for a certain date.

In the function and throughout the app, holidays_date refers to the date of the holidays to be listed, header refers to the title of the page, and holidays_html refers to the html which contains the holidays to be listed. We'll also be using the render_template(...) function which renders a specific html file from the templates directory. Other arguments added to the function are variables which are being passed to the html file.

In app.py, update list_holidays() with the code below:

@APP.route('/', methods=['GET', 'POST'])
@APP.route('/<holidays_date>', methods=['GET', 'POST'])
def list_holidays(holidays_date=None):
    holidays_html = ""

    return render_template("index.html", header=holidays_date.replace('_', ' '),
                           holidays_html=holidays_html)

Get today's date[edit]

If no date is specified, we'll list holidays for today's date. To use Python's datetime module to get today's date, import the module with from datetime import datetime then create the following function:

def get_todays_date():
    current_month = datetime.now().strftime('%B')
    current_day = datetime.now().strftime('%d')
    if current_day.startswith('0'):
        current_day = current_day.replace('0', '')

    return current_month + "_" + current_day

Call the function in list_holidays(...):

if holidays_date is None:
        holidays_date = get_todays_date()

Get the holidays to be listed[edit]

Once we have the date, we get the holidays for that date. Wikipedia has a page for each date and the holidays are under a section titled "Holidays and observances". To get the holidays, we need to get it's section number and the content in that section number.

Create a function to get the section number using API:Parse:

def get_holidays_section(url, page, date_to_get):
    params = {
        "format":"json",
        "action":"parse",
        "prop":"sections",
        "page":page
    }

    response = S.get(url=url, params=params)
    data = response.json()
    sections = data['parse']['sections']
    section_number = "0"

    for index, value in enumerate(sections):
        if value['anchor'] == "Holidays_and_observances":
            section_number = index + 1

        if url == TEST_URL:
            if value['anchor'] == date_to_get:
                section_number = index + 1

    return section_number

Create a function called get_holidays(...) to get the holidays in that section using API:Parse as well, then call the functions in list_holidays(...):

section_number = get_holidays_section(URL, holidays_date, None)
holidays = get_holidays(URL, holidays_date, section_number)
holidays_html = holidays

Update holiday links[edit]

The html of the holidays returned contains Help:Links#Internal_links internal links that point to those holidays, e.g "/wiki/New_Years_Day". We need to prepend "//en.wikipedia.org" to these links using jQuery in order to make them external links in our app, and make them open in a new tab. To do that, add the following code to $HOME/holidays-viewer/static/update-links.js:

$( document ).ready( function() {
    $( ".holidays-html a" ).attr( "target", "_blank" );

    $( ".holidays-html a" ).attr( "href", function( i, href ) {
      return "//en.wikipedia.org" + href;
    });
});

Then add jQuery to layout.html using:

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="static/update-links.js"></script>

Step 5: Search for holidays of other dates[edit]

To get holidays of other dates, create a search route to display a form that collects the month and day to search for:

@APP.route("/search")
def search():

    return render_template("search.html", header="Search date")

Once the search form has been submitted, update holidays_date to be the date that has been entered. To do that, add the following code to list_holidays(...):

if request.method == 'POST' and 'search' in request.form:
        search_month = str(request.form.get('monthList'))
        search_day = str(request.form.get('dayList'))
        holidays_date = search_month +"_"+search_day

Step 6: Add a holiday[edit]

The page to which we'll be adding a new holiday is protected from edits by anonymous users, so we need to log in using API:Login#clientlogin first.

To add a holiday, send a request to API:Edit with the date and description of the holiday. The edit adds new holidays to this page on Test Wikipedia: Sandbox/Holidays_and_observances. This is to prevent adding test holidays to English Wikipedia.

After the holiday is added, redirect to the homepage where the holidays added will also be shown, and formatted in bold to differentiate them from the real holidays. To fetch the test holidays alongside the real holidays, update list_holidays(...):

test_section_number = get_holidays_section(TEST_URL, TEST_PAGE, holidays_date)
test_holidays = get_holidays(TEST_URL, TEST_PAGE, test_section_number)

holidays_html = test_holidays + holidays
flash("Holidays added through this app are in bold")

Step 7: Styling the app[edit]

To add more style to our app, create a stylesheet named style.css and link to it from layout.html by adding <link rel="stylesheet" href="static/style.css">.

Application layout[edit]

At this point, the structure of your app should be:

$HOME/holidays-viewer
├── templates/
│   └── add.html
    └── index.html
    └── layout.html
    └── login.html
    └── search.html
├── static/
│   └── style.css
    └── update-links.js
├── app.py

With app.py and layout.html being:

Holidays viewer homepage screenshot

Next steps[edit]

See also[edit]