Jump to content

API:Prohlížeč svátků

From mediawiki.org
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
This page is a translated version of the page API:Holidays viewer and the translation is 100% complete.

Přehled

Tento návod popisuje, jak vytvořit ukázkovou aplikaci, která načte svátky a dodržování pro dané datum z Wikipedie, s možností přihlášení a přidání nových svátků.

Nástroje a technologie použité k vytvoření demo aplikace jsou:

Postup pro vytvoření této aplikace krok za krokem

Krok 1: Nastavení vývojového prostředí Python a Flask

Python je předinstalovaný na většině distribucí Linuxu. Pro ostatní operační systémy viz Python příručka pro začátečníky, kde najdete pokyny k instalaci.

Nainstalujte Flask spuštěním pip install flask. Pokud Pip nemáte, získejte ho z oficiálního webu Pip


Krok 2: Vytvořte jednoduchou aplikaci Flask

Ve svém domovském adresáři vytvořte složku s názvem holidays-viewer, která bude obsahovat všechny soubory aplikace. Uvnitř složky vytvořte soubor s názvem app.py a vložte do něj následující kód:

#!/usr/bin/python3

from flask import Flask

APP = Flask(__name__)

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

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

Spusťte aplikaci pomocí příkazu python app.py a otevřete http://127.0.0.1:5000/ ve svém prohlížeči. Měli byste vidět zobrazení "Holidays and observances".

Krok 3: Vytvořte základní návrh

Aplikace bude mít čtyři stránky: domovskou stránku, stránku vyhledávání, přihlašovací stránku a stránku pro přidání. Každá stránka bude mít některé společné prvky, takže musíme vytvořit soubor základního návrhu nazvaný layout.html, který bude tyto prvky obsahovat.

Všimněte si, že používáme třídy Bootstrap k aplikaci konkrétního stylu CSS na prvek, Materialize icons pro ikony přidání, vyhledávání a šipky zpět a Jinja k rozšíření základního rozvržení na další stránky a k předání proměnných z Pythonu do HTML.

$HOME/holidays-viewer/templates/layout.html
<title>Holidays</title>

<link rel="stylesheet" href="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="//tools-static.wmflabs.org/fontcdn/css?family=Material+Icons">

<div class="content bg-secondary rounded m-auto">
  <div class="title-bar bg-primary-dark text-white pl-2">
    <small>Holidays and observances</small>
  </div>
  <div class="header-bar bg-primary text-white shadow p-2">
    {% if request.path != url_for('list_holidays') %}
    <a class=" btn text-white" href="{{ url_for('list_holidays') }}">
      <i class="material-icons">arrow_back</i>
    </a>
    {% endif %}
    <h5>{{header}}</h5>
    <div class="filler"></div>
    <a class="btn text-white" href="{{ url_for('add') }}">
      <i class="material-icons">add</i>
    </a>
    <a class="btn text-white" href="{{ url_for('search') }}">
      <i class="material-icons">search</i>
    </a>
  </div>
  {% with messages = get_flashed_messages() %}
    {% if messages %}
    <div class="alert alert-primary mb-0" role="alert">
      {% for message in messages %}
        {{ message }}
      {% endfor %}
      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
        <span aria-hidden="true">×</span>
      </button>
    </div>
    {% endif %}
  {% endwith %}
  {% block content %}{% endblock %}
</div>
<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>

Ostatní stránky se rozšíří o layout.html pomocí níže uvedeného kódu:

{% extends "layout.html" %}
{% block content %}
  <!--obsah pro další stránky-->
{% endblock %}


Krok 4: Seznam svátků

Kořenová adresa URL aplikace spustí funkci list_holidays(...), která uvádí svátky pro určité datum.

Ve funkci a v celé aplikaci, holidays_date odkazuje na datum svátků, které mají být uvedeny, header odkazuje na název stránky a holidays_html odkazuje na html, který obsahuje svátky, které mají být uvedeny. Použijeme také funkci render_template(...), která vykreslí konkrétní html soubor z adresáře šablon. Dalšími argumenty přidanými do funkce jsou proměnné, které jsou předávány do html souboru.

V app.py aktualizujte list_holidays() pomocí kódu níže:

@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)
$HOME/holidays-viewer/templates/index.html
{% extends "layout.html" %}
{% block content %}
  <div class="holidays-html">
    {{holidays_html|safe}}
  </div>
{% endblock %}

Získání dnešního data

Pokud není zadáno žádné datum, uvedeme svátky pro dnešní datum. Chcete-li použít modul datetime Pythonu k získání dnešního data, importujte modul s from datetime import datetime a poté vytvořte následující funkci:

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

