活动类型附件

这是 Google 课堂插件演示系列中的第 5 个演示。

在本演示中,您将修改前面演示步骤中的示例以生成 activity 类型附件。这些是指需要学生提交的任何附件,例如书面回复、测验或其他学生生成的工件。

区分内容类型附件和活动类型附件非常重要。活动类型的附件与内容类型的附件有以下不同:

  • “学生视图”iframe 的右上角会显示一个“提交”按钮。
  • 它们为学生作业提供唯一标识符。
  • 他们的附件卡片会显示在 Google 课堂评分者界面中。
  • 他们可以为所属的作业设置成绩。

请参阅下一篇演示,了解有关评分的讨论。在本演示过程中,您将完成以下操作:

  • 修改之前向 Google 课堂 API 发出的附件创建请求,以创建活动类型的附件。
  • 为学生提交的作业实现永久性存储。
  • 修改之前的“学生视图”路由,以接受学生输入。
  • 提供用于提供“学生作业评价”iframe 的路由。

完成后,您可以在以教师身份登录时,通过 Google 课堂界面在作业中创建活动型附件。班级中的学生也可以在 iframe 中完成活动并提交回复。教师可以在 Google 课堂的评分界面中查看学生提交的作业。

在本示例中,请重复使用上一个演示中的附件模板,该模板显示了著名地标的图片以及包含地标名称的图片说明。此 activity 包括提示学生提供地标的名称。

修改附件创建请求

前往您在上一个演示中创建内容类型附件的代码部分。这里的关键项是 AddOnAttachment 对象的实例,我们之前在其中为附件指定了 teacherViewUristudentViewUrititle

虽然所有插件附件都需要这三个字段,但studentWorkReviewUri 的存在与否决定了附件是活动类型还是内容类型。填充了 studentWorkReviewUriCREATE 请求会变为活动类型的附件,而没有 studentWorkReviewUriCREATE 请求会变为内容类型的附件。

对此请求进行的唯一修改是填充 studentWorkReviewUri 字段。在此处添加一个命名恰当的路线;您将在后续步骤中实现该路线。

Python

在我们提供的示例中,此方法位于 webapp/attachment_routes.py 文件中的 create_attachments 方法中。

attachment = {
    # Specifies the route for a teacher user.
    "teacherViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True),
    },
    # Specifies the route for a student user.
    "studentViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True)
    },
    # Specifies the route for a teacher user when the attachment is
    # loaded in the Classroom grading view.
    # The presence of this field marks this as an activity-type attachment.
    "studentWorkReviewUri": {
        "uri":
            flask.url_for(
                "view_submission", _scheme='https', _external=True)
    },
    # The title of the attachment.
    "title": f"Attachment {attachment_count}",
}

为内容类型附件添加了永久存储

记录学生对我们的活动的回应。您可以在教师在“学生作业评价”iframe 中查看提交内容时查看该信息。

Submission 设置数据库架构。我们提供的示例要求学生输入图片中显示的地标的名称。因此,Submission 包含以下属性:

  • attachment_id:连接的唯一标识符。由 Google 课堂分配,并在创建附件时在响应中返回。
  • submission_id:学生提交的标识符。由 Google 课堂分配,并在学生视图的 getAddOnContext 响应中返回。
  • student_response:学生提供的答案。

Python

扩展上一步中的 SQLite 和 flask_sqlalchemy 实现。

前往您定义之前表格的文件(如果您按照我们提供的示例操作,则为 models.py)。在文件底部添加以下代码。

