Inhaltstyp-Anhänge

Dies ist der vierte Walkthrough in der Reihe zu Classroom-Add-ons.

In dieser Anleitung interagieren Sie mit der Google Classroom API, um Anhänge zu erstellen. Sie stellen Routen bereit, über die Nutzer die Inhalte des Anhangs aufrufen können. Die Ansichten unterscheiden sich je nach Rolle des Nutzers im Kurs. In dieser Anleitung geht es um Anhänge vom Typ „content-type“, für die keine Einreichung durch den Schüler oder Studenten erforderlich ist.

In dieser Anleitung führen Sie die folgenden Schritte aus:

  • Rufen Sie die folgenden Add-on-Abfrageparameter ab und verwenden Sie sie:
    • addOnToken: Ein Autorisierungstoken, das an die Ansicht „Attachment Discovery“ übergeben wird.
    • itemId: Eine eindeutige Kennung für die CourseWork, CourseWorkMaterial oder Announcement, die den Add-on-Anhang erhält.
    • itemType: Entweder „courseWork“, „courseWorkMaterials“ oder „announcement“.
    • courseId: Eine eindeutige Kennung für den Google Classroom-Kurs, in dem die Aufgabe erstellt wird.
    • attachmentId: Eine eindeutige Kennung, die von Google Classroom nach der Erstellung einem Add-on-Anhang zugewiesen wird.
  • Implementieren Sie nichtflüchtigen Speicher für Anhänge vom Typ „content“.
  • Stellen Sie Routen zum Erstellen von Anhängen und zum Bereitstellen der iFrames für die Ansicht für Lehrkräfte und die Ansicht für Lernende bereit.
  • Senden Sie die folgenden Anfragen an die Google Classroom-Add-ons API:
    • Neuen Anhang erstellen
    • Rufen Sie den Add‑on-Kontext ab, der angibt, ob der angemeldete Nutzer ein Schüler/Student oder eine Lehrkraft ist.

Danach können Sie Aufgaben über die Google Classroom-Benutzeroberfläche Inhaltsanhänge hinzufügen, wenn Sie als Lehrkraft angemeldet sind. Lehrkräfte und Schüler/Studenten im Kurs können sich die Inhalte ebenfalls ansehen.

Classroom API aktivieren

Ab diesem Schritt können Sie Aufrufe an die Classroom API senden. Die API muss für Ihr Google Cloud-Projekt aktiviert sein, bevor Sie sie aufrufen können. Rufen Sie den Bibliothekseintrag für die Google Classroom API auf und wählen Sie Aktivieren aus.

Suchparameter für die Ansicht „Anhangsuche“ verarbeiten

Wie bereits erwähnt, übergibt Google Classroom beim Laden der Ansicht zur Suche nach Anhängen im iFrame Abfrageparameter:

  • courseId: Die ID des aktuellen Classroom-Kurses.
  • itemId: Eine eindeutige Kennung für die CourseWork, CourseWorkMaterial oder Announcement, die den Add-on-Anhang erhält.
  • itemType: Entweder „courseWork“, „courseWorkMaterials“ oder „announcement“.
  • addOnToken: Ein Token, das zur Autorisierung bestimmter Aktionen von Classroom-Add-ons verwendet wird.
  • login_hint: Die Google-ID des aktuellen Nutzers.

In dieser Anleitung werden courseId, itemId, itemType und addOnToken behandelt. Behalten Sie diese bei und übergeben Sie sie, wenn Sie die Classroom API aufrufen.

Speichern Sie die übergebenen Suchparameterwerte wie im vorherigen Schritt in unserer Sitzung. Das ist wichtig, da dies die einzige Möglichkeit für Classroom ist, diese Abfrageparameter zu übergeben.

Python

Rufen Sie die Flask-Serverdatei auf, die Routen für die Ansicht „Anhangsuche“ bereitstellt (attachment-discovery-routes.py, wenn Sie unserem Beispiel folgen). Rufen Sie oben auf der Landingpage-Route Ihres Add-ons (/classroom-addon in unserem Beispiel) die Abfrageparameter courseId, itemId, itemType und addOnToken ab und speichern Sie sie.

# 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")

Schreiben Sie diese Werte nur dann in die Sitzung, wenn sie vorhanden sind. Sie werden nicht noch einmal übergeben, wenn der Nutzer später zur Ansicht „Anhangsuche“ zurückkehrt, ohne das iFrame zu schließen.

Nichtflüchtigen Speicher für Anhänge vom Typ „content-type“ hinzufügen

Sie benötigen eine lokale Aufzeichnung aller erstellten Anhänge. So können Sie die Inhalte, die die Lehrkraft ausgewählt hat, anhand von Kennungen, die von Classroom bereitgestellt werden, nachschlagen.

Richten Sie ein Datenbankschema für ein Attachment ein. In unserem Beispiel werden Anhänge mit einem Bild und einer Bildunterschrift dargestellt. Ein Attachment enthält die folgenden Attribute:

  • attachment_id: Eine eindeutige Kennung für einen Anhang. Wird von Classroom zugewiesen und in der Antwort zurückgegeben, wenn ein Anhang erstellt wird.
  • image_filename: Der lokale Dateiname des anzuzeigenden Bildes.
  • image_caption: Die Bildunterschrift, die mit dem Bild angezeigt werden soll.

