واجهة برمجة التطبيقات:عارض صورة اليوم المختارة

This page is a translated version of the page API:Picture of the day viewer and the translation is 100% complete.

نظرة عامة

سوف تتعلم في هذا التدريب التعليمي كيفية تصميم عارض ويكيبيديا:صورة اليوم المختارة مستخدما واجهة برمجة تطبيقات ميدياويكي من النوع Action.

سوف يعلمك هذا التدريب التعليمي كيفية تنفيذ هذا مستخدما:

عملية خطوة بخطوة لتصميم هذا التطبيق

الخطوة 1: تأسيس بيئة تطوير البرمجيات مستخدما بايثون وفلاسك

تأسيس بايثون

يستخدم هذا التدريب التوضيحي بايثون الإصدار 3. يمكنك تنزيل أحدث إصدار من بايثون من هنا:

طالع الدليل الإرشادي للمبتدئين في نظام بايثون لمزيد من التعليمات التي تتناول تنصيب بايثون على أنظمة تشغيل أخرى.

تأسيس فلاسك

إن هو يمكنك استخدامه في تثبيت Flask: pip install flask. إن لم يكن لديك «بيب» بالفعل، ثبته من الموقع الشبكي الرسمي لبيب.


الخطوة 2: إنشاء تطبيق فلاسك بسيط

لو كان كل شيء مثبتًا لديك ويعمل على ما يرام، ضع النص البرمجي التالي في app.py، داخل مجلد مشروعك: $HOME/picture-of-the-day-viewer/app.py. حينما تشغله (مستخدمًا flask run أو python app.py)، سوف يعرض «Hello world» في http://localhost:5000/:

#!/usr/bin/python3

from flask import Flask
APP = Flask(__name__)

@APP.route("/")
def hello():
  return "Hello World!"

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

الخطوة 3: عارض صورة اليوم المختارة

منذ أن كل شيء جاهز، يمكنك البدء في كتابة الكود البرمجي لعارض صورة اليوم المختارة. صورة اليوم المختارة، اختصارًا «POTD»، هي Wikipedia:Wikipedia:Featured_pictures صورة مختارة تعرض يوميًا على الصفحة الرئيسية لويكيبيديا. سوف تحصل على الصورة من قالب ويكي يتغير كل يوم.

جلب تاريخ اليوم

منذ أن صورة اليوم المختارة تحدّث يوميًا، سوف تحتاج لتاريخ اليوم كي تصل إلى الأرشيف والحصول على نسخة مستقرة من الصورة الملائمة. كي تفعل ذلك، استورد الفئة date الخاصة ببايثون.

الخطوة التالية هي تحديد دالة جديدة هي index(). سوف تعرض Index() صفحة الويب وتمرر أية بيانات متعلقة باستدعاءات واجهة برمجة التطبيقات لديك. طالع عرض الصفحة لمزيد من المعلومات عن ملف index.html الذي سوف نستخدمه في صفة القالب المعني. أما الآن، يجب أن يحتوي index() على متغير يحتوي على التاريخ الجاري. سوف نستخدمه لاحقًا في صياغة استعلام للوصول إلى صورة اليوم المختارة.

#!/usr/bin/python3
from datetime import date
from flask import Flask, render_template

APP = Flask(__name__)

@APP.route("/")
def index():
  todays_date = date.today().isoformat()

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


استدعاء واجهة برمجة التطبيقات التي تحمل اسم Action

تعمل واجهة برمجة التطبيقات التي تحمل الاسم Action عن طريق إرسال البيانات ردًا على. كي تنفّذ ذلك، سوف تستورد مكتبة طلبات بايثون.

تاليًا أضف متغيرين اثنين جديدين: SESSION = requests.Session() وENDPOINT = "https://en.wikipedia.org/w/api.php". سوف تستخدم عنصر SESSION كي ترفع طلبات إلى معرف الموارد الموحد ENDPOINT.

استدعي في دالة جديدة، fetch_potd()، العنصر واجهة برمجة التطبيقات:صور كي تطلب دمج الصورة داخل صفحة صورة اليوم المختارة المحمية (مثال). لأغراض هذا الاستدعاء، استخدم اسم ملف الصورة كي تستدعي واجهة برمجة التطبيقات:معلومات_صورة ، والحصول على معرف الموارد الموحد المصدر للصورة. في هذا المثال، يتولى التعامل مع استدعاء واجهة برمجة التطبيقات الثاني الدالة المساعدة، fetch_image_src().

تدرج أرشيفات ويكيبيديا التاريخ مستخدمة صيغة، على سبيل المثال: 2019-01-31، تعبيرًا عن 31 يناير 2019. يمكنك الحصول على الصيغة الصحيحة مستخدمًا طريقة التاريخ هذه isoformat().

def fetch_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

def fetch_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

في الختام، غيّر index() كي تستدعي fetch_potd(). استورد render_template من flask، واجعل index() يرد render_template("index.html", data=data).