# Database model to represent a student submission.
class Submission(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    submission_id = db.Column(db.String(120), primary_key=True)

    # The unique identifier for the student's submission.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The student's response to the question prompt.
    student_response = db.Column(db.String(120))

将新的 Submission 类导入包含附件处理路由的服务器文件中。

修改“学生视图”路线

接下来,修改之前的“学生视图”路由,以显示一个小表单并接受学生的输入。您可以重复使用上一个演示中的大部分代码。

找到为学生视图提供路线的服务器代码。这是创建附件时在 studentViewUri 字段中指定的路由。首先要做的更改是从 getAddOnContext 响应中提取 submissionId

Python

在我们提供的示例中,此方法位于 webapp/attachment_routes.py 文件中的 load_activity_attachment 方法中。

# Issue a request to the courseWork.getAddOnContext endpoint
addon_context_response = classroom_service.courses().courseWork(
).getAddOnContext(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"]).execute()

# One of studentContext or teacherContext will be populated.
user_context = "student" if addon_context_response.get(
    "studentContext") else "teacher"

# If the user is a student...
if user_context == "student":
    # Extract the submissionId from the studentContext object.
    # This value is provided by Google Classroom.
    flask.session["submissionId"] = addon_context_response.get(
            "studentContext").get("submissionId")

您还可以发出请求来获取学生提交内容的状态。响应包含 SubmissionState 值,用于指示状态,例如学生是否已打开附件或上交附件。如果您想禁止修改已提交的作业,或者希望为教师提供有关学生进度的分析数据,这项功能可能会很有用:

Python

在我们提供的示例中,这是上述 load_activity_attachment 方法的延续。

# Issue a request to get the status of the student submission.
submission_response = classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().get(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    attachmentId=flask.session["attachmentId"],
    submissionId=flask.session["submissionId"]).execute()

最后,从数据库中提取附件信息并提供输入表单。我们提供的示例中的表单由一个字符串输入字段和一个提交按钮组成。显示地标图片,并提示学生输入地标的名称。 他们提供回复后,请将其记录在我们的数据库中。

Python

在我们提供的示例中,这是上述 load_activity_attachment 方法的延续。

# Look up the attachment in the database.
attachment = Attachment.query.get(flask.session["attachmentId"])

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 complete the activity below.")

form = activity_form_builder()

if form.validate_on_submit():
    # Record the student's response in our database.

    # Check if the student has already submitted a response.
    # If so, update the response stored in the database.
    student_submission = Submission.query.get(flask.session["submissionId"])

    if student_submission is not None:
        student_submission.student_response = form.student_response.data
    else:
        # Store the student's response by the submission ID.
        new_submission = Submission(
            submission_id=flask.session["submissionId"],
            attachment_id=flask.session["attachmentId"],
            student_response=form.student_response.data)
        db.session.add(new_submission)

    db.session.commit()

    return flask.render_template(
        "acknowledge-submission.html",
        message="Your response has been recorded. You can close the " \
            "iframe now.",
        instructions="Please Turn In your assignment if you have " \
            "completed all tasks."
    )

# Show the activity.
return flask.render_template(
    "show-activity-attachment.html",
    message=message_str,
    image_filename=attachment.image_filename,
    image_caption=attachment.image_caption,
    user_context=user_context,
    form=form,
    responses=response_strings)

为了区分用户,不妨考虑停用提交功能,改为在教师视图中显示正确答案。

为“学生作业评价”iframe 添加路由

最后,添加用于传送学生作业评价 iframe 的路径。此路线的名称应与创建附件时为 studentWorkReviewUri 提供的名称一致。当教师在 Google 课堂评分器界面中查看学生提交的作业时,系统会打开此路线。

当 Google 课堂打开“学生作业评价”iframe 时,您会收到 submissionId 查询参数。您可以使用它从本地数据库检索学生的作业:

Python

在我们提供的示例中,它位于 webapp/attachment_routes.py 文件中。

@app.route("/view-submission")
def view_submission():
    """
    Render a student submission using the show-student-submission.html template.
    """

    # Save the query parameters passed to the iframe in the session, just as we did
    # in previous routes. Abbreviated here for readability.
    add_iframe_query_parameters_to_session(flask.request.args)

    # For the sake of brevity in this example, we'll skip the conditional logic
    # to see if we need to authorize the user as we have done in previous steps.
    # We can assume that the user that reaches this route is a teacher that has
    # already authorized and created an attachment using the add-on.

    # In production, we recommend fully validating the user's authorization at
    # this stage as well.

    # Look up the student's submission in our database.
    student_submission = Submission.query.get(flask.session["submissionId"])

    # Look up the attachment in the database.
    attachment = Attachment.query.get(student_submission.attachment_id)

    # Render the student's response alongside the correct answer.
    return flask.render_template(
        "show-student-submission.html",
        message=f"Loaded submission {student_submission.submission_id} for "\
            f"attachment {attachment.attachment_id}.",
        student_response=student_submission.student_response,
        correct_answer=attachment.image_caption)

测试插件

重复上一个演示文稿中的测试插件步骤。您应该有一个可供学生打开的附件。

如需测试活动附件,请完成以下步骤:

  • 以与教师测试用户同在一个课程的学生测试用户身份登录 Google 课堂
  • 前往课业标签页,然后展开相应测试的作业
  • 点击插件附件卡片以打开“学生视图”,然后提交该活动的回答。
  • 完成活动后,关闭 iframe。(可选)点击上交按钮。

完成此活动后,您应该不会在 Google 课堂中看到任何变化。现在,测试“学生作业评分”iframe:

  • 教师测试用户的身份登录“课堂”。
  • 成绩标签页中,找到您的测试作业的列。点击测试作业的名称。
  • 找到测试学生用户的卡片。点击卡片上的附件。

确认学生看到的提交内容正确无误。

恭喜!您可以继续执行下一步:同步附件成绩