Python

Erweitern Sie die SQLite- und flask_sqlalchemy-Implementierung aus den vorherigen Schritten.

Rufen Sie die Datei auf, in der Sie die Nutzertabelle definiert haben (models.py, wenn Sie unserem Beispiel folgen). Fügen Sie Folgendes am Ende der Datei unter der Klasse User hinzu.

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))

Importieren Sie die neue Attachment-Klasse in die Serverdatei mit Ihren Routen für die Verarbeitung von Anhängen.

Neue Routen einrichten

Richten Sie zuerst einige neue Seiten in der Anwendung ein. Damit können Nutzer Inhalte über unser Add-on erstellen und ansehen.

Routen zum Erstellen von Anhängen hinzufügen

Sie benötigen Seiten, damit die Lehrkraft Inhalte auswählen und Anfragen zum Erstellen von Anhängen für Aufgaben stellen kann. Implementieren Sie den /attachment-options-Pfad, um Inhaltsoptionen anzuzeigen, die die Lehrkraft auswählen kann. Außerdem benötigen Sie Vorlagen für die Seiten zur Auswahl von Inhalten und zur Bestätigung der Erstellung. Die von uns bereitgestellten Beispiele enthalten Vorlagen dafür und können auch die Anfragen und Antworten der Classroom API anzeigen.

Alternativ können Sie auch die Landingpage Ihrer bestehenden Ansicht „Anhangsuche“ ändern, um die Inhaltsoptionen anzuzeigen, anstatt die neue Seite /attachment-options zu erstellen. Wir empfehlen, für diese Übung eine neue Seite zu erstellen, damit das im zweiten Schritt der Anleitung implementierte SSO-Verhalten beibehalten wird, z. B. der Widerruf der App-Berechtigungen. Sie sollten beim Erstellen und Testen Ihres Add-ons hilfreich sein.

Lehrkräfte können aus einer kleinen Auswahl von Bildern mit Untertiteln in unserem Beispiel auswählen. Wir haben vier Bilder von berühmten Sehenswürdigkeiten bereitgestellt, deren Bildunterschriften aus den Dateinamen abgeleitet wurden.

Python

In unserem Beispiel befindet sich diese in der Datei 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,
    )

Dadurch wird eine Seite „Anhänge erstellen“ generiert, die so aussieht:

Ansicht zur Auswahl von Python-Beispielinhalten

Die Lehrkraft kann mehrere Bilder auswählen. Erstellen Sie für jedes Bild, das der Lehrer in der Methode create_attachments ausgewählt hat, einen Anhang.

Anfragen zum Erstellen von Anhängen stellen

Nachdem Sie wissen, welche Inhalte der Lehrer anhängen möchte, senden Sie Anfragen an die Classroom API, um Anhänge für die Aufgabe zu erstellen. Speichern Sie die Details des Anhangs in Ihrer Datenbank, nachdem Sie eine Antwort von der Classroom API erhalten haben.

Rufen Sie zuerst eine Instanz des Classroom-Dienstes ab:

Python

In unserem Beispiel befindet sich diese in der Datei 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)

Senden Sie eine CREATE-Anfrage an den Endpunkt courses.courseWork.addOnAttachments. Erstellen Sie für jedes vom Kursleiter ausgewählte Bild zuerst ein AddOnAttachment-Objekt:

Python

In unserem Beispiel ist das eine Fortsetzung der Methode 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}",
    }

Für jeden Anhang müssen mindestens die Felder teacherViewUri, studentViewUri und title angegeben werden. teacherViewUri und studentViewUri stehen für die URLs, die geladen werden, wenn der Anhang vom jeweiligen Nutzertyp geöffnet wird.

Senden Sie das AddOnAttachment-Objekt im Text einer Anfrage an den entsprechenden addOnAttachments-Endpunkt. Geben Sie die Kennungen courseId, itemId, itemType und addOnToken bei jeder Anfrage an.

Python

In unserem Beispiel ist das eine Fortsetzung der Methode 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()

Erstellen Sie einen Eintrag für diesen Anhang in Ihrer lokalen Datenbank, damit Sie später die richtigen Inhalte laden können. Classroom gibt in der Antwort auf die Erstellungsanfrage einen eindeutigen id-Wert zurück. Verwenden Sie diesen als Primärschlüssel in Ihrer Datenbank. Beachten Sie außerdem, dass Classroom den Abfrageparameter attachmentId beim Öffnen der Ansichten für Lehrkräfte und Schüler/Studenten übergibt:

Python

In unserem Beispiel ist das eine Fortsetzung der Methode 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()

Leiten Sie den Nutzer an dieser Stelle auf eine Bestätigungsseite weiter, auf der bestätigt wird, dass er Anhänge erfolgreich erstellt hat.

Anhänge aus Ihrem Add‑on zulassen

