هذا هو الجزء الرابع من سلسلة الجولات الإرشادية حول إضافات Classroom.
في هذا الشرح، يمكنك التفاعل مع Google Classroom API لإنشاء مرفقات. عليك توفير مسارات للمستخدمين لعرض محتوى المرفق. وتختلف طرق العرض حسب دور المستخدم في الصف. تتضمّن هذه الجولة الإرشادية مرفقات من نوع content-type لا تتطلّب إرسال الطالب.
خلال هذا الشرح التفصيلي، ستكمل ما يلي:
- استرداد مَعلمات طلب البحث الإضافية التالية واستخدامها:
addOnToken
: رمز تفويض يتم تمريره إلى "عرض اكتشاف المرفقات".-
itemId
: معرّف فريد لـ CourseWork أو CourseWorkMaterial أو Announcement الذي يتلقّى مرفق الإضافة. itemType
: إما "courseWork" أو "courseWorkMaterials" أو "announcement"-
courseId
: معرّف فريد لدورة Google Classroom التدريبية التي يتم إنشاء المهمة فيها. attachmentId
: معرّف فريد يخصّصه Google Classroom لمرفق إضافة بعد إنشائه.
- تنفيذ مساحة تخزين دائمة للمرفقات الخاصة بنوع المحتوى
- توفير مسارات لإنشاء المرفقات وعرض إطارات iframe الخاصة بـ "عرض المعلّم" و"عرض الطالب"
- أرسِل الطلبات التالية إلى واجهة برمجة التطبيقات Google Classroom Add-ons API:
- إنشاء مرفق جديد
- الحصول على سياق الإضافة الذي يحدّد ما إذا كان المستخدم الذي سجّل الدخول طالبًا أو معلّمًا
بعد الانتهاء، يمكنك إنشاء مرفقات من نوع المحتوى في المهام من خلال واجهة مستخدم Google Classroom عند تسجيل الدخول بصفتك معلّمًا. يمكن أيضًا للمعلّمين والطلاب في الصف الاطّلاع على المحتوى.
تفعيل Classroom API
يمكنك إجراء طلبات إلى Classroom API بدءًا من هذه الخطوة. يجب تفعيل واجهة برمجة التطبيقات لمشروعك على Google Cloud قبل أن تتمكّن من طلب بيانات منها. انتقِل إلى إدخال المكتبة في Google Classroom API واختَر تفعيل.
التعامل مع مَعلمات طلب البحث في "عرض اكتشاف المرفقات"
كما ذكرنا سابقًا، يمرِّر Google Classroom مَعلمات طلب البحث عند تحميل "عرض اكتشاف المرفقات" في الإطار iframe:
courseId
: معرّف دورة Classroom التدريبية الحالية.-
itemId
: معرّف فريد لـ CourseWork أو CourseWorkMaterial أو Announcement الذي يتلقّى مرفق الإضافة. itemType
: إما "courseWork" أو "courseWorkMaterials" أو "announcement"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
جديدة. ننصحك بإنشاء صفحة جديدة لأغراض هذا التمرين حتى تتمكّن من الحفاظ على سلوك تسجيل الدخول الموحّد الذي تم تنفيذه في خطوة الشرح الثانية، مثل إبطال أذونات التطبيق. من المفترض أن تكون هذه الموارد مفيدة أثناء إنشاء الإضافة واختبارها.
يمكن للمعلّم الاختيار من بين مجموعة صغيرة من الصور مع تعليقات توضيحية في المثال الذي نوفّره. قدّمنا أربع صور لمعالم شهيرة، وتم استخراج الشروح الخاصة بها من أسماء الملفات.
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) للمرفقات المسموح بها" في صفحة إعدادات التطبيق ضمن حزمة تطوير البرامج (SDK) في Google Workspace Marketplace. يمكن أن تنشئ الإضافة مرفقات من إحدى بادئات معرّف الموارد المنتظم (URI) المدرَجة في هذه الصفحة فقط. هذا الإجراء الأمني يساعد في الحد من إمكانية التعرّض لهجمات الوسيط.
أبسط طريقة هي تقديم نطاق المستوى الأعلى في هذا الحقل، مثل https://example.com
. سيعمل https://localhost:<your port number>/
إذا كنت تستخدم جهازك المحلي كخادم ويب.
إضافة مسارات لعرض "طريقة عرض المعلّم" و"طريقة عرض الطالب"
هناك أربع إطارات iframe يمكن تحميل إضافات Google Classroom فيها. لقد أنشأت حتى الآن مسارات تعرض إطار iframe الخاص بـ "عرض اكتشاف المرفقات" فقط. بعد ذلك، أضِف مسارات لعرض إطارات iframe الخاصة بـ "عرض المعلّم" و"عرض الطالب" أيضًا.
يجب تضمين إطار iframe الخاص بوضع "عرض المعلّم" لعرض معاينة لتجربة الطالب، ولكن يمكن تضمين معلومات إضافية أو ميزات تعديل بشكل اختياري.
عرض الطالب هو الصفحة التي تظهر لكل طالب عند فتح مرفق إضافة.
لأغراض هذا التمرين، أنشئ مسارًا واحدًا يعرض كلاً من "طريقة عرض المعلّم" و"طريقة عرض الطالب"./load-content-attachment
استخدِم طرق Classroom API لتحديد ما إذا كان المستخدم معلّمًا أو طالبًا عند تحميل الصفحة.
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 Classroom. انقر على البطاقة لفتح إطار iframe الخاص بـ "عرض المعلّم" وتأكَّد من ظهور المرفق الصحيح. انقر على الزر تعيين.
أكمِل الخطوات التالية لاختبار تجربة الطالب:
- بعد ذلك، سجِّل الدخول إلى Classroom بصفتك مستخدمًا تجريبيًا من الطلاب في الصف نفسه الذي يضم المستخدم التجريبي من المعلّمين.
- ابحث عن مهمة الاختبار في علامة التبويب "الواجب الدراسي".
- وسِّع المَهمّة الدراسية وانقر على بطاقة المرفق لفتح إطار iframe الخاص بـ "طريقة عرض الطالب".
تأكَّد من ظهور المرفق الصحيح للطالب.
تهانينا! أنت الآن جاهز للانتقال إلى الخطوة التالية: إنشاء مرفقات خاصة بنوع النشاط.