تعزيز سير عمل التطوير باستخدام Gemini Code Assist

1- مقدمة

e5b98fd4e417c877.png

في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية الاستفادة من Gemini Code Assist في مراحل رئيسية من تطوير البرامج، مثل التصميم والإنشاء والاختبار والنشر. سنصمم ونطور تطبيقًا كاملاً وننشره على Google Cloud.

سننشئ واجهة برمجة تطبيقات وتطبيقًا للبحث عبر الجلسات في حدث فني. سيكون لكل جلسة عنوان وملخص ومدة وفئات ومتحدث واحد أو أكثر.

الأنشطة

  • تصميم وإنشاء واختبار ونشر تطبيق ويب بناءً على مواصفات OpenAPI من البداية

المعلومات التي ستطّلع عليها

  • كيفية استخدام Gemini Code Assist لإنشاء مواصفات OpenAPI
  • كيفية استخدام ميزات إنشاء الرموز في Gemini Code Assist لتطوير تطبيق Python Flask وفقًا لمواصفات OpenAPI
  • كيفية استخدام Gemini Code Assist لإنشاء واجهة أمامية على الويب لتطبيق Python Flask
  • كيفية استخدام Gemini Code Assist للحصول على مساعدة حول كيفية نشر التطبيق على Google Cloud Run
  • استخدِم ميزات Gemini Code Assist، مثل "تفسير الرموز" و"إنشاء حالات الاختبار"، أثناء إنشاء التطبيق واختباره.

المتطلبات

  • متصفح الويب Chrome
  • حساب Gmail
  • مشروع على Google Cloud تم تفعيل الفوترة فيه
  • تم تفعيل Gemini Code Assist لمشروعك على السحابة الإلكترونية

يستهدف هذا التمرين المعملي المطوّرين من جميع المستويات، بما في ذلك المبتدئين. على الرغم من أن نموذج التطبيق بلغة بايثون، فأنت لست بحاجة إلى أن تكون على دراية ببرمجة بايثون لفهم ما يحدث. سينصب تركيزنا على التعرّف على إمكانات Gemini Code Assist.

‫2. إعداد Gemini Code Assist

يتناول هذا القسم كل الإجراءات المطلوبة للبدء في هذا التمرين.

تفعيل Gemini Code Assist في بيئة Cloud Shell IDE

سنستخدم Cloud Shell IDE، وهي بيئة تطوير مستنِدة إلى Code OSS مُدار بالكامل، في بقية الدرس التطبيقي حول الترميز. نحتاج إلى تفعيل أداة Code Assist وضبطها في بيئة تطوير البرامج (IDE) في Cloud Shell، وتتوفّر الخطوات أدناه:

  1. انتقِل إلى ide.cloud.google.com. قد يستغرق ظهور بيئة التطوير المتكاملة (IDE) بعض الوقت، لذا يُرجى الانتظار وقبول أي خيارات تلقائية للإعداد. في حال ظهرت لك بعض التعليمات حول إعداد بيئة التطوير المتكاملة (IDE)، يُرجى المتابعة وإكمال هذه التعليمات باستخدام الإعدادات التلقائية.
  2. انقر على الزر Cloud Code - تسجيل الدخول في شريط مدى التقدّم السفلي كما هو موضَّح. عليك تفويض المكوّن الإضافي وفقًا للتعليمات. إذا ظهر لك "Cloud Code - no project" في شريط الحالة، اختَر ذلك ثم اختَر المشروع المحدّد على Google Cloud من قائمة المشاريع التي تخطّط للعمل فيها.

6f5ce865fc7a3ef5.png

  1. انقر على زر Code Assist في أسفل يسار الصفحة كما هو موضّح، واختَر مشروع Google Cloud الصحيح للمرة الأخيرة. إذا طُلِب منك تفعيل واجهة Cloud AI Companion API، يُرجى إجراء ذلك والمتابعة.
  2. بعد اختيار مشروعك على Google Cloud، تأكَّد من أنّه يمكنك رؤية ذلك في رسالة حالة رمز السحابة الإلكترونية في شريط الحالة وأنّ خدمة Code Assist مفعَّلة أيضًا على يسار الصفحة، في شريط الحالة كما هو موضَّح أدناه:

709e6c8248ac7d88.png

Gemini Code Assist جاهز للاستخدام

3- إعداد Firestore

Cloud Firestore هي قاعدة بيانات مستندات مُدارة بالكامل بدون خوادم سنستخدمها كخلفية لبيانات التطبيق. يتم تنظيم البيانات في Cloud Firestore في مجموعات من المستندات.

نحتاج إلى إنشاء مجموعة باسم sessions في قاعدة بيانات Firestore التلقائية. ستتضمّن هذه المجموعة نماذج من البيانات (المستندات) التي سنستخدمها بعد ذلك في طلبنا.

افتح المحطة الطرفية من داخل بيئة التطوير المتكاملة في Cloud Shell عبر القائمة الرئيسية كما هو موضّح أدناه:

