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

Это четвертое пошаговое руководство из серии руководств по использованию дополнений для класса.

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

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

  • Получите и используйте следующие параметры запроса дополнения:
    • addOnToken : Токен авторизации, передаваемый в представление обнаружения вложений.
    • itemId : Уникальный идентификатор для учебного задания, учебного материала или объявления, к которому прикрепляется дополнительный файл.
    • itemType : Либо "courseWork", "courseWorkMaterials", либо "announcement".
    • courseId : Уникальный идентификатор курса Google Classroom, в котором создается задание.
    • attachmentId : Уникальный идентификатор, присваиваемый Google Classroom дополнительному вложению после его создания.
  • Внедрить постоянное хранилище для вложений типа контента.
  • Укажите маршруты для создания вложений и для отображения iframe-элементов «Просмотр преподавателя» и «Просмотр ученика».
  • Отправьте следующие запросы к API дополнений Google Classroom:
    • Создать новый файл-вложение.
    • Получите контекст дополнения, который определяет, является ли вошедший в систему пользователь студентом или преподавателем.

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

Включите API для учебных классов

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

Обработка параметров запроса представления обнаружения вложений.

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

  • courseId : Идентификатор текущего курса в аудитории.
  • itemId : Уникальный идентификатор для учебного задания, учебного материала или объявления, к которому прикрепляется дополнительный файл.
  • itemType : Либо "courseWork", "courseWorkMaterials", либо "announcement".
  • addOnToken : Токен, используемый для авторизации определенных действий надстройки Classroom.
  • login_hint : Идентификатор Google текущего пользователя.

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

Как и на предыдущем шаге, сохраните значения переданных параметров запроса в нашей сессии. Важно сделать это при первом открытии окна поиска вложений, поскольку это единственная возможность для 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 .

Запросы на создание вложений к сообщениям

Теперь, когда вы знаете, какие материалы учитель хочет прикрепить, отправьте запросы к 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 на конечную точку 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 в теле запроса на соответствующую конечную точку 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()

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

Разрешите прикрепление файлов из вашего дополнения.

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

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

Добавить маршруты для отображения информации для учителя и ученика.

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

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

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

Выполните следующие шаги, чтобы проверить пользовательский опыт:

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

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

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