API:Tatil görüntüleyicisi

From mediawiki.org
This page is a translated version of the page API:Holidays viewer and the translation is 99% complete.

Genel bakış

Bu eğitim, Vikipedi'den belirli bir tarihe ait tatilleri ve kutlamaları getiren bir demo uygulamasının nasıl oluşturulacağını ve yeni tatiller eklemek için oturum açma seçeneğini kapsar.

Demo uygulamasını oluşturmak için kullanılan araçlar ve teknolojiler şunlardır:

A step-by-step process for building this application

1. adım: Python ve Flask geliştirme ortamını ayarlayın

Python, çoğu Linux dağıtımında önceden yüklenmiş olarak gelir. Diğer işletim sistemleri için kurulum talimatları için Python başlangıç ​​kılavuzuna bakın.

pip install flask çalıştırarak Flask'ı kurun. Pip'iniz yoksa, resmi Pip web sitesinden alın


2. adım: Basit bir Flask uygulaması oluşturun

Ana dizininizde, tüm uygulamanın dosyalarını içerecek holidays-viewer adlı bir klasör oluşturun. Klasörün içinde app.py adlı bir dosya oluşturun ve aşağıdaki kodu içine yerleştirin:

#!/usr/bin/python3

from flask import Flask

APP = Flask(__name__)

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

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

python app.py komutunu kullanarak uygulamayı çalıştırın ve tarayıcınızda http://127.0.0.1:5000/ açın. "Holidays and observances" görüntülendiğinizi görmelisiniz.

3. adım: Temel düzeni oluşturun

Uygulamanın dört sayfası olacaktır: ana sayfa, bir arama sayfası, bir giriş sayfası ve bir ekleme sayfası. Her sayfanın bazı ortak öğeleri olacaktır, bu nedenle bu öğeleri içermek için layout.html adında bir temel düzen dosyası oluşturmamız gerekir.

Bir öğeye belirli bir CSS stilini uygulamak için Bootstrap sınıfları, ekle, ara ve geri ok simgeleri için Materialize simgeleri ve temel düzeni diğer sayfalara genişletmek ve Python'dan HTML'ye değişkenleri aktarmak için Jinja kullandığımızı unutmayın.

$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>

Diğer sayfalar aşağıdaki layout.html kodu kullanarak uzatacak:

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

4. adım: Tatilleri listeleyin

Uygulamanın kök URL'si, belirli bir tarihteki tatilleri listeleyen list_holidays(...) işlevini tetikleyecektir.

İşlevde ve uygulama genelinde, holidays_date listelenecek tatillerin tarihini, header sayfanın başlığını, holidays_html ise listelenecek tatilleri içeren html'yi ifade eder. Ayrıca, şablonlar dizininden belirli bir html dosyası oluşturan render_template(...) işlevini de kullanacağız. İşleve eklenen diğer argümanlar html dosyasına aktarılan değişkenlerdir.

app.py içinde, list_holidays() ile aşağıdaki kodla güncelleyin:

@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 %}

Bugünün tarihini alın

Tarih belirtilmezse, bugünün tarihi için tatilleri listeleriz. Python'un datetime modülünü kullanarak bugünün tarihini almak için modülü from datetime import datetime ile içe aktarın ve ardından aşağıdaki işlevi oluşturun:

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

İşlevini list_holidays(...) cinsinden çağırın:

if holidays_date is None:
        holidays_date = get_todays_date()

Listelenecek tatilleri alın

Tarihi aldığımızda, o tarih için tatilleri alırız. Vikipedi'nin her tarih için bir sayfası vardır ve tatiller "Holidays and observances" başlıklı bir bölümün altındadır. Tatilleri görmek için bölüm numarasını ve o bölüm numarasındaki içeriği almamız gerekiyor.

API:Parse kullanarak bölüm numarasını almak için bir işlev oluşturun:

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

Bu bölümdeki tatilleri API:Parse kullanarak da almak için get_holidays(...) adlı bir işlev oluşturun, ardından list_holidays(...) içindeki işlevleri çağırın:

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

