外部附件和上交

这是 Google 课堂插件演练系列中的第七篇演练。

在此演练中,您将为 Web 应用添加行为,以从 Google 课堂外部创建附件。使用此行为可让用户通过现有产品或网站创建插件附件。对于 CourseWork 集成,这也是一个很好的补充,因为您可以将现有流量引导至插件提供的改进版用户体验,而无需更改其流程。如需了解建议的流程,请参阅我们的在 Google 课堂之外创建附件指南页面。

您还可以向插件添加行为,以通过编程方式修改包含插件附件的作业。您可以修改附加了插件附件的任何作业,无论该作业是谁创建的。这对于学生在完成活动后提交作业非常有用,可以向教师表明学生已完成布置的任务,并且其作业已准备好接受检查。

您扩展支持内容类型activity 类型附件的插件的最终版本。本指南中使用了 content-type 附件。

添加作业管理 OAuth 范围

确保您的应用请求以下范围:

  • https://www.googleapis.com/auth/classroom.addons.teacher
  • https://www.googleapis.com/auth/classroom.addons.student
  • https://www.googleapis.com/auth/classroom.coursework.students

之前不需要 classroom.coursework.students 范围,该范围用于创建或修改 CourseWork 分配。将此范围添加到 Cloud 项目的 Google Workspace Marketplace SDKOAuth 同意屏幕和服务器代码中的范围列表中。

Python

  SCOPES = [
    "https://www.googleapis.com/auth/classroom.addons.teacher",
    "https://www.googleapis.com/auth/classroom.addons.student",
    "https://www.googleapis.com/auth/classroom.coursework.students",
  ]

在 Google 课堂中创建作业

向非 iframe 网页添加按钮

本演示中描述的流程允许用户从非 Google 产品创建 Google 课堂作业和附件。在实践中,这很可能是您现有的网站或应用。在此示例中,您需要创建一个模拟网页作为外部网站。您需要一个按钮或链接,点击该按钮或链接后,系统会打开一个新路线,该路线会执行建议的 CourseWork 流程来创建新作业。

如果您还没有按钮或链接,还需要添加一个按钮或链接,以便用户登录。您需要用户凭据才能发出后续 API 请求,因此用户必须完成 OAuth 2.0 握手。如需获取具体指导,请参阅登录演练

Python

提供的 Python 示例修改了第一个演练步骤中介绍的 /index 路由。

<!-- /webapp/templates/index.html -->
<a href="clear-credentials.html">Logout</a>
<a href="start-auth-flow.html">Login</a>

<br>

<a href="create-coursework-assignment.html">Create a CourseWork Assignment</a>

添加一个 HTML 模板来表示您网站中的目的地。此页面将显示将附加到 CourseWork 作业的内容。

<!-- /webapp/templates/example-coursework-assignment.html -->
<h1>CourseWork assignment loaded!</h1>
<p>You've loaded a CourseWork assignment! It was created from an external web page.</p>

创建一个新的 Python 模块文件来处理与 CourseWork 相关的路由。 在提供的示例中,此值为 coursework_routes.py。添加以下三条路线;请注意,您稍后会填写部分内容。

# /webapp/coursework_routes.py
@app.route("/create-coursework-assignment")
def create_coursework_assignment():
  """
  Completes the assignment creation flow.
  """

  # Check that the user is signed in. If not, perform the OAuth 2.0
  # authorization flow.
  credentials = get_credentials()

  if not credentials:
    return start_auth_flow("coursework_assignment_callback")

  # Construct the Google Classroom service.
  classroom_service = get_classroom_service()

  pass  # To be completed later.

@app.route("/example-coursework-assignment/<assignment_type>")
def example_coursework_assignment(assignment_type):
  """
  Renders the "example-coursework-assignment.html" template.
  """
  return flask.render_template(
      "example-coursework-assignment.html", assignment_type=assignment_type
  )

