Вложения типа контента

Это четвертое пошаговое руководство из серии пошаговых руководств по дополнениям к игре «Класс».

В этом пошаговом руководстве вы взаимодействуете с API Google Classroom для создания вложений. Вы предоставляете пользователям маршруты для просмотра содержимого вложений. Виды различаются в зависимости от роли пользователя в классе. В этом пошаговом руководстве рассматриваются вложения типа content-type , которые не требуют отправки учащимся.

В ходе этого пошагового руководства вы выполните следующее:

  • Получите и используйте следующие дополнительные параметры запроса:
    • addOnToken : токен авторизации, переданный в представление обнаружения вложений.
    • itemId : уникальный идентификатор для CourseWork, CourseWorkMaterial или Announcement, который получает вложение-дополнение.
    • itemType : «courseWork», «courseWorkMaterials» или «announcement».
    • courseId : уникальный идентификатор курса Google Classroom, в котором создается задание.
    • attachmentId : уникальный идентификатор, назначаемый Google Classroom дополнительному вложению после создания.
  • Реализуйте постоянное хранилище для вложений контента.
  • Предоставьте маршруты для создания вложений и обслуживания фреймов «Представление учителя» и «Представление ученика».
  • Отправьте следующие запросы к API дополнений Google Classroom:
    • Создайте новое вложение.
    • Получите контекст дополнения, который определяет, является ли вошедший в систему пользователь студентом или преподавателем.

После завершения вы можете создавать вложения в виде контента к заданиям через пользовательский интерфейс Google Classroom, войдя в систему как учитель. Учителя и ученики в классе также могут просматривать контент.

Включить API класса

Вызовите API Classroom, начиная с этого шага. API должен быть включен для вашего проекта Google Cloud, прежде чем вы сможете вызывать его. Перейдите к записи библиотеки API Google Classroom и выберите Enable .

Обработка параметров запроса Attachment Discovery View

Как обсуждалось ранее , Google Classroom передает параметры запроса при загрузке представления обнаружения вложений в iframe:

  • courseId : идентификатор текущего курса Classroom.
  • itemId : уникальный идентификатор для CourseWork, CourseWorkMaterial или Announcement, который получает вложение-дополнение.
  • itemType : «courseWork», «courseWorkMaterials» или «announcement».
  • addOnToken : токен, используемый для авторизации определенных действий надстройки Classroom.
  • login_hint : идентификатор Google текущего пользователя.

В этом пошаговом руководстве рассматриваются courseId , itemId , itemType и addOnToken . Сохраняйте и передавайте их при вызовах API Classroom.

Как и в предыдущем шаге пошагового руководства, сохраните переданные значения параметров запроса в нашем сеансе. Важно, чтобы мы сделали это, когда Attachment Discovery View впервые открывается, так как это единственная возможность для Classroom передать эти параметры запроса.

Питон

Перейдите к файлу сервера Flask, который предоставляет маршруты для Attachment Discovery View ( 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 : Подпись, отображаемая рядом с изображением.

Питон

Расширьте реализацию 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-options для отображения параметров контента для выбора учителем. Вам также нужны шаблоны для выбора контента и страниц подтверждения создания. Наши предоставленные примеры содержат шаблоны для них, а также могут отображать запросы и ответы из API класса.

Обратите внимание, что вы можете также изменить существующую целевую страницу Attachment Discovery View, чтобы отобразить параметры контента вместо создания новой страницы /attachment-options . Мы рекомендуем создать новую страницу для целей этого упражнения, чтобы сохранить поведение SSO, реализованное на втором этапе пошагового руководства , например, отзыв разрешений приложения. Они должны оказаться полезными при создании и тестировании вашего дополнения.

Учитель может выбрать из небольшого набора изображений с подписями в нашем примере. Мы предоставили четыре изображения известных достопримечательностей, подписи к которым получены из имен файлов.

Питон

В нашем примере это файл 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 .

Выдача запросов на создание вложений

Теперь, когда вы знаете, какие фрагменты контента учитель хотел бы прикрепить, отправьте запросы в API класса, чтобы создать вложения по нашему заданию. Сохраните данные вложения в своей базе данных после получения ответа от API класса.

Начнем с получения экземпляра службы Classroom:

Питон

В нашем примере это файл 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 :

Питон

В нашем примере это продолжение метода 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 с каждым запросом.

Питон

В нашем примере это продолжение метода 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 при открытии представлений Teacher и Student:

Питон

В нашем примере это продолжение метода 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. До сих пор вы построили только маршруты, которые обслуживают Attachment Discovery View iframe. Далее добавьте маршруты для обслуживания Teacher и Student View iframes.

Интерфейс iframe «Просмотр учителя» необходим для предварительного просмотра работы ученика, но при желании может включать дополнительную информацию или функции редактирования.

Страница «Просмотр для студентов» отображается каждому студенту при открытии вложенного файла.

Для целей этого упражнения создайте один маршрут /load-content-attachment , который обслуживает как Teacher, так и Student View. Используйте методы API Classroom, чтобы определить, является ли пользователь учителем или учеником при загрузке страницы.

Питон

В нашем примере это файл 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 , соответствующую типу элемента.

Питон

В нашем примере это продолжение метода 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 просмотра Teacher или Student.

Питон

В нашем примере это продолжение метода 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 Google Classroom. Щелкните карточку, чтобы открыть окно учителя iframe и убедиться, что отображается правильное вложение. Щелкните кнопку Assign .

Чтобы проверить опыт студента, выполните следующие шаги:

  • Затем войдите в систему Classroom как тестовый пользователь-ученик того же класса, что и тестовый пользователь-учитель.
  • Тестовое задание можно найти на вкладке «Работа в классе».
  • Разверните задание и щелкните карточку вложения, чтобы открыть iframe «Просмотр учащихся».

Убедитесь, что для учащегося отображается правильное вложение.

Поздравляем! Вы готовы перейти к следующему шагу: созданию вложений типа «активность» .