Zavolejte funkci v list_holidays(...):

if holidays_date is None:
        holidays_date = get_todays_date()

Získejte seznam svátků

Jakmile máme datum, získáme pro toto datum svátky. Wikipedia má stránku pro každé datum a svátky jsou v sekci s názvem "Holidays and observances". Abychom získali svátky, potřebujeme získat její číslo sekce a obsah v tomto čísle sekce.

Vytvořte funkci pro získání čísla sekce pomocí 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

Vytvořte funkci nazvanou get_holidays(...), abyste získali svátky v této sekci také pomocí API:Parse , pak zavolejte funkce v list_holidays(...):

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

Aktualizace odkazů na svátky

Vrácený kód HTML svátků obsahuje interní odkazy, které ukazují na tyto svátky, např. "/wiki/New_Years_Day". K těmto odkazům musíme přidat "//en.wikipedia.org" pomocí jQuery, abychom z nich vytvořili externí odkazy v naší aplikaci a otevřeli je na nové kartě. Chcete-li to provést, přidejte následující kód do $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;
    });
});

Poté přidejte jQuery do layout.html pomocí:

<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="static/update-links.js"></script>


Krok 5: Vyhledejte svátky jiných dat

Chcete-li získat svátky pro jiná data, vytvořte trasu pro vyhledávání, aby se zobrazil formulář, který shromažďuje měsíc a den k hledání:

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

    return render_template("search.html", header="Search date")
$HOME/holidays-viewer/templates/search.html
{% extends "layout.html" %}
{% block content %}
  <div class="instructions m-3">
    Search for holidays by date
  </div>
  <div class="base rounded shadow bg-white m-3">
    <form class="m-auto" action="/" method="POST">
      <fieldset>
        <div class="label-field">Select Month</div>
        <select class="bg-secondary mb-5 border-0" name="monthList">
          <option value="January">January
          <option value="February">February
          <option value="March">March
          <option value="April">April
          <option value="May">May
          <option value="June">June
          <option value="July">July
          <option value="August">August
          <option value="September">September
          <option value="October">October
          <option value="November">November
          <option value="December">December
        </select>
      </fieldset>
      <fieldset>
        <div class="label-field">Select Day</div>
        <select class="bg-secondary mb-5 border-0" name="dayList">
          <option value="1">1
          <option value="2">2
          <option value="3">3
          <option value="4">4
          <option value="5">5
          <option value="6">6
          <option value="7">7
          <option value="8">8
          <option value="9">9
          <option value="10">10
          <option value="11">11
          <option value="12">12
          <option value="13">13
          <option value="14">14
          <option value="15">15
          <option value="16">16
          <option value="17">17
          <option value="18">18
          <option value="19">19
          <option value="20">20
          <option value="21">21
          <option value="22">22
          <option value="23">23
          <option value="24">24
          <option value="25">25
          <option value="26">26
          <option value="27">27
          <option value="28">28
          <option value="29">29
          <option value="30">30
          <option value="31">31
        </select>
      </fieldset>
      <button type="submit" name="search" class="bg-primary btn btn-submit text-white">Submit</button>
    </form>
  </div>
{% endblock %}

Po odeslání vyhledávacího formuláře aktualizujte holidays_date na datum, které jste zadali. Chcete-li to provést, přidejte následující kód do 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

Krok 6: Přidání svátku

Stránka, na kterou přidáme nový svátek, je chráněna před úpravami anonymních uživatelů, takže se musíme nejprve přihlásit pomocí API:Přihlášení#Přihlášení klienta.

Chcete-li přidat svátek, pošlete požadavek na API:Úpravy s datem a popisem svátku. Úprava přidává nové svátky na tuto stránku na Test Wikipedia: Sandbox/Holidays_and_observances. Je to proto, aby se zabránilo přidávání testovacích svátků do anglické Wikipedie.

