קבצים מצורפים מסוג תוכן

זהו המדריך הרביעי בסדרת המדריכים בנושא תוספים ל-Classroom.

במדריך הזה תלמדו איך ליצור קבצים מצורפים באמצעות Google Classroom API. אתם מספקים למשתמשים מסלולים לצפייה בתוכן הקובץ המצורף. התצוגות משתנות בהתאם לתפקיד של המשתמש בכיתה. במדריך הזה נסביר איך לצרף קבצים מסוג תוכן, שלא דורשים מהתלמידים לשלוח אותם.

במהלך המדריך הזה תלמדו:

  • אחזור של פרמטרים הבאים של שאילתות התוספים ושימוש בהם:
    • addOnToken: אסימון הרשאה שהוענק לתצוגה 'גילוי קבצים מצורפים'.
    • itemId: מזהה ייחודי של העבודה, של חומר הלימוד או של ההודעה שבהם מצורף התוסף.
    • itemType: 'courseWork',‏ 'courseWorkMaterials' או 'announcement'.
    • courseId: מזהה ייחודי של הקורס ב-Google Classroom שבו נוצרת המטלה.
    • attachmentId: מזהה ייחודי שמערכת Google Classroom מקצה לקובץ מצורף של תוסף אחרי היצירה שלו.
  • הטמעת אחסון מתמיד לקובצי מצורף מסוג תוכן.
  • לספק מסלולים ליצירת קבצים מצורפים ולהצגת ה-iframe של תצוגת המורה ותצוגת התלמיד.
  • שולחים את הבקשות הבאות ל-Google Classroom Add-ons API:
    • יוצרים קובץ מצורף חדש.
    • אחזור ההקשר של התוסף, שמזהה אם המשתמש שמחובר הוא תלמיד או מורה.

בסיום, תוכלו ליצור קבצים מצורפים מסוג תוכן במטלות דרך ממשק המשתמש של Google Classroom כשאתם מחוברים כמורים. גם המורים והתלמידים בכיתה יכולים לראות את התוכן.

הפעלת Classroom API

מתחילים בשלב הזה לבצע קריאות ל-Classroom API. כדי שתוכלו לבצע קריאות ל-API, עליכם להפעיל אותו בפרויקט ב-Google Cloud. עוברים אל הרשומה בספרייה של Google Classroom API ובוחרים באפשרות Enable.

טיפול בפרמטרים של השאילתה בתצוגת גילוי הקבצים המצורפים

כפי שהסברנו קודם, Google Classroom מעביר פרמטרים של שאילתות בזמן טעינת תצוגת הגילוי של הקבצים המצורפים ב-iframe:

  • courseId: המזהה של הקורס הנוכחי ב-Classroom.
  • itemId: מזהה ייחודי של העבודה, של חומר הלימוד או של ההודעה שבהם מצורף התוסף.
  • 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 לקובץ השרת עם מסלולי הטיפול בקבצים המצורפים.

הגדרת מסלולים חדשים

בשלב הראשון של המדריך הזה, נגדיר כמה דפים חדשים באפליקציה. הם מאפשרים למשתמשים ליצור תוכן ולצפות בו באמצעות התוסף שלנו.

הוספת מסלולים ליצירת קבצים מצורפים

צריך ליצור דפים שבהם המורים יוכלו לבחור תוכן ולשלוח בקשות ליצירת קבצים מצורפים. מטמיעים את המסלול /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,
    )

הפעולה הזו תיצור את הדף 'יצירת קבצים מצורפים', שנראה כך:

תצוגת בחירת תוכן לדוגמה ב-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 לנקודת הקצה (endpoint) 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 בגוף הבקשה לנקודת הקצה (endpoint) המתאימה של 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()

בשלב הזה, כדאי להפנות את המשתמש לדף אישור, שבו הוא יוכל לאשר שהוא הצליח ליצור את הקבצים המצורפים.

איך מאפשרים לשלוח קבצים מצורפים מהתוסף

עכשיו זה הזמן להוסיף כתובות מתאימות לשדה Allowed Attachment URI Prefixes בדף App Configuration ב-SDK של Google Workspace Marketplace. התוסף יכול ליצור קבצים מצורפים רק מאחד מתחילית ה-URI שמפורטות בדף הזה. זהו אמצעי אבטחה שנועד לצמצם את האפשרות של מתקפות מסוג 'אדם בתווך'.

הגישה הפשוטה ביותר היא לציין בשדה הזה את הדומיין ברמה העליונה, לדוגמה https://example.com. https://localhost:<your port number>/ יפעל אם אתם משתמשים במכונה המקומית כשרת האינטרנט.

הוספת מסלולים לתצוגות של המורה והתלמיד

יש ארבעה פריטי iframe שבהם יכול להיות שתוצג טעינת תוסף של Google Classroom. עד כה יצרתם רק מסלולים שמוצג בהם ה-iframe של תצוגת הגילוי של הקובץ המצורף. בשלב הבא, מוסיפים מסלולים להצגת iframes של תצוגת המורה ותצוגת התלמיד/ה.

ה-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] בתור אחד ממשתמשי הבדיקה בתפקיד מורה.
  • עוברים לכרטיסייה עבודות ויוצרים מטלה חדשה.
  • לוחצים על הלחצן Add-ons (תוספים) מתחת לאזור הטקסט ובוחרים את התוסף הרצוי. ה-iframe נפתח והתוסף טוען את URI להגדרת הקובץ המצורף שציינתם בדף הגדרת האפליקציה של Google Workspace Marketplace SDK.
  • בוחרים קטע תוכן שרוצים לצרף למטלה.
  • סוגרים את ה-iframe אחרי שהתהליך ליצירת הקובץ המצורף מסתיים.

כרטיס של קובץ מצורף אמור להופיע בממשק המשתמש ליצירת מטלות ב-Google Classroom. לוחצים על הכרטיס כדי לפתוח את ה-iframe של תצוגת המורה ומוודאים שהקובץ המצורף הנכון מופיע. לוחצים על הלחצן הקצאה.

כדי לבדוק את חוויית הלמידה:

  • לאחר מכן, נכנסים ל-Classroom בתור משתמש בדיקה של תלמיד/ה באותה כיתה שבה נמצא משתמש הבדיקה של המורה.
  • מחפשים את המטלה של המבחן בכרטיסייה 'עבודות'.
  • מרחיבים את המטלה ולוחצים על כרטיס הקובץ המצורף כדי לפתוח את ה-iframe של תצוגת התלמיד/ה.

מוודאים שהקובץ המצורף הנכון מופיע אצל התלמיד/ה.

מעולה! עכשיו אפשר להמשיך לשלב הבא: יצירת קבצים מצורפים מסוג 'פעילות'.