f1535e14c9beeec6.png

يجب إنشاء مجموعة باسم sessions. تحتوي هذه القائمة على قائمة بنماذج مستندات الجلسات. سيحتوي كل مستند على السمات التالية:

  1. title: string
  2. فئات: مصفوفة من السلاسل
  3. مكبرات الصوت: مصفوفة من السلاسل
  4. duration: سلسلة
  5. summary: سلسلة

لنقم بتعبئة هذه المجموعة بعيّنة بيانات عن طريق نسخ ملف يحتوي على عيّنات بيانات إلى حزمة في مشروعك الخاص، حيث يمكنك بعد ذلك استيراد المجموعة باستخدام الأمر gcloud firestore import.

إعداد قاعدة بيانات Firestore

انتقِل إلى صفحة متجر Firestore في Cloud Console.

إذا لم يسبق لك إعداد قاعدة بيانات Firestore في المشروع، أنشئ قاعدة بيانات default. أثناء إنشاء قاعدة البيانات، انتقل إلى القيم التالية:

  • وضع Firestore: Native
  • الموقع الجغرافي: اختَر نوع الموقع الجغرافي Region واختَر المنطقة المناسبة لتطبيقك. دوِّن هذا الموقع حيث ستحتاج إليه في الخطوة التالية لموقع الحزمة.
  • قم بإنشاء قاعدة البيانات.

504cabdb99a222a5.png

سننشئ الآن مجموعة sessions باتّباع الخطوات التالية:

  1. أنشِئ حزمة في مشروعك باستخدام الأمر gsutil الموضّح أدناه. استبدِل المتغيّر <PROJECT_ID> في الأمر أدناه برقم تعريف مشروعك على Google Cloud. استبدِل <BUCKET_LOCATION> باسم منطقة يتوافق مع "المنطقة الجغرافية" لقاعدة بيانات Firestore التلقائية (كما هو موضّح في الخطوة السابقة)، ويمكن أن يكون هذا الاسم US-WEST1 أو EUROPE-WEST1 أو ASIA-EAST1 :
gsutil mb -l <BUCKET-LOCATION> gs://<PROJECT_ID>-my-bucket
  1. الآن بعد أن تم إنشاء الحزمة، نحتاج إلى نسخ عملية تصدير قاعدة البيانات التي أعددناها إلى هذه الحزمة، قبل أن نتمكن من استيرادها إلى قاعدة بيانات Firebase. استخدِم الأمر الموضح أدناه:
gsutil cp -r gs://sessions-master-database-bucket/2024-03-26T09:28:15_95256  gs://<PROJECT_ID>-my-bucket

الآن بعد أن أصبح لدينا البيانات المطلوب استيرادها، يمكننا الانتقال إلى الخطوة الأخيرة المتمثلة في استيراد البيانات إلى قاعدة بيانات Firebase (default) التي أنشأناها.

  1. استخدم الأمر gcloud الموضح أدناه:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2024-03-26T09:28:15_95256

ستستغرق عملية الاستيراد بضع ثوانٍ، وعندما تصبح جاهزة، يمكنك التحقّق من صحة قاعدة بيانات Firestore والمجموعة من خلال الانتقال إلى https://console.cloud.google.com/firestore/databases، واختيار قاعدة بيانات default ومجموعة sessions كما هو موضَّح أدناه:

d3e294d46ba29cd5.png

وبذلك تكون قد أكملت إنشاء مجموعة Firestore التي سنستخدمها في تطبيقنا.

‫4. إنشاء قالب التطبيق

سننشئ نموذجًا من التطبيقات (تطبيق Python Flask) سنستخدمه في باقي الدرس التطبيقي حول الترميز. سيبحث هذا التطبيق في الجلسات المتاحة في مؤتمر تقني.

يُرجى اتّباع الخطوات التالية:

  1. انقر على اسم مشروع Google Cloud في شريط الحالة أدناه.

f151759c156c124e.png

  1. ستظهر قائمة بالخيارات. انقر على تطبيق جديد من القائمة أدناه.

91ea9836f38b7f74.png

  1. اختَر تطبيق Cloud Run (سيكون وقت تشغيل التطبيق).
  2. اختَر نموذج التطبيق Python (Flask): Cloud Run.
  3. أدخل اسمًا للتطبيق واحفظه في موقعك المفضل.
  4. سيظهر إشعار يؤكد إنشاء تطبيقك، وتظهر نافذة جديدة يتم فيها تحميل تطبيقك كما هو موضّح أدناه. يتم فتح ملف README.md. يمكنك إغلاق هذا العرض في الوقت الحالي.

aaa3725b17ce27cf.png

5- التفاعل مع Gemini Code Assist

