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

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

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

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

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

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

הפעלה של Classroom API

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

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

כפי שהסברנו קודם, מערכת Google Classroom מעבירה פרמטרים של שאילתה כשטוענים את תצוגת Discovery של הקבצים המצורפים ב-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 של תצוגת התלמיד/ה.

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

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