Inhaltstyp-Anhänge

Dies ist die vierte Schritt-für-Schritt-Anleitung in der Reihe zu Classroom-Add-ons.

In dieser Anleitung interagieren Sie mit der Google Classroom API, um Anhänge zu erstellen. Sie stellen Nutzern Routen zum Ansehen des Inhalts des Anhangs zur Verfügung. Die Ansichten unterscheiden sich je nach Rolle des Nutzers im Kurs. In dieser Anleitung geht es um Anhänge vom Inhaltstyp, für die keine Einreichung durch Schüler oder Studenten erforderlich ist.

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

  • Rufen Sie die folgenden Add-on-Suchparameter ab und verwenden Sie sie:
    • addOnToken: Ein Autorisierungstoken, das an die Ansicht „Anhänge – Suche“ übergeben wird.
    • itemId: Eine eindeutige Kennung für die Kursarbeit, das Kursarbeitsmaterial oder die Mitteilung, an die der Add-on-Anhang gesendet wird.
    • 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 einem Add-on-Anhang nach der Erstellung von Google Classroom zugewiesen wird.
  • Implementieren Sie einen nichtflüchtigen Speicher für Anhänge mit Inhaltstypen.
  • Geben Sie Routen an, um Anhänge zu erstellen und die Iframes für die Ansicht der Lehrkraft und die Ansicht der Schüler/Studenten zu senden.
  • Senden Sie die folgenden Anfragen an die Google Classroom Add-ons API:
    • Erstellen Sie einen neuen Anhang.
    • Rufen Sie den Add-on-Kontext ab, der angibt, ob der angemeldete Nutzer ein Schüler/Student oder eine Lehrkraft ist.

Wenn Sie fertig sind, können Sie als Lehrkraft über die Google Classroom-Benutzeroberfläche Anhänge mit Inhaltstypen für Aufgaben erstellen. Auch Lehrkräfte und Schüler/Studenten im Kurs können sich die Inhalte ansehen.

Classroom API aktivieren

Rufen Sie ab diesem Schritt die Classroom API auf. 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.

Umgang mit den Suchparametern der Ansicht „Anhänge – Suche“

Wie bereits erwähnt, übergibt Google Classroom Abfrageparameter beim Laden der Ansicht „Anhängesuche“ im IFrame:

  • courseId: Die ID des aktuellen Classroom-Kurses.
  • itemId: Eine eindeutige Kennung für die Kursarbeit, das Kursarbeitsmaterial oder die Mitteilung, an die der Add-on-Anhang gesendet wird.
  • itemType: Entweder „courseWork“, „courseWorkMaterials“ oder „announcement“.
  • addOnToken: Ein Token, mit dem bestimmte Aktionen von Classroom-Add-ons autorisiert werden.
  • login_hint: Die Google-ID des aktuellen Nutzers.

In dieser Anleitung geht es um courseId, itemId, itemType und addOnToken. Behalten Sie diese bei und geben Sie sie beim Aufrufen der Classroom API weiter.

Speichern Sie die übergebenen Suchparameterwerte wie im vorherigen Schritt in der Sitzung. Das ist wichtig, wenn die Ansicht „Anhänge – Suche“ zum ersten Mal geöffnet wird, da dies die einzige Möglichkeit ist, dass Classroom diese Abfrageparameter übergeben kann.

Python

Rufen Sie die Flask-Serverdatei auf, die Routen für die Ansicht „Anhänge“ enthält (attachment-discovery-routes.py, wenn Sie unserem Beispiel folgen). Rufen Sie oben in der Landingpage-Route des 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")

Diese Werte werden nur in die Sitzung geschrieben, wenn sie vorhanden sind. Sie werden nicht noch einmal übergeben, wenn der Nutzer später zur Ansicht zur Suche nach Anhängen zurückkehrt, ohne den Iframe zu schließen.

Nichtflüchtigen Speicher für Anhänge mit Inhaltstyp hinzufügen

Sie benötigen einen lokalen Eintrag aller erstellten Anhänge. So können Sie die Inhalte, die die Lehrkraft ausgewählt hat, anhand der von Classroom bereitgestellten IDs aufrufen.

Richten Sie ein Datenbankschema für eine Attachment ein. In unserem Beispiel sind Anhänge mit einem Bild und einer Bildunterschrift zu sehen. 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 Bilds.
  • 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 Tabelle „Nutzer“ definiert haben (models.py, wenn Sie unserem Beispiel folgen). Fügen Sie die folgenden Zeilen unten in 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 Anhangsklasse in die Serverdatei mit Ihren Routen für die Anhangsverwaltung.

Neue Routen einrichten

Beginnen Sie diesen Schritt mit dem Einrichten einiger neuer Seiten in unserer Anwendung. Mit diesen können Nutzer Inhalte über unser Add-on erstellen und ansehen.

Routen zum Erstellen von Anhängen hinzufügen

Sie benötigen Seiten, auf denen die Lehrkräfte Inhalte auswählen und Anträge auf das Erstellen von Anhängen stellen können. Implementieren Sie die Route /attachment-options, um Inhaltsoptionen für die Auswahl durch die Lehrkraft anzuzeigen. Außerdem benötigen Sie Vorlagen für die Seiten zur Inhaltsauswahl und zur Bestätigung der Erstellung. Die bereitgestellten Beispiele enthalten Vorlagen für diese und können auch die Anfragen und Antworten der Classroom API anzeigen.