Tatil bağlantılarını güncelleyin

Döndürülen tatillerin HTML'si, bu tatilleri gösteren dahili bağlantılar içerir, ör. "/wiki/New_Years_Day". Uygulamamızda harici bağlantılar yapabilmek ve yeni bir sekmede açmalarını sağlamak için jQuery kullanarak bu bağlantıların başına "//en.wikipedia.org" eklememiz gerekiyor. Bunu yapmak için, aşağıdaki kodu $HOME/holidays-viewer/static/update-links.js ekleyin:

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

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

Ardından layout.html ile jQuery ekleyin:

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


5. adım: Başka tarihlerdeki tatilleri arayın

Diğer tarihlerdeki tatilleri öğrenmek için, aranacak ayı ve günü toplayan bir formu görüntülemek üzere bir arama rotası oluşturun:

@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 %}

Arama formu gönderildikten sonra, holidays_date girilen tarih olacak şekilde güncelleyin. Bunu yapmak için, aşağıdaki kodu list_holidays(...) ekleyin:

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

6. adım: Tatil ekleyin

Yeni bir tatil ekleyeceğimiz sayfa anonim kullanıcılar tarafından yapılan düzenlemelere karşı korunmaktadır, bu nedenle önce API:Oturum aç#clientlogin kullanarak oturum açmamız gerekir.

Tatil eklemek için API:Edit ile tatilin tarihini ve açıklamasını içeren bir istek gönderin. Düzenleme, Test Vikipedi'deki bu sayfaya yeni tatiller ekler: Sandbox/Holidays_and_observances. Bu, İngilizce Vikipedi'ye test tatillerinin eklenmesini önlemek içindir.

Tatil eklendikten sonra, eklenen tatillerin de gösterileceği ana sayfaya yönlendirin ve onları gerçek tatillerden ayırmak için kalın olarak biçimlendirin. Test tatillerini gerçek tatillerin yanında getirmek için list_holidays(...) güncelleyin:

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 %}

7. adım: Uygulama şekillendirme

Uygulamamıza daha fazla stil eklemek için style.css adında bir stil sayfası oluşturun ve <link rel="stylesheet" href="static/style.css"> ekleyerek layout.html üzerinden ona bağlantı verin.

$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);
}

Uygulama düzeni

Bu noktada, uygulamanızın yapısı şu şekilde olmalıdır:

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

app.py ve layout.html olmak üzere:

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

"""
    app.py

    MediaWiki API Demoları

    Tatil görüntüleyicisi: Vikipedi'den diğer tarihlerdeki tatilleri arama ve yeni tatiller eklemek için giriş yapma seçenekleriyle günün tatillerini getiren bir demo uygulaması.

    MIT lisansı
"""

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):
    """ Geçerli tarih veya özel bir tarih için tatilleri listeler
    """

    if holidays_date is None:
        holidays_date = get_todays_date()

    # Update date to a custom date
    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

    # Get the section numbers for the holidays on Wikipedia and for those on the test page
    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():
    """ Geçerli ayı metin olarak ve geçerli günü sayı olarak alın
    """

    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):
    """ Vikipedi'deki tatiller ve tatiller için bölüm numarasını test sayfasından alın
    """

    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):
    """ Tatilleri içeren html'yi alın
    """

    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():
    """ Özel tarihlerdeki tatilleri arayın
    """

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

@APP.route("/login", methods=['GET', 'POST'])
def login():
    """ Vikipedi'yi oturum açın
    """

    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():
    """ Bir test sayfasına yeni bir tatil ekleyin ve eklenen tatilleri göstermek için o tarihin tatillerine yönlendirin
    """

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

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

        # Wiki markup to format the added holiday's text as a list item and in bold
        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>
Tatil görüntüleyicisi ana sayfa ekran görüntüsü

Sonraki adımlar

  • MediaWiki API'sini kullanarak geliştirdiğiniz bir demo uygulamasına bu kod örnekleri deposuna katkıda bulunun.

Ayrıca bakınız