@app.route("/coursework-assignment-callback")
def coursework_assignment_callback():
  """
  Completes the OAuth 2.0 handshake and stores credentials in the session.
  This is identical to the callback introduced in the sign-in walkthrough,
  but redirects the user to the index page instead of the attachment
  discovery page.
  """
  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE,
      scopes=SCOPES,
      state=flask.session["state"],
      redirect_uri=flask.url_for("coursework_assignment_callback", _external=True),
  )

  flow.fetch_token(authorization_response=flask.request.url)

  credentials = flow.credentials
  flask.session["credentials"] = session_credentials_to_dict(
      credentials
  )

  # Close the current window and redirect the user to the index page.
  return flask.render_template("close-me.html", redirect_destination="index")

检查用户是否符合创建附件的条件

用户必须满足多项前提条件,您才能代表他们创建附件。为方便起见,Google 提供了 userProfiles.checkUserCapability 方法来确定用户是否满足这些前提条件。满足前提条件的用户称为符合条件的用户。

将资格条件检查添加到 CourseWork 创建路线实现中。 然后,测试响应中的 allowed 字段。对于符合条件的用户,请按照逻辑创建包含加购项附件的作业。否则,请创建 LinkMaterial。您需要知道用户想要在哪个课程中创建作业。通常,您会提示用户指定要使用的课程。为简单起见,我们在本示例中使用硬编码的值。

Python

# /webapp/coursework_routes.py
@app.route("/create-coursework-assignment")
def create_coursework_assignment():
  """
  Completes the assignment creation flow.
  """
  # ... Check that the user is signed in and get the Classroom service ...

  # Check whether the user can create add-on attachments.
  eligibility_response = (
      classroom_service.userProfiles()
      .checkUserCapability(
        userId="me",
        capability="CREATE_ADD_ON_ATTACHMENT",
        # The previewVersion is necessary while the method is available in the
        # Workspace Developer Preview Program.
        previewVersion="V1_20240930_PREVIEW",
      ).execute()
  )
  is_create_attachment_eligible = eligibility_response.get("allowed")

  if is_create_attachment_eligible:
    # See the "Create an assignment with add-on attachment for eligible users" section for implementation.
  if not is_create_attachment_eligible:
    # See the "Create a Link Material" section for implementation.

为符合条件的用户创建包含插件附件的作业

如果用户有资格创建附件,请执行以下操作:

  1. 发送 API 请求,以在 Google 课堂中创建不含附件courseWork 作业。
  2. 提取新创建的布置的 id
  3. 构造新的 CourseWork AddOnAttachment
  4. 发送请求,以在 Google 课堂中新创建的作业上创建插件附件。

Python

# The ID of the course to which the assignment will be added.
course_id = 1234567890  # TODO(developer) Replace with an actual course ID.

# /webapp/coursework_routes.py
if is_create_attachment_eligible:
  # Create an assignment.
  coursework = {
      "title": "My CourseWork Assignment with Add-on Attachment",
      "description": "Created using the Classroom CourseWork API.",
      "workType": "ASSIGNMENT",
      "state": "DRAFT",  # Set to 'PUBLISHED' to assign to students.
  }

  # Issue a request to create the assignment.
  create_assignment_response = (
      classroom_service.courses()
      .courseWork()
      .create(courseId=course_id, body=coursework)
      .execute()
  )

  # Create an add-on attachment that links to the selected content and
  # associate it with the new assignment.
  content_url = flask.url_for(
      "example_coursework_assignment",
      assignment_type="add-on-attachment",
      _scheme="https",
      _external=True,
  )

  # Construct an AddOnAttachment instance.
  attachment = {
      "teacherViewUri": {"uri": content_url},
      "studentViewUri": {"uri": content_url},
      "title": f'Test Attachment for Assignment {create_assignment_response.get("id")}',
  }

  # Issue a request to create the attachment.
  add_on_attachment_response = (
      classroom_service.courses()
      .courseWork()
      .addOnAttachments()
      .create(
          courseId=course_id,
          itemId=create_assignment_response.get("id"),  # ID of the new assignment.
          body=attachment,
      )
      .execute()
  )

如果用户不符合创建附件的条件,请改为创建链接素材资源,具体操作如下:

Python