Jetzt ist ein guter Zeitpunkt, um alle entsprechenden Adressen auf der Seite App-Konfiguration im Google Workspace Marketplace SDK in das Feld „Zulässige URI-Präfixe für Anhänge“ einzufügen. Ihr Add‑on kann nur Anhänge mit einem der auf dieser Seite aufgeführten URI-Präfixe erstellen. Dies ist eine Sicherheitsmaßnahme, um das Risiko von Man-in-the-Middle-Angriffen zu verringern.

Am einfachsten geben Sie in diesem Feld Ihre Top-Level-Domain an, z. B. https://example.com. https://localhost:<your port number>/ würde funktionieren, wenn Sie Ihren lokalen Computer als Webserver verwenden.

Routen für die Ansichten für Lehrkräfte und Schüler/Studenten hinzufügen

Es gibt vier iFrames, in denen ein Google Classroom-Add-on geladen werden kann. Bisher haben Sie nur Routen erstellt, die das iFrame für die Ansicht „Anhangsuche“ bedienen. Fügen Sie als Nächstes Routen hinzu, um auch die iFrames für die Ansicht für Lehrkräfte und Schüler bereitzustellen.

Der Teacher View-iFrame ist erforderlich, um eine Vorschau der Schüleransicht zu zeigen. Er kann optional zusätzliche Informationen oder Bearbeitungsfunktionen enthalten.

Die Schüleransicht ist die Seite, die jedem Schüler angezeigt wird, wenn er einen Add-on-Anhang öffnet.

Erstellen Sie für diese Übung eine einzelne /load-content-attachment-Route, die sowohl die Ansicht für Lehrkräfte als auch die Ansicht für Schüler und Studenten bereitstellt. Verwenden Sie Classroom API-Methoden, um beim Laden der Seite zu ermitteln, ob der Nutzer eine Lehrkraft oder ein Schüler/Student ist.

Python

In unserem Beispiel befindet sich diese in der Datei 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")

Denken Sie daran, dass Sie den Nutzer an dieser Stelle auch authentifizieren sollten. Hier sollten Sie auch den Abfrageparameter login_hint verarbeiten und den Nutzer bei Bedarf zu Ihrem Autorisierungsablauf weiterleiten. Weitere Informationen zu diesem Ablauf finden Sie in der Anleitung zur Anmeldung, die in den vorherigen Anleitungen beschrieben wird.

Senden Sie dann eine Anfrage an den getAddOnContext-Endpunkt, der dem Elementtyp entspricht.

Python

In unserem Beispiel ist das eine Fortsetzung der Methode 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()

Diese Methode gibt Informationen zur Rolle des aktuellen Nutzers in der Klasse zurück. Die Ansicht für den Nutzer je nach seiner Rolle ändern. Genau eines der Felder studentContext oder teacherContext wird im Antwortobjekt ausgefüllt. Anhand dieser Informationen können Sie entscheiden, wie Sie den Nutzer ansprechen.

Verwenden Sie in jedem Fall den Wert des Abfrageparameters attachmentId, um zu ermitteln, welcher Anhang aus unserer Datenbank abgerufen werden soll. Dieser Abfrageparameter wird beim Öffnen der Lehrer- oder Schüleransicht-URIs angegeben.

Python

In unserem Beispiel ist das eine Fortsetzung der Methode 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)

Add-on testen

Führen Sie die folgenden Schritte aus, um das Erstellen von Anhängen zu testen:

  • Melden Sie sich in [Google Classroom] als einer Ihrer Lehrkraft-Testnutzer an.
  • Gehen Sie zum Tab Kursaufgaben und erstellen Sie eine neue Aufgabe.
  • Klicken Sie unter dem Textbereich auf die Schaltfläche Add-ons und wählen Sie das gewünschte Add-on aus. Der iFrame wird geöffnet und das Add‑on lädt den URI für die Einrichtung von Anhängen, den Sie im Google Workspace Marketplace SDK auf der Seite App-Konfiguration angegeben haben.
  • Wählen Sie einen Inhalt aus, den Sie an die Aufgabe anhängen möchten.
  • Schließen Sie das iFrame, nachdem der Vorgang zum Erstellen des Anhangs abgeschlossen ist.

In der Benutzeroberfläche zum Erstellen von Aufgaben in Google Classroom sollte eine Karte mit einem Anhang angezeigt werden. Klicken Sie auf die Karte, um den Teacher View-iFrame zu öffnen, und prüfen Sie, ob der richtige Anhang angezeigt wird. Klicken Sie auf die Schaltfläche Zuweisen.

So testen Sie die Schüler- und Studentenansicht:

  • Melden Sie sich dann als Testnutzer mit Schülerkonto in Classroom an. Er muss denselben Kurs wie der Testnutzer mit Lehrkraftkonto haben.
  • Sie finden die Testaufgabe auf dem Tab „Kursaufgaben“.
  • Maximieren Sie die Aufgabe und klicken Sie auf die Karte mit dem Anhang, um den iFrame „Schüler-/Studentenansicht“ zu öffnen.

Prüfen Sie, ob der richtige Anhang für den Schüler/Studenten angezeigt wird.

Glückwunsch! Sie können mit dem nächsten Schritt fortfahren: Anhänge für Aktivitätstypen erstellen.