لأغراض هذا الدرس، سنستخدم محادثة Gemini Code Assist المتوفّرة في Cloud Shell IDE كجزء من إضافة Cloud Code في VS Code. يمكنك إظهاره من خلال النقر على الزر Code Assist في شريط التنقل الأيمن. ابحث عن رمز Code Assist a489f98a34898727.png في شريط أدوات التنقّل الأيمن وانقر عليه.

سيؤدي ذلك إلى ظهور جزء محادثة Code Assist داخل Cloud Shell IDE ويمكنك الدردشة باستخدام Code Assist.

14ad103efaa0ddaa.png

لاحظ رمز المهملات في الأعلى، وهذه هي طريقتك لإعادة ضبط سياق سجلّ محادثات Code Assist. يُرجى العِلم أيضًا أنّ هذه المحادثة مرتبطة بالملفات التي تعمل عليها في بيئة التطوير المتكاملة.

6. تصميم واجهة برمجة التطبيقات

ستكون خطوتنا الأولى هي الحصول على المساعدة من Gemini Code Assist خلال مرحلة التصميم. في هذه الخطوة، سننشئ مواصفات OpenAPI للكيانات (الجلسات الفنية في حدث) التي نريد البحث فيها.

أدخِل الطلب التالي:

Generate an Open API spec that will allow me to retrieve all sessions, sessions by category, session by id. Each session has the following attributes: id, title, list of speakers, list of categories, summary and duration.

ومن المفترض أن يؤدي هذا إلى إنشاء مواصفات OpenAPI للبحث عبر الجلسات عبر معلَمات طلب بحث متنوعة. في ما يلي عيّنة المواصفات:

openapi: 3.0.0
info:
 title: Sessions API
 description: This API allows you to retrieve all sessions, sessions by category, and session by id.
 version: 1.0.0
servers:
 - url: https://sessions.example.com
paths:
 /sessions:
   get:
     summary: Get all sessions
     operationId: getSessions
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
 /sessions/{id}:
   get:
     summary: Get session by id
     operationId: getSessionById
     parameters:
       - name: id
         in: path
         required: true
         description: The id of the session
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/Session'
 /sessions/categories/{category}:
   get:
     summary: Get sessions by category
     operationId: getSessionsByCategory
     parameters:
       - name: category
         in: path
         required: true
         description: The category of the sessions
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
components:
 schemas:
   Session:
     type: object
     properties:
       id:
         type: string
         description: The id of the session
       title:
         type: string
         description: The title of the session
       speakers:
         type: array
         items:
           type: string
         description: The list of speakers for the session
       categories:
         type: array
         items:
           type: string
         description: The list of categories for the session
       summary:
         type: string
         description: The summary of the session
       duration:
         type: string
         description: The duration of the session

ويمكنك ملاحظة أنّ المواصفات تشمل ما يلي:

  • وهو مخطط محدّد لـ نوع الجلسة.
  • تم تحديد العديد من مسارات واجهة برمجة التطبيقات:
  • /sessions
  • /sessions/{id}
  • /sessions/categories/{category}

أنشِئ ملفًا باسم sessionsapi.yaml في المجلد العلوي وانسخ المحتوى من نافذة محادثة Code Assist باستخدام الخيار "إدراج في الملف الحالي" (الزر +) واترك الملف مفتوحًا في بيئة تطوير البرامج (IDE) في Cloud Shell.

في هذه المرحلة، يمكنك ملاحظة ميزة مثيرة للاهتمام في Gemini Code Assist، وهي: الاقتباس. يتم عرض هذه المعلومات للمطوّر عند اقتباس الرمز الذي تم إنشاؤه طوليًا مباشرةً من مصدر آخر، مثل رمز المصدر المفتوح الحالي. ويوفر المصدر والترخيص لمطوّر البرامج ليقرر ما يريد فعله بها.

بافتراض أننا لا مشكلة في المحتوى الذي تم إنشاؤه، يمكننا الآن استخدام مستند المواصفات هذا لإنشاء تطبيق بايثون Flask له.

7. إنشاء التطبيق

سنطلب الآن من Code Assist إنشاء التطبيق. أدخِل الطلب التالي مع فتح ملف sessionsapi.yaml.

Generate a Python Application using the Flask framework, based on the sessionsapi.yaml file. This application uses a local in memory list of sessions. Do not use any Flask extensions.

من المفترض أن يزوّدك هذا بهيكل أساسي لتطبيق Python Flask يستند إلى الوظائف والمسارات التي تم تحديدها في ملف مواصفات OpenAPI.

يجب أن يكون رمز تطبيق Python Flask المقدَّم مماثلاً لرمز التطبيق التالي:

from flask import Flask, jsonify, request

app = Flask(__name__)

sessions = [
    {
        "id": "1",
        "title": "Session 1",
        "speakers": ["Speaker 1", "Speaker 2"],
        "categories": ["Category 1", "Category 2"],
        "summary": "This is a summary of session 1.",
        "duration": "1 hour",
    },
    {
        "id": "2",
        "title": "Session 2",
        "speakers": ["Speaker 3", "Speaker 4"],
        "categories": ["Category 3", "Category 4"],
        "summary": "This is a summary of session 2.",
        "duration": "1 hour 30 minutes",
    },
]