# The ID of the course to which the assignment will be added.
course_id = 1234567890  # TODO(developer) Replace with an actual course ID.

if not is_create_attachment_eligible:
    coursework = {
        "title": "My CourseWork Assignment with Link Material",
        "description": "Created using the Classroom CourseWork API.",
        "workType": "ASSIGNMENT",
        "state": "DRAFT",  # Set to 'PUBLISHED' to assign to students.
        # Specify the URL for your content as a Link Material.
        "materials": [
            {
                "link": {
                    "url": flask.url_for(
                        "example_coursework_assignment",
                        assignment_type="link-material",
                        _scheme="https",
                        _external=True,
                    )
                }
            }
        ],
    }

    # Issue a request to create the assignment.
    assignment_response = (
        classroom_service.courses()
        .courseWork()
        .create(courseId=course_id, body=coursework)
        .execute()
    )

修改已创建的作业

您可以访问、修改、提交、回收或退回任何包含至少一个插件附件的 Google 课堂动态项目,无论该动态项目是谁创建的。信息流项可以是任何 AnnouncementCourseWork 作业或 CourseWorkMaterial

为了演示这一点,您将添加一个用于修改指定流项的路由。使用此方法验证您是否可以访问和修改通过 API 通过 Google 课堂界面由教师创建的动态项目。

向您在本演练中首次修改的网页再添加一个链接或按钮。它应该会打开一个新路由来修改 CourseWork 分配。

Python

提供的 Python 示例会修改本演练前面部分修改的 /index 路由。

<!-- /webapp/templates/index.html -->
<a href="modify-coursework-assignment.html">Create a CourseWork Assignment</a>

创建新路由以处理与 CourseWork 相关的路由。在提供的示例中,此文件为 coursework_routes.py 文件。

# Check that the user is signed in.
credentials = get_credentials()

if not credentials:
  return start_auth_flow("coursework_assignment_callback")

# Get the Google Classroom service.
classroom_service = get_classroom_service()

# The ID of the course to which the assignment will be added.
# Ordinarily, you'll prompt the user to specify which course to use. For
# simplicity, we use a hard-coded value in this example.
course_id = 1234567890  # TODO(developer) Replace with an actual course ID.
assignment_id = 1234567890  # TODO(developer) Replace with an actual assignment ID.

# Retrieve details about the CourseWork assignment.
get_coursework_response = (
    classroom_service.courses()
    .courseWork()
    .get(courseId=course_id, id=assignment_id)
    .execute()
)

# Alter the current title.
assignment_title = f"{get_coursework_response.get('title')} (Modified by API request)"

# Issue a request to modify the assignment.
modify_coursework_response = (
    classroom_service.courses()
    .courseWork()
    .patch(
        courseId=course_id,
        id=assignment_id,
        updateMask="title",
        body={"title": assignment_title},
    )
    .execute()
)

测试插件

为简单起见,提供的示例使用硬编码的课程和作业标识符。您可以通过以下方式获取这些标识符:使用教师凭据向 coursescourseWork 资源的 getlist 方法发出请求。创建 courseWork 作业时,它们也会在响应中返回。

运行服务器,然后前往您的首页,并以没有 Google Workspace 教育教与学升级版或 Plus 版许可的教师用户身份登录。您可以在测试网域的管理控制台中切换用户的许可状态。点击创建 CourseWork 作业按钮,然后打开 Google 课堂界面,验证是否已创建附加了链接材料的作业。附件应显示所链接网页的标题和网址。

测试插件附件创建

返回到索引页,然后以教师用户身份登录(拥有 Google Workspace 教育版教与学加购项或 Plus 版许可)。点击创建课程作业按钮,然后打开 Google 课堂界面,验证是否已创建附加了插件的作业。附件应显示您的插件应用的名称以及代码中指定的标题。

测试分配修改

返回到索引页,确保您以拥有教与学升级版或 Plus 版许可的教师用户身份登录。点击 Modify a CourseWork Assignment 按钮,然后返回 Google 课堂界面,验证作业标题是否已更改。

恭喜!您已完成整个演练系列。