عرض الصفحة

 
لقطة من الشاشة من تطبيق عارض صورة اليوم المختارة

تحتوي قوالب Flask في الغالب على ترميز إتش تي إم إل، إلا أنها تحتوي أيضًا على Jinja لعرض محتوى متحرك. يبدو ترميز Jinja مثل ما يلي: {{ variable }}، ويستخدم في وضع متغيرات بايثون أو صيغها في بنية صفحاتنا الأساسية. أضف نص معياري أساسي من فئة وبضع عناصر إلى index.html. احرص على حفظ ذلك في مجلد داخل تطبيقك، اسمه /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>

تحويلها إلى صفحة تفاعلية

أضف عنصر ‎<form> إلى index.html، ثم امنحه مدخلات زر الإرسال التالية: «عودة - Back» و «التالي - Next». حينما لا يختار أي من الزرين، سوف يرسل النموذج طلب نشر «POST»، وسوف تمرر القيمة المختارة مرة أخرى إلى app.py. سوف يسمح هذا الأمر للمستخدمين تصفح أرشيف صورة اليوم المختارة.

بعد ذلك حدّث app.py داخل الدالة change_date()، كي تضبط التاريخ المعروض للمستخدم. وسّع أيضًا طريق / كي تتعامل مع طلبات النشر «POST» من النموذج. كي تسمح لملف app.py بقراءة رسائل طلبات النشر «POST»، استورد فئة Request من Flask.

هذا هو الكود البرمجي الكامل لبايثون وإتش تي إم إل:

$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

from datetime import date, timedelta
from flask import Flask, render_template, request
import requests

APP = Flask(__name__)
SESSION = requests.Session()
ENDPOINT = "https://en.wikipedia.org/w/api.php"
CURRENT_DATE = date.today()

@APP.route("/", methods=["GET", "POST"])
def index():
    """
    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)

def change_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

def fetch_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

def fetch_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>

تحديد نمط تطبيقك

يستعين Flask بمجلد، اسمه static، يحتوي على أية ملفات مساعدة تظل كما هي طوال مدة عمر التطبيق. هذا الموضع موضع مفيد يمكنك فيه وضع أية صفحات طرز أو نصوص برمجية إضافية. سوف تستخدم صفحة الطرز الخاصة بنا بعض الألوان والأشكال المرئية التي تستند إلى الدليل الإرشادي لأسلوب ويكيميديا. ضع ملف سي إس إس في $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: 1px solid #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: 3px 6px 10px rgba(0, 0, 0, 0.16);
}

.potd {
    width: inherit;
    height: 60vh;
}

figure {
    width: 100%;
    margin: auto;
    text-align: center;
}

figure img {
    display: block;
    margin: 12px auto; 
    max-width: 64vw;
    max-height: 50vh;
    border: 1px solid#3366cc; /* blue */
}

figure a {
    margin: 8px;
}

.date-container {
    display: block;
    position: absolute;
    bottom: 0;
    left: 0;
    right:0;
    text-align: center;
    font-weight: bold;
}

.current-date {
    margin: 16px auto;
    font-size: 2em;
    background: #ffffff;
    color: #72777d; /* grey */
}

.date-navigator {
    margin: 16px auto;
    font-size: 2em;
    text-transform: uppercase;
    text-align: center;
    background: #3366cc; /* blue */
    color: #ffffff;
}

.date-navigator input {
    margin: 8px;
    min-height: 44px;
    width: 45%;
    font-size: 0.67em;
    font-weight: inherit;
    text-transform: none;
    background: #3366cc; /* blue */
    color: inherit;
    border: 1px solid #ffffff;
    box-shadow: 3px 6px 10px rgba(0, 0, 0, 0.16);
    cursor: pointer;
}

.date-navigator input:hover {
    background: #447FF5; /* light blue */
}

.date-navigator input:active {
    background: #2a4b8d; /* dark blue */
    border: none;
    box-shadow: none;
}

.footer {
    text-align: center;
}

.date-navigator input:disabled {
    color: #c8cdff; /* grey */
    border: 1px solid #c8cdff; /* grey */
    box-shadow: none;
    cursor: default;
}

.date-navigator input:disabled:hover {
    background: #3366cc; /* blue */
}

الخطوات التالية

  • ساهم بتطبيق تجريبي صممته بنفسك مستخدمًا واجهة برمجة تطبيقات ميدياويكي في مستودع عينات الكود البرمجي هذا.
  • تعرف على بعض السبل لإضافة نص وصفي من صفحة صورة اليوم المختارة:
    • يقدم API:Search عنصر snippets يمكن استخدامه في صفة عارض لمحة سريعة عن وصلة شبكية
    • يمكن استخدام #1 للحصول على النص المعرب كاملًا من المقالات على مواقع الويكي التي نصّب فيها الامتداد #2 (طالع المثال).

انظر أيضا