@app.route('/sessions', methods=['GET'])
def get_sessions():
    return jsonify(sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
    session = next((session for session in sessions if session['id'] == id), None)
    if session is None:
        return jsonify({}), 404
    return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
    sessions_by_category = [session for session in sessions if category in session['categories']]
    return jsonify(sessions_by_category)

if __name__ == '__main__':
    app.run()

هناك ملف app.py حالي تم إنشاؤه كجزء من الخطوة السابقة. ما عليك سوى استبدال محتوياته بالرمز الذي تم إنشاؤه بواسطة Code Assist وحفظ الملف.

نريد تغيير السطر app.run() لاستخدام المنفذ 8080 وعنوان المضيف 0.0.0.0 والتشغيل أيضًا في وضع تصحيح الأخطاء أثناء التنفيذ المحلي.إليك طريقة تنفيذ ذلك. لنبدأ أولاً بتمييز/اختيار السطر:

app.run()

بعد ذلك، في واجهة Chat Assist، اكتب الطلب: Explain this.

ومن المفترض أن يُظهر هذا شرحًا مفصلاً لهذا السطر تحديدًا، وقد تم توضيح مثال له أدناه:

58ec896a32a4fb68.png

الآن، استخدم المطالبة التالية:

update the code to run the application on port 8080, host address 0.0.0.0, and in debug mode

يجب أن يكون الرمز المقترح الذي تم إنشاؤه على النحو التالي:

app.run(host='0.0.0.0', port=8080, debug=True)

احرص على تعديل ملف app.py باستخدام هذا المقتطف.

تشغيل التطبيق على الجهاز

لنقم بتشغيل التطبيق محليًا الآن للتحقق من صحة متطلبات التطبيق وفقًا لما بدأنا به.

تتمثل الخطوة الأولى في إنشاء بيئة Python افتراضية باستخدام تبعيات حزمة Python في requirements.txt ليتم تثبيتها في البيئة الافتراضية. ولإجراء ذلك، انتقِل إلى لوحة الأوامر (Ctrl+Shift+P) في بيئة التطوير المتكاملة في Cloud Shell واكتب إنشاء بيئة Python. اتّبِع الخطوات القليلة التالية لاختيار بيئة افتراضية (venv) ومترجم بتنسيق Python 3.x وملف requirements.txt.

بعد إنشاء البيئة، شغِّل نافذة طرفية جديدة (Ctrl+Shift+`) وأدخِل الأمر التالي:

python app.py

وفي ما يلي مثال لعملية التنفيذ:

(.venv) romin@cloudshell: $ python app.py 
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080
 * Running on http://10.88.0.3:8080
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 132-247-368

يمكنك الآن معاينة واجهة برمجة التطبيقات على عناوين URL التالية. نفترض أن خادم التطوير يعمل على المنفذ 8080. إذا لم يكن كذلك، يُرجى تغييره إلى رقم المنفذ المناسب.

  • https://<host-name>:8080/sessions
  • https://<host-name>:8080/sessions/{id}
  • https://<host-name>:8080/sessions/categories/{category}

اتّبِع الخطوات الواردة أدناه للتأكّد من أنّه يمكنك استرداد بيانات JSON المضمّنة في ملف app.py باستخدام عناوين URL هذه:

افتح نافذة طرفية جديدة وجرِّب أيًّا من الأوامر التالية:

curl -X GET http://127.0.0.1:8080/sessions
curl -X GET http://127.0.0.1:8080/sessions/<ID>
curl -X GET http://127.0.0.1:8080/sessions/categories/<CATEGORY_NAME> 

8. إعادة بناء التعليمات البرمجية

بدلاً من أن يحتوي app.py على عيّنة بيانات JSON غير قابلة للتغيير في البرنامج، قد نرغب في فصل/استخراج هذه البيانات في وحدة أخرى، لكي نتمكّن من الفصل بين الرمز والبيانات بشكل واضح. لنقم بذلك!

يُرجى إبقاء ملف app.py مفتوحًا وإرسال الطلب التالي:

Can I improve this code and separate out the sessions data from this app.py file?

ومن المفترض أن يمنحك هذا بعض الاقتراحات حول كيفية القيام بذلك. فيما يلي نموذج اقتراح تلقيناه ومن المفترض أن تحصل على شيء مشابه له:

9b9c56cb527dac4c.png

بعد ذلك، سنفصل بياناتنا في ملف sessions.py حسب ما تقترحه أداة Code Assist.

إنشاء ملف جديد باسم sessions.py

, يكون محتواها عبارة عن قائمة JSON وفقًا للبيانات التي ننشئها، في ما يلي:

sessions = [
   {
       "id": "1",
       "title": "Session 1",
       "speakers": ["Speaker 1", "Speaker 2"],
       "categories": ["Category 1", "Category 2"],
       "summary": "This is a summary of session 1.",
       "duration": "1 hour",
   },
   {
       "id": "2",
       "title": "Session 2",
       "speakers": ["Speaker 3", "Speaker 4"],
       "categories": ["Category 3", "Category 4"],
       "summary": "This is a summary of session 2.",
       "duration": "1 hour 30 minutes",
   },
]

وقد تم الآن تبسيط الملف app.py إلى حد كبير، كما هو موضَّح أدناه:

from flask import Flask, jsonify, request
from sessions import sessions

app = Flask(__name__)

@app.route('/sessions', methods=['GET'])
def get_sessions():
   return jsonify(sessions.sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
   session = next((session for session in sessions.sessions if session['id'] == id), None)
   if session is None:
       return jsonify({}), 404
   return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   return jsonify(sessions_by_category)

if __name__ == '__main__':
   app.run(host='0.0.0.0', port=8080, debug=True)

يُرجى التأكد من أنّه لا يزال بإمكانك تشغيل التطبيق محليًا وممارسة واجهة برمجة التطبيقات باستخدام هذه التغييرات التي تمت إعادة ضبط إعداداتها. من المحتمل أن يكون خادم تطوير python لا يزال قيد التشغيل، لذا عليك فقط استدعاء أوامر curl من الخطوة السابقة.

9. الدمج مع مجموعة Firestore

تتمثل الخطوة التالية في الانتقال من قائمة JSON المحلية المتوفرة في الذاكرة لجلساتنا وربط تطبيقنا بمجموعة الجلسات في قاعدة بيانات Firestore التي أنشأناها في بداية هذا الدرس التطبيقي حول الترميز.

يُرجى إبقاء ملف sessions.py مفتوحًا وإرسال الطلب التالي:

Can you further refactor the sessions.py code to read from a Firestore database that has a collection named sessions. The collection has the same attributes as the session object that we have defined. Use the Python module google-cloud-firestore. 

لقد تلقّينا الاقتراح التالي لقراءة جميع الجلسات من مجموعة Firestore:

import google.cloud.firestore

# Create a Firestore client
db = google.cloud.firestore.Client()

# Get the sessions collection
sessions_collection = db.collection("sessions")

# Create a list of sessions
sessions = []

# Iterate over the documents and add them to the list
for doc in sessions_collection.stream():
   session = doc.to_dict()
   session["id"] = doc.id
   sessions.append(session)

ادمج الرمز في sessions.py.

إذا كان خادم Flask Development مُشغَّلاً محليًا، قد يرجع السبب إلى توقف التطبيق عن العمل للشكوى من عدم العثور على وحدة Python.

يمكنك أن تطلب من Code Assist مثلاً تحديد وحدة Python التي يجب إضافتها إلى ملف requirements.txt على النحو التالي:

Which Python package needs to be installed to make the firestore code work?

سيقدم لك هذا اسم وحدة Python (مثل google-cloud-firestore). أضف ذلك إلى ملف requirements.txt.

عليك إعادة إنشاء بيئة Python باستخدام الوحدة المضافة حديثًا (google-cloud-firestore). لإجراء ذلك، أدخِل الأمر التالي في نافذة Terminal الحالية:

pip install -r requirements.txt

شغِّل التطبيق مرة أخرى (أعِد تشغيله باستخدام python app.py) وانتقِل إلى عنوان URL لـ /sessions. من المفترض أن تحصل الآن على نماذج المستندات التي أضفناها إلى مجموعة sessions.

975d05e6518f1a6a.png

يمكنك طلب البحث عن معرفات الموارد المنتظمة (URI) الأخرى لاسترداد جلسات محددة أو جميع الجلسات لفئة معينة كما هو موضح في الخطوات السابقة.

10. شرح الرمز

حان الوقت الآن لاستخدام ميزة "Explain this" في Gemini Code Assist للحصول على بعض المعلومات عن الرمز البرمجي. يمكنك الانتقال إلى أي ملف من الملفات أو اختيار مقتطفات محدَّدة من الرموز وتوجيه الطلب التالي إلى Code Assist: Explain this.

لإجراء ذلك، انتقِل إلى ملف sessions.py وميِّز الرمز المحدَّد في Firestore واحصل على شرح للرمز. حاول أيضًا استخدام هذه الميزة في ملفات أخرى في مشروعك، وليس فقط كود بايثون.

11. إنشاء تطبيق الويب

والآن بعد أن أنشأنا واجهة برمجة التطبيقات ودمجناها مع مجموعة Firestore المباشرة، دعنا ننشئ واجهة أمامية مستندة إلى الويب للتطبيق. تعمل واجهتنا الأمامية على الويب في الوقت الحالي على الحفاظ على وظائفها إلى أدنى حد، أي أنّها قادرة على البحث عن الجلسات التي تنتمي إلى فئة معيّنة. يُرجى العِلم أنّ لدينا مسار واجهة برمجة تطبيقات، أي /sessions/categories/{category}، وبالتالي من المفترض أن يستدعي تطبيق الويب ذلك ويردّ النتائج.

لنتعمق في الأمر. قدّم المطالبة التالية إلى Code Assist:

Generate a web application that allows me to search for sessions by category and uses the Flask application that we created. Please use basic HTML, CSS and JS. Embed all the Javascript and CSS code into a single HTML file only.

سيؤدي هذا إلى إنشاء HTML لتطبيق الويب مع تضمين جافا سكريبت وCSS فيه. سيُطلب منك أيضًا إضافة مسار جديد إلى ملف app.py، لكي يتم عرض الصفحة الرئيسية لأي مستخدم ينتقل إلى الجذر أو عنوان URL الأساسي. إذا لم يُذكر هذه المعلومات، اسأل عنها أو استخدم المقتطف الموضح أدناه:

@app.route('/')
def index():
   return render_template('index.html')

يمكنك حفظ هذا الملف بتنسيق index.html ولكن قد يكون لديك سؤال حول المكان الذي يجب حفظ هذا الملف فيه (أي مجلد؟). يمكننا طرح سؤال متابعة على Code Assist.

Given that I am using the flask framework, where should I put the index.html file?

من المفترض أن يُقدّم لك معلومات واضحة تفيد بأنّه يستخدم إطار عمل render_template، وبالتالي يجب وضع ملف index.html داخل المجلد templates. ستجد هذا المجلد متاحًا لأنّنا أنشأنا تطبيقًا استنادًا إلى نموذج Flask في بداية هذا الدرس التطبيقي حول الترميز. ونتيجة لذلك، هناك ملف index.html حالي، وعليك ببساطة استبدال محتواه بالملف الجديد الذي تم إنشاؤه هنا. يشير Code Assist أيضًا إلى استيراد render_template في ملف app.py.

احفظ رمز تطبيق الويب في ملف index.html وتذكَّر أن تضع هذا الملف في مجلد templates.

تشغيل التطبيق على الجهاز

لنقم بتشغيل التطبيق محليًا الآن للتحقق من صحة متطلبات التطبيق وفقًا لما بدأنا به.

تأكَّد من أنّ خادم Flask المحلي لا يزال قيد التشغيل ولا يعرض أي أخطاء. وفي حال حدوث أخطاء، عليك معالجتها وبدء تشغيل الخادم.

بعد إعداد التطبيق وتشغيله، يمكنك الانتقال إلى عنوان URL للصفحة الرئيسية للتطبيق. من المفترض أن يظهر لك ملف index.html المعروض أدناه:

8ca586acc4536879.png

قدِّم إحدى الفئات من البيانات كإدخال (مثل AI) وانقر على الزر Search. من المفترض أن يؤدي ذلك إلى عرض الجلسات التي تم وضع علامة عليها باستخدام الفئة AI.

165faded790a6c.png

التمرين الاختياري هو عرض بيانات وصفية إضافية مثل المدة والفئات والمتحدثين والملخص لكل جلسة.

12. إنشاء حالة اختبار

لقد أنشأنا واجهة برمجة تطبيقات الجلسات، وقد حان الوقت لاستخدام Gemini Code Assist الآن لإنشاء حالات اختبار للوحدات لمسارات واجهات برمجة التطبيقات المختلفة.

يُرجى إبقاء ملف app.py مفتوحًا وإرسال الطلب التالي:

Generate unit test cases for app routes. Use unittest module.

لقد حصلنا على الرد التالي:

import unittest
from app import app

class FlaskAppTestCase(unittest.TestCase):

    def test_index(self):
        tester = app.test_client()
        response = tester.get('/')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions(self):
        tester = app.test_client()
        response = tester.get('/sessions')
        self.assertEqual(response.status_code, 200)

    def test_get_session_by_id(self):
        tester = app.test_client()
        response = tester.get('/sessions/1')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions_by_category(self):
        tester = app.test_client()
        response = tester.get('/sessions/categories/category1')
        self.assertEqual(response.status_code, 200)

if __name__ == '__main__':
    unittest.main()

أنشئ ملفًا باسم tests.py باستخدام الرمز أعلاه.

ملاحظة حول إنشاء حالات اختبار

قد تظهر لك قائمة رموز مختلفة عن تلك الواردة أعلاه، ما قد يتسبب في بعض المشاكل أثناء تنفيذ حالات الاختبار. على سبيل المثال، لاحظنا أنه في بعض عمليات التشغيل التي نجريها، تم إضاعة الرموز الرئيسية التالية:

from app import app

يجب إدخال الرمز أعلاه لاستيراد تطبيق Flask الحالي الذي سنستدعي حالات الاختبار وفقًا له.

if __name__ == '__main__':

`unittest.main()`

يجب استخدام الرمز أعلاه لإجراء حالات الاختبار.

ننصحك بالاطّلاع على كل حالة من حالات الاختبار والتحقّق من assertEqual والشروط الأخرى في الرمز الذي تم إنشاؤه للتأكّد من نجاحها. ونظرًا لأن البيانات خارجية في جمع Firestore، فقد لا تتمكن من الوصول إليها وقد تستخدم بعض البيانات الوهمية ونتيجة لذلك قد تخفق الاختبارات. لذلك، قم بتعديل حالات الاختبار الخاصة بك وفقًا لذلك أو قم بالتعليق على بعض حالات الاختبار التي قد لا تحتاجها على الفور.

وكعرض توضيحي، أجرينا حالات الاختبار باستخدام الأمر التالي (تأكد من تشغيل خادم التطوير المحلي، إذ سيتم إجراء الاتصالات إلى نقاط نهاية واجهة برمجة التطبيقات المحلية):

python tests.py

حصلنا على النتيجة الموجزة التالية:

Ran 4 tests in 0.274s

FAILED (failures=2)

هذا صحيح فعلاً لأنّ رقم تعريف الجلسة لم يكن صحيحًا في الاختبار الثالث ولم تكن هناك فئة باسم category1

.

لذا اضبط حالات الاختبار وفقًا لذلك واختبرها.

13 التطوير المستند إلى الاختبار

لنطّلع الآن على إضافة طريقة بحث جديدة في واجهة برمجة تطبيقات الجلسات الخاصة بنا وفقًا لمنهجية الاختبار القائم على التطوير (TDD)، والتي تتعلق بكتابة حالات الاختبار أولاً، ما يجعلها تخفق بسبب عدم التنفيذ، واستخدام Gemini Code Assist لإنشاء التنفيذ غير الناجح كي يجتاز الاختبار.

انتقِل إلى ملف Testing.py (على افتراض أنّك أصلحت ملف tests.py وحصلت على جميع الاختبارات التي اجتازت العملية). طرح سؤال في Code Assist على الطلب التالي:

Generate a new test case to search for sessions by speaker

ساعدنا ذلك في تنفيذ حالة الاختبار التالية التي أدرجناها كما يجب في ملف tests.py.

  def test_get_sessions_by_speaker(self):
        tester = app.test_client()
        response = tester.get('/sessions/speakers/speaker1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json, [sessions.sessions[0], sessions.sessions[1]])

في حال إجراء الاختبارات، من المفترض أن يظهر لك الخطأ التالي:

$ python tests.py 
.F.
======================================================================
FAIL: test_get_sessions_by_speaker (__main__.FlaskAppTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/romin/hello-world-5/tests.py", line 21, in test_get_sessions_by_speaker
    self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200

----------------------------------------------------------------------
Ran 3 tests in 0.010s

FAILED (failures=1)

ويرجع ذلك إلى أنّ حالة الاختبار قد استدعاء المسار التالي (/sessions/speakers/) ولا يوجد تنفيذ لذلك في app.py.

اسمح لنا بأن نطلب من Code Assist تزويدنا بعملية تنفيذ. انتقِل إلى ملف app.py وأرسِل الطلب التالي إلى أداة Code Assist:

Add a new route to search for sessions by a specific speaker

حصلنا على عملية التنفيذ التالية التي اقترحها Code Assist، والتي أضفناها إلى ملف app.py:

@app.route('/sessions/speakers/<speaker>', methods=['GET'])
def get_sessions_by_speaker(speaker):
    sessions_by_speaker = [session for session in sessions.sessions if speaker in session['speakers']]
    return jsonify(sessions_by_speaker)

يمكنك الرجوع إلى ملف tests.py وتعديل حالة الاختبار على النحو التالي لإجراء فحص سريع:

   def test_get_sessions_by_speaker(self):
       tester = app.test_client()
       response = tester.get('/sessions/speakers/Romin Irani')
       self.assertEqual(response.status_code, 200)
       self.assertEqual(len(response.json), 1)

سار الاختبار بشكل جيد. ونترك لك تمرينًا يتيح لك الاطّلاع على حالات الاختبار التي تم إنشاؤها، وتعديلها قليلاً استنادًا إلى البيانات التي قد تكون لديك في Firestore واستخدام طرق assert* المناسبة في حالات اختبار Python Unit Test.

14. النشر إلى Google Cloud Run

والآن بعد أن شعرنا بالرضا عن جودة التطوير، ستكون خطوتنا الأخيرة هي نشر هذا التطبيق في Google Cloud Run. ولكن ربما، لأخذ تدابير جيدة، يجب أن نسأل Code Assist عما إذا كنا قد نسينا أي شيء. بعد فتح app.py، أرسِل الطلب التالي :

Is there something here I should change before I deploy to production?

سؤال جيد بما أننا نسينا تعيين علامة تصحيح الأخطاء على وضع إيقاف التشغيل :

2f87ed3a811fb218.png

كما هو موضّح، أوقِف تصحيح الأخطاء وتابِع لطلب المساعدة من Gemini Code Assist في ما يخصّ الأمر gcloud الذي يمكن استخدامه لنشر التطبيق في Cloud Run مباشرةً من المصدر (بدون الحاجة إلى إنشاء حاوية أولاً).

أدخِل الطلب التالي:

I would like to deploy the application to Cloud Run directly from source. What is the gcloud command to do that?

جرِّب بعض صيغ الطلب أعلاه. وأحد الأساليب الأخرى التي جربناها:

I would like to deploy this application to Cloud Run. I don't want to build a container image locally but deploy directly from source to Cloud Run. What is the gcloud command for that?

من المفترض أن يظهر لك الأمر gcloud التالي:

gcloud run deploy sessions --source .

قد تحصل أيضًا على ما يلي:

gcloud run deploy <service-name> --source . \
—-platform managed \
—-allow-unauthenticated

نفِّذ الأمر أعلاه من المجلد الجذر للتطبيق. عندما يُطلب منك تفعيل region، اختَر us-central1، وعندما يُطلب منك السماح باستخدام unauthenticated invocations، اختَر Y. قد يُطلب منك أيضًا تفعيل واجهات Google Cloud APIs، مثل Artifact Registry وCloud Build وCloud Run، والحصول على إذن لإنشاء مستودع Artifact Registry. يُرجى المتابعة ومنح الإذن.

سيستغرق اكتمال عملية النشر حوالي دقيقتين، لذا يُرجى الانتظار.

وبعد النشر بنجاح، سيظهر لك عنوان URL لخدمة تشغيل السحابة الإلكترونية. انتقِل إلى عنوان URL المتاح للجميع ومن المفترض أن يظهر تطبيق الويب نفسه الذي تم نشره وتشغيله بنجاح.

c5322d0fd3e0f616.png

تهانينا، أحسنت!

15. (اختياري) استخدام التسجيل في السحابة الإلكترونية

يمكننا توفير إمكانية تسجيل الدخول إلى تطبيقنا بحيث تكون سجلات التطبيق مركزية في إحدى خدمات Google Cloud (تسجيل الدخول عبر السحابة الإلكترونية). يمكننا بعد ذلك استخدام ميزة "المراقبة من Gemini" لفهم الإدخالات في السجلّ أيضًا.

لإجراء ذلك، علينا أولاً استخدام مكتبة Python Cloud Logging من Google Cloud لتسجيل المعلومات أو التحذير أو رسائل الخطأ (بناءً على مستوى السجلّ / مستوى الخطورة).

دعونا نحاول أن نسأل ذلك أولًا إلى Code Assist. جرِّب الطلب التالي:

How do I use the google-cloud-logging package in Python?

من المفترض أن يصلك ردّ يتضمّن بعض المعلومات عنها، كما هو موضّح أدناه:

2472e1ccaf8a217d.png

لنُضف عبارات التسجيل إلى الدالة التي تبحث عن الجلسات حسب الفئة.

أولاً، أضِف حزمة Python google-cloud-logging إلى ملف requirements.txt.

وفيما يلي مقتطف من التعليمة البرمجية يوضح كيف دمجنا التعليمة البرمجية لتنفيذ التسجيل:

...
from google.cloud import logging
...
app = Flask(__name__)

# Create a logger
logger = logging.Client().logger('my-log')

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   logger.log_text(f"Fetching sessions with category {category}")
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   logger.log_text(f'Found {len(sessions_by_category)} sessions with category {category}')
   return jsonify(sessions_by_category)

# # Other App Routes

انشر الخدمة في Cloud Run مرة أخرى باستخدام الأمر نفسه الوارد في القسم السابق، وبعد نشرها، نفِّذ بعض الاتصالات بنقطة النهاية /sessions/categories/<category>.

انتقِل إلى Cloud Console → Logs Explorer.

59e297577570695.png

...ويجب أن تتمكن من الفلترة في عبارات التسجيل هذه كما هو موضّح أدناه:

914f1fb6cac30a89.png

يمكنك النقر على أيّ من عبارات السجلّ وتوسيعها ثم النقر على Explain this log entry الذي سيستخدم Gemini لتوضيح إدخال السجلّ. يُرجى العِلم أنّه في حال عدم تفعيل "Gemini في Google Cloud"، سيُطلب منك تفعيل Cloud AI Companion API. يُرجى المتابعة وتنفيذ ذلك وفقًا للتعليمات.

في ما يلي نموذج للرد:

7fc9783910fa92cc.png

16. تهانينا

تهانينا، لقد نجحت في إنشاء تطبيق من البداية واستخدمت Gemini Code Assist في جوانب متعددة من SDLC، بما في ذلك التصميم والإنشاء والاختبار والنشر.

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

اطّلع على بعض هذه الدروس التطبيقية حول الترميز...

المستندات المرجعية