Po přidání svátků přesměrujte na domovskou stránku, kde se zobrazí také přidané svátky a zformátujte je tučně, abyste je odlišili od skutečných svátků. Chcete-li načíst zkušební svátky vedle skutečných svátků, aktualizujte 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")
$HOME/holidays-viewer/templates/login.html
{% extends "layout.html" %}
{% block content %}
  <div class="instructions m-3">
    <p>You need to login to Wikipedia in order to add a new holiday
  </div>
  <div class="base rounded shadow bg-white m-3">
    <form class="m-auto" action="/login" method="POST">
      <div class="form-group">
        <div class="form-field">
          <div class="label-field">Username</div>
          <input class="bg-secondary mb-5 border-0" name="username">
        </div>
        <div class="form-field">
          <div class="label-field">Password</div>
          <input class="bg-secondary mb-5 border-0" type="password" name="password">
        </div>
      </div>
      <button type="submit" name="login" class="bg-primary btn btn-submit text-white">Login</button>
    </form>
  </div>
{% endblock %}
$HOME/holidays-viewer/templates/add.html
{% extends "layout.html" %}
{% block content %}
  <div class="instructions m-3">
    <p>Add a new test holiday
  </div>
  <div class="base rounded shadow bg-white m-3">
    <form class="m-auto" action="" method="POST">
      <div class="form-group">
        <div class="form-field">
          <div class="label-field">Date [MMMM dd]</div>
          <input class="bg-secondary border-0 mb-5" name="date" placeholder="e.g April 1">
        </div>
        <div class="form-field">
          <div class="label-field">Description</div>
          <input class="bg-secondary border-0 mb-5" name="description" placeholder="e.g April fools' day">
        </div>
      </div>
      <button type="submit" name="add" class="bg-primary btn btn-submit text-white">Add</button>
    </form>
  </div>
{% endblock %}

Krok 7: Styling aplikace

Chcete-li do naší aplikace přidat další styl, vytvořte šablonu stylů s názvem style.css a propojte ji z layout.html přidáním <link rel="stylesheet" href="static/style.css">.

$HOME/holidays-viewer/static/style.css
.content {
    width: 420px;
    min-height: 100vh;
}

.holidays-html{
    overflow-y: auto;
    overflow-x: hidden;
    max-height: 88vh;
    scrollbar-width: thin;
}

.base {
    height: 400px;
    display: flex;
}

input, select {
    width: 300px;
    height: 40px;
}

.btn-submit {
    width: 300px;
}

.btn {
    cursor: pointer;
    align-content: center;
    background-color: transparent;
}

.bg-primary {
    background-color: #36c !important;
}

.bg-primary-dark {
    background-color: #2a4b8d !important;
}

.bg-secondary {
    background-color: #eaecf0 !important;
}

.header-bar {
    height: 48px;
    display: flex;
    flex: 1;
    align-items: center;
}

.filler {
    flex-grow: 1;
    text-align: center
}

h2 {
    display: none;
}

ul {
    margin: 5px;
    padding: 0;
}

li  {
    list-style-type: none;
    margin-bottom: 4px;
    background-color: white;
    padding: 8px;
    border-radius: 5px;
}

ul li li {
    box-shadow: 0 .5rem 1rem rgba(0,0,0,.15);
}

Rozvržení aplikace

V tomto okamžiku by struktura vaší aplikace měla být:

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

S app.py a layout.html jsou:

$HOME/holidays-viewer/app.py
#!/usr/bin/python3

"""
    app.py

    Ukázky MediaWiki API

    Prohlížeč svátků: Ukázková aplikace, která načte den svátků z Wikipedie s možností vyhledávat svátky jiných dat a přihlásit se pro přidání nových svátků.

    Licence MIT
"""

from datetime import datetime
from flask import Flask, render_template, flash, request, url_for, redirect
import requests


APP = Flask(__name__)
APP.secret_key = 'your_secret_key'

URL = "https://en.wikipedia.org/w/api.php"
TEST_URL = "https://test.wikipedia.org/w/api.php"
TEST_PAGE = "Sandbox/Holidays_and_observances"
S = requests.Session()
IS_LOGGED_IN = False

@APP.route('/', methods=['GET', 'POST'])
@APP.route('/<holidays_date>', methods=['GET', 'POST'])
def list_holidays(holidays_date=None):
    """ Uvádí svátky pro aktuální datum nebo vlastní datum
    """

    if holidays_date is None:
        holidays_date = get_todays_date()

    # Aktualizovat datum na vlastní datum
    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

    # Získejte čísla sekcí pro svátky na Wikipedii a pro ty na testovací stránce
    section_number = get_holidays_section(URL, holidays_date, None)
    test_section_number = get_holidays_section(TEST_URL, TEST_PAGE, holidays_date)

    holidays = get_holidays(URL, holidays_date, section_number)
    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')

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


def get_todays_date():
    """ Získá aktuální měsíc jako text a aktuální den jako číslo
    """

    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

def get_holidays_section(url, page, date_to_get):
    """ Získá číslo sekce pro svátky na Wikipedii a svátky na testovací stránce
    """

    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