Sie können auch die Landingpage Ihrer vorhandenen Ansicht für die Suche nach Anhängen ä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 erhalten bleibt, z. B. der Widerruf der App-Berechtigungen. Diese sollten Ihnen beim Erstellen und Testen Ihres Add-ons nützlich sein.

In unserem Beispiel kann eine Lehrkraft aus einer kleinen Auswahl von Bildern mit Untertiteln auswählen. Wir haben vier Bilder berühmter Wahrzeichen bereitgestellt, deren Bildunterschriften aus den Dateinamen abgeleitet sind.

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

Daraufhin wird die Seite „Anhänge erstellen“ angezeigt, die in etwa so aussieht:

Python-Beispiel für die Inhaltsauswahl

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

Anfragen zum Erstellen von Anhängen stellen

Da Sie jetzt 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)

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

Python

In unserem Beispiel ist dies 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 Anfragetext an den entsprechenden addOnAttachments-Endpunkt. Geben Sie bei jeder Anfrage die Kennungen courseId, itemId, itemType und addOnToken an.

Python

In unserem Beispiel ist dies 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 den richtigen Inhalt 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 unserer Datenbank. Beachten Sie auch, dass Classroom den Abfrageparameter attachmentId übergibt, wenn die Ansichten für Lehrkräfte und Schüler/Studenten geöffnet werden:

Python

In unserem Beispiel ist dies 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()

Sie können den Nutzer an dieser Stelle auf eine Bestätigungsseite weiterleiten, auf der er sieht, dass er Anhänge erstellt hat.

Anhänge aus Ihrem Add-on zulassen

Fügen Sie jetzt auf der Seite App-Konfiguration des Google Workspace Marketplace SDK im Feld „Zulässige URI-Präfixe für Anhänge“ alle entsprechenden Adressen hinzu. Ihr Add-on kann nur Anhänge mit einem der auf dieser Seite aufgeführten URI-Präfixe erstellen. Dies ist eine Sicherheitsmaßnahme, die das Risiko von Man-in-the-Middle-Angriffen verringert.

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 „Lehrkraft“ und „Schüler/Student“ hinzufügen

Es gibt vier Iframes, in die ein Google Classroom-Add-on geladen werden kann. Sie haben bisher nur Routen erstellt, über die der iFrame für die Ansicht „Anhang – Entdecken“ ausgeliefert wird. Fügen Sie als Nächstes Routen hinzu, um auch die Iframes für die Ansichten von Lehrkräften und Schülern zu senden.

Der Iframe für die Ansicht für Lehrkräfte ist erforderlich, um eine Vorschau der Schüler-/Studentenoberfläche anzuzeigen. Er kann aber auch optional zusätzliche Informationen oder Bearbeitungsfunktionen enthalten.

Die Ansicht für Schüler/Studenten ist die Seite, die jedem Schüler/Studenten 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/Studenten anzeigt. Mithilfe von Classroom API-Methoden können Sie beim Laden der Seite 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 solltest du auch den Abfrageparameter login_hint verarbeiten und den Nutzer bei Bedarf zu deinem Autorisierungsvorgang weiterleiten. Weitere Informationen zu diesem Ablauf finden Sie in den Details zur Anmeldung, die in den vorherigen Schritt-für-Schritt-Anleitungen erläutert wurden.

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

Python

In unserem Beispiel ist dies 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()

Mit dieser Methode werden Informationen zur Rolle des aktuellen Nutzers im Kurs zurückgegeben. Die Ansicht, die dem Nutzer angezeigt wird, kann je nach Rolle geändert werden. Im Antwortobjekt muss genau eines der Felder studentContext oder teacherContext ausgefüllt sein. Anhand dieser Informationen können Sie entscheiden, wie Sie den Nutzer ansprechen.

Anhand des Werts des Abfrageparameters attachmentId können Sie in jedem Fall ermitteln, welcher Anhang aus unserer Datenbank abgerufen werden soll. Dieser Abfrageparameter wird beim Öffnen der URIs für die Ansicht der Lehrkraft oder der Schüler/Studenten angegeben.

Python

In unserem Beispiel ist dies 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.
  • Rufen Sie den Tab Kursaufgaben auf und erstellen Sie eine neue Aufgabe.
  • Klicken Sie unter dem Textfeld 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 auf der Seite App-Konfiguration des Google Workspace Marketplace SDK angegeben haben.
  • Wählen Sie einen Inhalt aus, den Sie der Aufgabe anhängen möchten.
  • Schließen Sie den IFrame, nachdem der Vorgang zum Erstellen des Anhangs abgeschlossen ist.

In der Benutzeroberfläche zum Erstellen von Aufgaben in Google Classroom sollte eine Anhängekarte angezeigt werden. Klicken Sie auf die Karte, um den IFrame für die Ansicht des Lehrers zu öffnen, und prüfen Sie, ob der richtige Anhang angezeigt wird. Klicken Sie auf die Schaltfläche Zuweisen.

Führen Sie die folgenden Schritte aus, um die Nutzung für Schüler und Studenten zu testen:

  • Melden Sie sich dann als Schüler-/Studenten-Testnutzer in Classroom in demselben Kurs an wie der Lehrkraft-Testnutzer.
  • Sie finden die Testaufgabe auf dem Tab „Kursaufgaben“.
  • Maximieren Sie die Aufgabe und klicken Sie auf die Karte „Anhang“, um den IFrame für die 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 vom Typ „Aktivität“ erstellen.