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

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

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

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

  • Получите и используйте следующие дополнительные параметры запроса:
    • 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 и выберите «Включить» .

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

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

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

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

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

Питон

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

Питон

Расширьте реализацию 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 Classroom.

Обратите внимание, что вы также можете изменить существующую целевую страницу «Представление обнаружения вложений» для отображения параметров контента вместо создания новой страницы /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 .

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

Теперь, когда вы знаете, какие материалы преподаватель хотел бы прикрепить, отправляйте запросы к Classroom API для создания вложений по нашему заданию. Сохраните информацию о вложениях в своей базе данных после получения ответа от Classroom 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 при открытии представлений «Преподаватель» и «Студент»:

Питон

В нашем примере это продолжение метода 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()

На этом этапе рассмотрите возможность перенаправить пользователя на страницу подтверждения, подтверждающую, что он успешно создал вложения.

Разрешить вложения из вашего дополнения

Сейчас самое время добавить подходящие адреса в поле «Разрешенные префиксы URI для вложений» на странице конфигурации приложения в Google Workspace Marketplace SDK. Ваше дополнение может создавать вложения только с одним из префиксов URI, перечисленных на этой странице. Это мера безопасности, помогающая снизить вероятность атак типа «человек посередине».

Самый простой подход — указать в этом поле домен верхнего уровня, например https://example.com . https://localhost:<your port number>/ подойдет, если вы используете локальный компьютер в качестве веб-сервера.

Добавить маршруты для представлений учителя и ученика

Есть четыре iframe , в которые может быть загружено дополнение Google Classroom. Пока что вы создали маршруты только для iframe представления «Обнаружение вложений». Теперь добавьте маршруты для iframe представления «Преподаватель» и «Студент».

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

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

Для этого упражнения создайте один маршрут /load-content-attachment , обслуживающий как режим преподавателя, так и режим учащегося. Используйте методы 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 представления преподавателя или учащегося.

Питон

В нашем примере это продолжение метода 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] как один из пользователей вашего тестового уровня для учителей .
  • Перейдите на вкладку «Работа в классе» и создайте новое задание .
  • Нажмите кнопку «Дополнения» под текстовой областью, затем выберите нужное дополнение. Откроется iframe, и дополнение загрузит URI настройки вложения , указанный вами на странице конфигурации приложения в Google Workspace Marketplace SDK.
  • Выберите фрагмент контента, который вы хотите прикрепить к заданию.
  • Закройте iframe после завершения процесса создания вложения.

В интерфейсе создания задания в Google Классе должна появиться карточка с вложением. Щелкните по ней, чтобы открыть окно «Представление учителя» и убедиться, что отображается нужное вложение. Нажмите кнопку «Назначить» .

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

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

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

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