هذه هي الجولةالرابعة في سلسلة جولاتنا الإرشادية حولإضافات Classroom.
في هذه الجولة الإرشادية، ستتفاعل مع Google Classroom API لإنشاء مرفقات. يمكنك توفير طرق للمستخدمين لعرض محتوى المرفق. تختلف طرق العرض حسب دور المستخدم في الصف. تتناول هذه الجولة التوضيحية المرفقات التي تندرج ضمن نوع المحتوى، والتي لا تتطلّب إرسال الطلاب لها.
خلال هذه الجولة الإرشادية، يمكنك إكمال ما يلي:
- استرداد مَعلمات طلب البحث الإضافية التالية واستخدامها:
addOnToken
: تم تمرير رمز تفويض مميز إلى طريقة عرض "اكتشاف المرفقات".itemId
: معرّف فريد لـ CourseWork أو CourseWorkMaterial أو الإعلان الذي يتلقى مرفق الإضافة.itemType
: إما "courseWork" أو "courseWorkMaterials" أو "announcement".-
courseId
: معرّف فريد لدورة Google Classroom التي يتم إنشاء المهمة فيها attachmentId
: هو معرّف فريد يعيّنه Google Classroom لمرفق إضافة بعد الإنشاء.
- تنفيذ سعة تخزين دائمة للمرفقات التي تندرج ضمن نوع المحتوى
- يجب توفير مسارات لإنشاء المرفقات وعرض إطارَي iframe لـ "عرض المعلّم" و"عرض الطالب".
- إرسال الطلبات التالية إلى واجهة برمجة تطبيقات إضافات Google Classroom:
- أنشئ مرفقًا جديدًا.
- الحصول على سياق الإضافة الذي يحدِّد ما إذا كان المستخدم الذي سجّل الدخول هو طالب أو معلّم
بعد الانتهاء، يمكنك إنشاء مرفقات من نوع المحتوى في المهام الدراسية من خلال واجهة مستخدم Google Classroom عند تسجيل الدخول كمعلّم. يمكن للمعلّمين والطلاب في الصف الدراسي أيضًا الاطّلاع على المحتوى.
تفعيل Classroom API
يمكنك إجراء طلبات بيانات من واجهة برمجة تطبيقات Classroom بدءًا من هذه الخطوة. يجب تفعيل واجهة برمجة التطبيقات لمشروعك على Google Cloud قبل أن تتمكّن من إجراء مكالمات إليها. انتقِل إلى إدخال مكتبة Google Classroom API واختَر تفعيل.
التعامل مع مَعلمات طلب البحث لعرض استكشاف المرفقات
كما سبق أن ناقشنا، تُرسِل Google Classroom مَعلمات طلب البحث عندتحميل "عرض استكشاف المرفقات" في إطار iframe:
courseId
: رقم تعريف الدورة التدريبية الحالية في Classroom.itemId
: معرّف فريد لمادة CourseWork أو CourseWorkMaterial أو Announcement التي تتلقّى مرفق الإضافة.itemType
: إما "courseWork" أو "courseWorkMaterials" أو "إعلان"addOnToken
: هو رمز مميّز يُستخدم لتفويض إجراءات معيّنة لإضافة Classroom.login_hint
: رقم تعريف Google للمستخدم الحالي
تتناول هذه الجولة الإرشادية courseId
وitemId
وitemType
وaddOnToken
.
الاحتفاظ بهذه الرموز وتمريرها عند إصدار الطلبات في Classroom API.
وكما في الخطوة التفصيلية السابقة، خزِّن قيم مَعلمات طلب البحث التي تم تمريرها في جلستنا. من المهم إجراء ذلك عند فتح طريقة عرض "اكتشاف المرفقات" لأول مرة، لأن هذه هي الفرصة الوحيدة المتاحة لـ Classroom لتمرير معلَمات طلب البحث هذه.
Python
انتقِل إلى ملف خادم Flask الذي يقدّم مسارات لعرض "استكشاف" عند إرفاق ملف (attachment-discovery-routes.py
إذا كنت تتّبع المثال الذي قيّدناه). في أعلى مسار الإحالة الناجحة الإضافية
(/classroom-addon
في المثال المقدَّم)، استرجع معلمات طلب البحث
courseId
وitemId
وitemType
وaddOnToken
واحفظها.
# Retrieve the itemId, courseId, and addOnToken query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("addOnToken"):
flask.session["addOnToken"] = flask.request.args.get("addOnToken")
اكتب هذه القيم في الجلسة فقط إذا كانت موجودة؛ ولن يتم تمريرها مرة أخرى إذا حدث وعاد المستخدم إلى طريقة عرض اكتشاف المرفقات في وقت لاحق بدون إغلاق iframe.
إضافة مساحة تخزين دائمة للمرفقات من نوع المحتوى
تحتاج إلى سجلّ على الجهاز لأي مرفقات تم إنشاؤها. يتيح لك ذلك البحث عن المحتوى الذي اختاره المعلّم باستخدام المعرّفات التي يوفّرها Classroom.
إعداد مخطّط قاعدة بيانات Attachment
يعرض المثال الذي قدّمناه ملفًا مرفقًا يعرض صورة وتعليقًا توضيحيًا. يحتوي Attachment
على
السمات التالية:
attachment_id
: معرّف فريد لمرفق تم تعيينها من خلال Classroom وتم عرضها في الرد عند إنشاء مرفق.image_filename
: اسم الملف المحلي للصورة المطلوب عرضها.image_caption
: الشرح الذي سيتم عرضه مع الصورة
Python
توسيع نطاق تنفيذ SQLite وflask_sqlalchemy
من الخطوات السابقة
انتقِل إلى الملف الذي حدّدت فيه جدول "المستخدِم" (models.py
إذا كنت تتّبع المثال المقدَّم). أضِف ما يلي في أسفل
الملف أسفل فئة User
.
class Attachment(db.Model):
# The attachmentId is the unique identifier for the attachment.
attachment_id = db.Column(db.String(120), primary_key=True)
# The image filename to store.
image_filename = db.Column(db.String(120))
# The image caption to store.
image_caption = db.Column(db.String(120))
استورِد فئة "المرفق" الجديدة إلى ملف الخادم مع مسارات معالجة المرفق.
إعداد مسارات جديدة
ابدأ هذه الجولة التفصيلية بإعداد بعض الصفحات الجديدة في تطبيقنا. تتيح هذه الوظائف للمستخدم إنشاء المحتوى وعرضه من خلال الإضافة.
إضافة مسارات إنشاء المرفقات
يجب أن تتضمّن صفحات تتيح للمعلّم اختيار المحتوى وإصدار طلبات إنشاء المرفقات. نفِّذ المسار /attachment-options
لعرض خيارات المحتوى
ليتمكّن المعلّم من الاختيار. تحتاج أيضًا إلى قوالب لصفحات اختيار
المحتوى وتأكيد الإنشاء. وتحتوي أمثلتنا المقدَّمة على نماذج لهذه العناصر،
ويمكنها أيضًا عرض الطلبات والردود من
Classroom API.
يمكنك بدلاً من ذلك تعديل الصفحة المقصودة الحالية لعرض استكشاف المرفقات لعرض خيارات المحتوى بدلاً من إنشاء صفحة /attachment-options
الجديدة. نقترح عليك إنشاء صفحة جديدة لأغراض هذا التمرين لكي تحافظ على سلوك خدمة "الدخول الموحّد" (SSO) الذي تم تنفيذه في الخطوة الثانية، مثل إبطال أذونات التطبيق. من المفترض أن تكون هذه الخطوات مفيدة عند إنشاء الإضافة واختبارها.
يمكن للمعلم الاختيار من بين مجموعة صغيرة من الصور التي تتضمن ترجمة في المثال المقدم. لقد قدمنا أربع صور للمعالم الشهيرة التي يتم اشتقاق تسمياتها من أسماء الملفات.
Python
في المثال الذي قدمناه، تجد ذلك في ملف webapp/attachment_routes.py
.
@app.route("/attachment-options", methods=["GET", "POST"])
def attachment_options():
"""
Render the attachment options page from the "attachment-options.html"
template.
This page displays a grid of images that the user can select using
checkboxes.
"""
# A list of the filenames in the static/images directory.
image_filenames = os.listdir(os.path.join(app.static_folder, "images"))
# The image_list_form_builder method creates a form that displays a grid
# of images, checkboxes, and captions with a Submit button. All images
# passed in image_filenames will be shown, and the captions will be the
# title-cased filenames.
# The form must be built dynamically due to limitations in WTForms. The
# image_list_form_builder method therefore also returns a list of
# attribute names in the form, which will be used by the HTML template
# to properly render the form.
form, var_names = image_list_form_builder(image_filenames)
# If the form was submitted, validate the input and create the attachments.
if form.validate_on_submit():
# Build a dictionary that maps image filenames to captions.
# There will be one dictionary entry per selected item in the form.
filename_caption_pairs = construct_filename_caption_dictionary_list(
form)
# Check that the user selected at least one image, then proceed to
# make requests to the Classroom API.
if len(filename_caption_pairs) > 0:
return create_attachments(filename_caption_pairs)
else:
return flask.render_template(
"create-attachment.html",
message="You didn't select any images.",
form=form,
var_names=var_names)
return flask.render_template(
"attachment-options.html",
message=("You've reached the attachment options page. "
"Select one or more images and click 'Create Attachment'."),
form=form,
var_names=var_names,
)
ينتج عن ذلك صفحة "إنشاء مرفقات" تشبه ما يلي:
يمكن للمعلّم اختيار صور متعدّدة. أنشئ مرفقًا واحدًا لكل صورة
اختارها المعلّم في طريقة create_attachments
.
طلبات إنشاء مرفقات المشاكل
الآن بعد أن عرفت أجزاء المحتوى التي يريد المعلّم إرفاقها، أرسِل طلبات إلى Classroom API لإنشاء مرفقات في المَهمّة. تخزين تفاصيل المرفق في قاعدة البيانات بعد تلقّي استجابة من Classroom API
ابدأ بالحصول على مثيل لخدمة Classroom:
Python
في المثال الذي قدمناه، تجد ذلك في ملف webapp/attachment_routes.py
.
def create_attachments(filename_caption_pairs):
"""
Create attachments and show an acknowledgement page.
Args:
filename_caption_pairs: A dictionary that maps image filenames to
captions.
"""
# Get the Google Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom",
version="v1",
credentials=credentials)
أرسِل طلب CREATE
إلى نقطة نهاية courses.courseWork.addOnAttachments
. لكل صورة يحددها المعلّم، عليك أولاً إنشاء
كائن AddOnAttachment
:
Python
في المثال الذي قدّمناه، هذا هو استمرار لطريقة create_attachments
.
# Create a new attachment for each image that was selected.
attachment_count = 0
for key, value in filename_caption_pairs.items():
attachment_count += 1
# Create a dictionary with values for the AddOnAttachment object fields.
attachment = {
# Specifies the route for a teacher user.
"teacherViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True),
},
# Specifies the route for a student user.
"studentViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True)
},
# The title of the attachment.
"title": f"Attachment {attachment_count}",
}
يجب تحديد الحقول teacherViewUri
وstudentViewUri
وtitle
على الأقل لكل مرفق. يمثّل teacherViewUri
وstudentViewUri
عناوين URL التي يتم تحميلها عند فتح المرفق من قِبل نوع المستخدِم المعنيّ.
أرسِل عنصر AddOnAttachment
في نص الطلب إلى نقطة نهاية
addOnAttachments
المناسبة. وعليك تقديم المعرّفات courseId
وitemId
وitemType
وaddOnToken
مع كل طلب.
Python
في المثال الذي قدّمناه، هذا هو استمرار لطريقة create_attachments
.
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
# Issue a request to create the attachment.
resp = parent.addOnAttachments().create(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"],
addOnToken=flask.session["addOnToken"],
body=attachment).execute()
أنشئ إدخالًا لهذا المرفق في قاعدة البيانات المحلية حتى تتمكّن من
تحميل المحتوى الصحيح لاحقًا. يعرض Classroom قيمة id
فريدة في الردّ على طلب الإنشاء، لذا استخدِمها كمفتاح أساسي في قاعدة البيانات. يُرجى العلم أيضًا أنّ Classroom يُرسل مَعلمة الطلب attachmentId
عند فتح "عرض المعلّم" و"عرض الطالب":
Python
في المثال الذي قدّمناه، هذا هو استمرار لطريقة create_attachments
.
# Store the value by id.
new_attachment = Attachment(
# The new attachment's unique ID, returned in the CREATE response.
attachment_id=resp.get("id"),
image_filename=key,
image_caption=value)
db.session.add(new_attachment)
db.session.commit()
يمكنك توجيه المستخدم إلى صفحة تأكيد في هذه المرحلة، للإقرار بأنه قد أنشأ المرفقات بنجاح.
السماح بالمرفقات من الإضافة
حان الوقت الآن لإضافة أي عناوين مناسبة إلى الحقل "بادئات URI للمرفقات المسموح بها" في صفحة إعداد التطبيق ضمن حزمة Google Workspace Marketplace SDK. لا يمكن لأداة الإضافة إنشاء مرفقات إلا من إحدى بادئات معرّفات الموارد المنتظمة المدرَجة في هذه الصفحة. وهذا إجراء أمني للمساعدة في تقليل احتمالية هجمات الوسيط.
إنّ أبسط طريقة هي تقديم نطاقك من المستوى الأعلى في هذا الحقل، مثلاً https://example.com
. https://localhost:<your port number>/
لن يعمل
إذا كنت تستخدم جهازك المحلي لخادم الويب.
إضافة مسارات لعرض "المعلم" و"الطالب"
هناك أربعة إطارات iframe يمكن تحميل إضافة Google Classroom فيها. لقد أنشأت حتى الآن مسارات تعرض فقط إطار iframe لعرض المرفقات أثناء التصفّح. بعد ذلك، أضِف مسارات لعرض إطارات iframe الخاصة بعرض المعلّم والطالب أيضًا.
يجب استخدام عنصر iframe في عرض المعلّم لعرض معاينة لتجربة الطالب، ولكن يمكن اختياريًا تضمين معلومات إضافية أو ميزات تعديل.
عرض الطالب هو الصفحة التي تظهر لكل طالب عند فتحه مرفق إضافة.
لأغراض هذا التمرين، أنشئ /load-content-attachment
مسارًا واحدًا يعرض "عرض المعلّم" و"عرض الطالب". استخدِم مثيلَي واجهة برمجة التطبيقات Classroom API
لتحديد ما إذا كان المستخدم معلّمًا أو طالبًا عند loading
الصفحة.
Python
في المثال الذي قدّمناه، يمكنك العثور على هذا العنصر في ملف webapp/attachment_routes.py
.
@app.route("/load-content-attachment")
def load_content_attachment():
"""
Load the attachment for the user's role."""
# Since this is a landing page for the Teacher and Student View iframes, we
# need to preserve the incoming query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("attachmentId"):
flask.session["attachmentId"] = flask.request.args.get("attachmentId")
تذكَّر أنّه عليك أيضًا مصادقة المستخدم في هذه المرحلة. عليك أيضًا التعامل مع مَعلمة طلب البحث login_hint
هنا، وتوجيه المستخدم إلى تدفق التفويض إذا لزم الأمر. اطّلِع على تفاصيل إرشادات تسجيل الدخول التي تمت مناقشتها
في الخطوات السابقة للحصول على مزيد من المعلومات حول هذه العملية.
بعد ذلك، يمكنك إرسال طلب إلى نقطة النهاية getAddOnContext
التي تتطابق مع نوع العنصر.
Python
في المثال الذي قدّمناه، هذه مواصلة للطريقة load_content_attachment
.
# Create an instance of the Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom"
version="v1",
credentials=credentials)
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
addon_context_response = parent.getAddOnContext(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"]).execute()
تعرض هذه الطريقة معلومات عن دور المستخدم الحالي في الصف.
ويمكنك تغيير طريقة العرض المعروضة للمستخدم حسب دوره. يتمّ تعبئة حقل واحد فقط من الحقلين
studentContext
أو teacherContext
في عنصر
الردّ. راجِع هذه المراجع لتحديد كيفية مخاطبة المستخدم.
في أي حال، استخدِم قيمة مَعلمة طلب البحث attachmentId
لمعرفة المرفق الذي تريد استرجاعه من قاعدة بياناتنا. يتم توفير معلمة طلب البحث هذه عند فتح
معرفات الموارد المنتظمة (URI) لعرض المعلم أو الطالب.
Python
في المثال الذي قدّمناه، هذا هو استمرار لطريقة
load_content_attachment
.
# Determine which view we are in by testing the returned context type.
user_context = "student" if addon_context_response.get(
"studentContext") else "teacher"
# Look up the attachment in the database.
attachment = Attachment.query.get(flask.session["attachmentId"])
# Set the text for the next page depending on the user's role.
message_str = f"I see that you're a {user_context}! "
message_str += (
f"I've loaded the attachment with ID {attachment.attachment_id}. "
if user_context == "teacher" else
"Please enjoy this image of a famous landmark!")
# Show the content with the customized message text.
return flask.render_template(
"show-content-attachment.html",
message=message_str,
image_filename=attachment.image_filename,
image_caption=attachment.image_caption,
responses=response_strings)
اختبار الإضافة
أكمِل الخطوات التالية لاختبار إنشاء المرفق:
- سجِّل الدخول إلى [Google Classroom] بصفتك أحد المستخدمين الاختباريين المعلّمين.
- انتقِل إلى علامة التبويب الواجب الدراسي وأنشئ مهمة جديدة.
- انقر على الزر الإضافات أسفل مربّع النص، ثم اختَر الإضافة التي تريدها. يتم فتح إطار iframe، وتحمِّل الإضافة معرّف الموارد المنتظم (URI) لإعداد مُرفَقات الذي حدّدته في صفحة إعداد التطبيق ضمن حزمة تطوير البرامج (SDK) في Google Workspace Marketplace.
- اختَر قطعة محتوى لإرفاقها بالمهمة.
- أغلِق إطار iframe بعد اكتمال عملية إنشاء المرفق.
من المفترض أن تظهر بطاقة مرفق في واجهة مستخدم إنشاء المهام في Google Google Classroom. انقر على البطاقة لفتح إطار iframe في "طريقة عرض المعلّمين" والتأكّد من ظهور المرفق الصحيح. انقر على الزر تعيين.
أكمِل الخطوات التالية لاختبار تجربة الطالب:
- بعد ذلك، سجِّل الدخول إلى Classroom كمستخدم اختباري للطلاب في الصف نفسه الذي ينتمي إليه مستخدم اختبار المعلّم.
- ابحث عن المهمة الدراسية في علامة التبويب "الواجب الدراسي".
- وسِّع المهمة الدراسية وانقر على بطاقة المرفق لفتح ملف iframe في "عرض الطالب".
تأكَّد من ظهور المرفق الصحيح للطالب.
تهانينا! يمكنك الآن الانتقال إلى الخطوة التالية: إنشاء ملفّات مرفقة من نوع "نشاط".