def get_holidays(url, page, section_number):
    """ Získá html, který obsahuje svátky
    """

    params = {
        "format":"json",
        "action":"parse",
        "prop":"text",
        "page": page,
        "section": section_number,
        "disableeditsection":1
    }

    response = S.get(url=url, params=params)
    data = response.json()
    text = data['parse']['text']['*']

    return text

@APP.route("/search")
def search():
    """ Vyhledá svátky podle vlastních dat
    """

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

@APP.route("/login", methods=['GET', 'POST'])
def login():
    """ Přihlásí se na Wikipedii
    """

    if request.method == 'POST' and 'login' in request.form:
        params_0 = {
            "action": "query",
            "meta": "tokens",
            "type": "login",
            "format": "json"
        }

        response = S.get(url=URL, params=params_0)
        data = response.json()

        login_token = data['query']['tokens']['logintoken']

        params_1 = {
            "action": "clientlogin",
            "username": str(request.form.get('username')),
            "password": str(request.form.get('password')),
            "loginreturnurl": "http://127.0.0.1:5000/login",
            "logintoken": login_token,
            "format": "json"
        }

        response = S.post(url=URL, data=params_1)
        data = response.json()

        if data['clientlogin']['status'] != 'PASS':
            flash('Oops! Something went wrong -- ' + data['clientlogin']['messagecode'])
        else:
            global IS_LOGGED_IN
            IS_LOGGED_IN = True
            flash('Login success! Welcome, ' + data['clientlogin']['username'] + '!')
            return redirect(url_for('add'))

    return render_template("login.html", header="Login")

@APP.route("/add", methods=['GET', 'POST'])
def add():
    """ Přidá nový svátek na testovací stránku a přesměrujte se na svátky daného data, abyste zobrazili přidané svátky
    """

    if not IS_LOGGED_IN:
        return redirect(url_for('login'))

    if request.method == 'POST' and 'add' in request.form:

        # Wiki kód pro formátování textu přidaného svátku jako položky seznamu a tučně
        holiday_text = "* '''" + str(request.form.get('description')) + "'''"
        date = str(request.form.get('date'))

        params_2 = {
            "action": "query",
            "meta": "tokens",
            "format": "json"
        }

        response = S.get(url=TEST_URL, params=params_2)
        data = response.json()

        csrf_token = data['query']['tokens']['csrftoken']

        params_4 = {
            "action": "edit",
            "title": TEST_PAGE,
            "token": csrf_token,
            "format": "json",
            "section": "new",
            "sectiontitle": date,
            "text": holiday_text,
        }

        response = S.post(url=TEST_URL, data=params_4)
        data = response.json()

        if data['edit']['result'] != 'Success':
            flash('Oops! Something went wrong -- ' + data['clientlogin']['messagecode'])
        else:
            flash('New holiday added successfully!')
            return redirect(url_for('list_holidays', holidays_date=date.replace(' ', '_')))

    return render_template("add.html", header="Add holiday")

if __name__ == "__main__":
    APP.run()
$HOME/holidays-viewer/templates/layout.html
<title>Holidays</title>

<link rel="stylesheet" href="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="//tools-static.wmflabs.org/fontcdn/css?family=Material+Icons">
<link rel="stylesheet" href="static/style.css">

<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="static/update-links.js"></script>

<div class="content bg-secondary rounded m-auto">
  <div class="title-bar bg-primary-dark text-white pl-2">
    <small>Holidays and observances</small>
  </div>
  <div class="header-bar bg-primary text-white shadow p-2">
    {% if request.path != url_for('list_holidays') %}
    <a class=" btn text-white" href="{{ url_for('list_holidays') }}">
      <i class="material-icons">arrow_back</i>
    </a>
    {% endif %}
    <h5>{{header}}</h5>
    <div class="filler"></div>
    <a class="btn text-white" href="{{ url_for('add') }}">
      <i class="material-icons">add</i>
    </a>
    <a class="btn text-white" href="{{ url_for('search') }}">
      <i class="material-icons">search</i>
    </a>
  </div>
  {% with messages = get_flashed_messages() %}
    {% if messages %}
    <div class="alert alert-primary mb-0" role="alert">
      {% for message in messages %}
        {{ message }}
      {% endfor %}
      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
        <span aria-hidden="true">×</span>
      </button>
    </div>
    {% endif %}
  {% endwith %}
  {% block content %}{% endblock %}
</div>
<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
Snímek obrazovky domovské stránky prohlížeče Holidays


Další kroky

Související odkazy