مرفقات نوع المحتوى

هذا هو رابع إرشاد تفصيلي في سلسلة الإرشادات التفصيلية حول إضافات Classroom.

في هذا الإرشاد التفصيلي، يمكنك التفاعل مع Google Classroom API لإنشاء المرفقات. يمكنك توفير مسارات للمستخدمين لعرض محتوى المرفق. تختلف طرق العرض استنادًا إلى دور المستخدم في الصف. يغطّي هذا الإرشاد التفصيلي المرفقات من نوع المحتوى، التي لا تتطلّب إرسال المحتوى من قِبل الطالب.

أثناء هذا الإرشاد التفصيلي، يمكنك إكمال ما يلي:

  • استرداد مَعلمات طلب البحث التالية للإضافة واستخدامها:
    • 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 من الخطوات السابقة.

انتقِل إلى الملف الذي حدّدت فيه جدول User (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 الجديدة إلى ملف الخادم الذي يحتوي على مسارات معالجة المرفقات.

إعداد مسارات جديدة

ابدأ خطوة الإرشاد التفصيلي هذه من خلال إعداد بعض الصفحات الجديدة في تطبيقنا. تتيح هذه الصفحات للمستخدم إنشاء المحتوى وعرضه من خلال الإضافة.

إضافة مسارات إنشاء المرفقات

تحتاج إلى صفحات ليختار المعلّم المحتوى ويرسل طلبات إنشاء المرفقات. نفِّذ المسار /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,
    )

ينتج عن ذلك صفحة "إنشاء المرفقات" تشبه ما يلي:

عرض اختيار محتوى مثال Python

يمكن للمعلّم اختيار صور متعددة. أنشِئ مرفقًا واحدًا لكل صورة اختارها المعلّم في طريقة 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 object:

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. لا يمكن للإضافة إنشاء مرفقات إلا من إحدى بادئات عناوين 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 في عنصر الردّ response object. افحص هذَين الحقلَين لتحديد كيفية مخاطبة المستخدم.

في أي حال، استخدِم قيمة مَعلمة طلب البحث 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 لإعداد المرفق الذي حدّدته في صفحة "إعدادات التطبيق" في Google Workspace Marketplace SDK.
  • اختَر جزءًا من المحتوى لإرفاقه بالواجب.
  • أغلِق إطار iframe بعد اكتمال عملية إنشاء المرفق.

من المفترض أن تظهر بطاقة مرفق في واجهة مستخدم إنشاء الواجب في Google Google Classroom. انقر على البطاقة لفتح إطار iframe الخاص بـ "عرض المعلّم" وتأكَّد من ظهور المرفق الصحيح. انقر على الزر تعيين.

أكمِل الخطوات التالية لاختبار تجربة الطالب:

  • بعد ذلك، سجِّل الدخول إلى Classroom بصفتك مستخدمًا تجريبيًا طالبًا في الصف نفسه الذي ينتمي إليه المستخدم التجريبي المعلّم.
  • ابحث عن الواجب التجريبي في علامة التبويب "الواجب الدراسي".
  • وسِّع نطاق الواجب وانقر على بطاقة المرفق لفتح إطار iframe الخاص بـ "عرض الطالب".

تأكَّد من ظهور المرفق الصحيح للطالب.

تهانينا! أنت على استعداد للانتقال إلى الخطوة التالية: إنشاء مرفقات من نوع النشاط.