rubric 是教师在为学生提交的内容评分时可以使用的模板。借助 Classroom API,您可以代表教师管理这些评分标准,还可以读取学生提交的作业的评分标准得分。
 图 1. Google 课堂作业中的示例评分准则视图。
图 1. Google 课堂作业中的示例评分准则视图。
本指南介绍了 Rubrics API 的基本概念和功能。如需了解评分标准的一般结构以及如何在 Google 课堂界面中进行评分标准评分,请参阅以下帮助中心文章。
前提条件
本指南假定您具备以下各项:
- Python 3.8.6 或更高版本
- pip 软件包管理工具
- 具有一个 Google Cloud 项目。
- 已启用 Google 课堂且已分配 Google Workspace 教育 Plus 版许可的 Google Workspace 教育版账号。如果您没有升级版开发者演示账号,可以申请升级版。
- 至少包含一个测试学生账号的测试课程。如果您没有可用于测试的 Google 课堂课程,请在界面中创建一个并添加测试学生。
为桌面应用授权凭据
如需以最终用户身份进行身份验证并访问应用中的用户数据,您需要创建一个或多个 OAuth 2.0 客户端 ID。客户端 ID 用于向 Google 的 OAuth 服务器标识单个应用。如果您的应用在多个平台上运行,您必须为每个平台分别创建客户端 ID。
- 在 Google Cloud 控制台中,前往 Google Cloud 凭据页面。
- 依次点击创建凭据 > OAuth 客户端 ID。
- 依次点击应用类型 > 桌面应用。
- 在名称字段中,输入凭据的名称。此名称仅在 Google Cloud 控制台中显示。例如,“Rubrics client”。
- 点击创建。此时会显示“OAuth 客户端已创建”界面,其中显示您的新客户端 ID 和客户端密钥。
- 依次点击下载 JSON 和确定。新创建的凭据会显示在“OAuth 2.0 客户端 ID”下方。
- 将下载的 JSON 文件另存为 credentials.json,然后将该文件移至您的工作目录。
- 依次点击创建凭据 > API 密钥,并记下 API 密钥。
如需了解详情,请参阅创建访问凭据。
配置 OAuth 范围
根据项目现有的 OAuth 范围,您可能需要配置其他范围。
- 前往 OAuth 权限请求页面。
- 依次点击修改应用 > 保存并继续,进入“范围”界面。
- 点击添加或移除范围。
- 如果您还没有以下范围,请添加:
- https://www.googleapis.com/auth/classroom.coursework.students
- https://www.googleapis.com/auth/classroom.courses
 
- 然后依次点击更新 > 保存并继续 > 保存并继续 > 返回信息中心。
如需了解详情,请参阅配置 OAuth 权限请求页面。
classroom.coursework.students 范围可实现对评分标准的读写访问(以及对 CourseWork 的访问),而 classroom.courses 范围可实现对课程的读写访问。
给定方法所需的范围列在该方法的参考文档中。例如,请参阅 courses.courseWork.rubrics.create 授权范围。您可以在 Google API 的 OAuth 2.0 权限范围中查看所有 Classroom 权限范围。
配置示例
在工作目录中,安装 Python 版 Google 客户端库:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
创建一个名为 main.py 的文件,该文件可构建客户端库并授权用户,使用您的 API 密钥替换 YOUR_API_KEY:
import json
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/classroom.courses',
          'https://www.googleapis.com/auth/classroom.coursework.students']
def build_authenticated_service(api_key):
    """Builds the Classroom service."""
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run.
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    try:
        # Build the Classroom service.
        service = build(
            serviceName="classroom",
            version="v1",
            credentials=creds,
            discoveryServiceUrl=f"https://classroom.googleapis.com/$discovery/rest?labels=DEVELOPER_PREVIEW&key={api_key}")
        return service
    except HttpError as error:
        print('An error occurred: %s' % error)
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
使用 python main.py 运行脚本。系统应提示您登录并同意 OAuth 范围。
创建分配
评分标准与作业(即 CourseWork)相关联,并且仅在 CourseWork 的上下文中才有意义。只有创建父级 CourseWork 项的 Google Cloud 项目才能创建评分标准。在本指南中,我们将创建一个包含脚本的新 CourseWork 作业。
将以下内容添加到 main.py 中:
def get_latest_course(service):
    """Retrieves the last created course."""
    try:
        response = service.courses().list(pageSize=1).execute()
        courses = response.get("courses", [])
        if not courses:
            print("No courses found. Did you remember to create one in the UI?")
            return
        course = courses[0]
        return course
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
def create_coursework(service, course_id):
    """Creates and returns a sample coursework."""
    try:
        coursework = {
            "title": "Romeo and Juliet analysis.",
            "description": """Write a paper arguing that Romeo and Juliet were
                                time travelers from the future.""",
            "workType": "ASSIGNMENT",
            "state": "PUBLISHED",
        }
        coursework = service.courses().courseWork().create(
            courseId=course_id, body=coursework).execute()
        return coursework
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
现在,更新 main.py 以检索您刚刚创建的测试类的 course_id、创建新的样本作业,并检索该作业的 coursework_id:
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    course = get_latest_course(service)
    course_id = course.get("id")
    course_name = course.get("name")
    print(f"'{course_name}' course ID: {course_id}")
    coursework = create_coursework(service, course_id)
    coursework_id = coursework.get("id")
    print(f"Assignment created with ID {coursework_id}")
    #TODO(developer): Save the printed course and coursework IDs.
保存 course_id 和 coursework_id。所有评分标准 CRUD 操作都需要这些字段。
您现在应该在 Google 课堂中看到一个示例 CourseWork。
 图 2. Google 课堂中示例作业的视图。
图 2. Google 课堂中示例作业的视图。
检查用户资格
如需创建和更新评分标准,提出请求的用户和相应课程的所有者都必须拥有 Google Workspace 教育 Plus 版许可。 Google 课堂支持用户资格端点,以便开发者确定用户可访问的功能。
更新并运行 main.py,以确认您的测试账号有权使用评分标准功能:
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()
    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
      print('User eligible for rubrics creation.')
创建评分准则
现在,您可以开始管理评分标准了。
您可以在 CourseWork 上创建评分标准,方法是调用包含完整评分标准对象的 create(),其中省略了条件和等级的 ID 属性(这些属性是在创建时生成的)。
将以下函数添加到 main.py 中:
def create_rubric(service, course_id, coursework_id):
    """Creates an example rubric on a coursework."""
    try:
        body = {
            "criteria": [
                {
                    "title": "Argument",
                    "description": "How well structured your argument is.",
                    "levels": [
                        {"title": "Convincing",
                         "description": "A compelling case is made.", "points": 30},
                        {"title": "Passable",
                         "description": "Missing some evidence.", "points": 20},
                        {"title": "Needs Work",
                         "description": "Not enough strong evidence..", "points": 0},
                    ]
                },
                {
                    "title": "Spelling",
                    "description": "How well you spelled all the words.",
                    "levels": [
                        {"title": "Perfect",
                         "description": "No mistakes.", "points": 20},
                        {"title": "Great",
                         "description": "A mistake or two.", "points": 15},
                        {"title": "Needs Work",
                         "description": "Many mistakes.", "points": 5},
                    ]
                },
                {
                    "title": "Grammar",
                    "description": "How grammatically correct your sentences are.",
                    "levels": [
                        {"title": "Perfect",
                         "description": "No mistakes.", "points": 20},
                        {"title": "Great",
                         "description": "A mistake or two.", "points": 15},
                        {"title": "Needs Work",
                         "description": "Many mistakes.", "points": 5},
                    ]
                },
            ]
        }
        rubric = service.courses().courseWork().rubrics().create(
            courseId=course_id, courseWorkId=coursework_id, body=body
            ).execute()
        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
然后,更新并运行 main.py 以创建示例评分标准,使用您之前获得的 Course 和 CourseWork ID:
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()
    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
      rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
      print(json.dumps(rubric, indent=4))
以下是有关评分标准表示法的一些说明:
- 评分标准和等级顺序会反映在 Google 课堂界面中。
- 有得分的级别(具有 points属性)必须按积分以升序或降序排序(不能随机排序)。
- 教师可以在界面中重新排序评分标准和得分等级(但不能重新排序未得分的等级),这会更改它们在数据中的顺序。
如需了解有关评分标准结构的更多注意事项,请参阅限制。
返回到界面后,您应该会在作业中看到评分标准。
 图 3. Google 课堂作业中的示例评分准则视图。
图 3. Google 课堂作业中的示例评分准则视图。
阅读评分准则
您可以使用标准 list() 和 get() 方法读取评分标准。
一个作业中最多只能有一个评分标准,因此 list() 看起来可能不太直观,但如果您还没有评分标准 ID,此方法会很有用。如果 CourseWork 没有关联的评分标准,则 list() 响应为空。
将以下函数添加到 main.py 中:
def get_rubric(service, course_id, coursework_id):
    """
    Get the rubric on a coursework. There can only be at most one.
    Returns null if there is no rubric.
    """
    try:
        response = service.courses().courseWork().rubrics().list(
            courseId=course_id, courseWorkId=coursework_id
            ).execute()
        rubrics = response.get("rubrics", [])
        if not rubrics:
            print("No rubric found for this assignment.")
            return
        rubric = rubrics[0]
        return rubric
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
更新并运行 main.py 以获取您添加的评分标准:
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(rubric, indent=4))
    #TODO(developer): Save the printed rubric ID.
请记下评分标准中的 id 属性,以便在后续步骤中使用。
如果您有评分标准 ID,Get() 会非常有用。在函数中使用 get() 可能如下所示:
def get_rubric(service, course_id, coursework_id, rubric_id):
    """
    Get the rubric on a coursework. There can only be at most one.
    Returns a 404 if there is no rubric.
    """
    try:
        rubric = service.courses().courseWork().rubrics().get(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id
        ).execute()
        return rubric
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
如果不存在评分标准,此实现会返回 404。
更新评分准则
对评分标准的更新通过 patch() 调用完成。由于评分标准的结构复杂,因此必须使用读取-修改-写入模式进行更新,其中整个 criteria 属性都会被替换。
更新规则如下:
- 未添加 ID 的条件或级别被视为添加。
- 之前存在的条件或级别缺失,则视为删除。
- 具有现有 ID 但数据已修改的条件或级别被视为修改。未修改的属性保持不变。
- 如果提供的条件或级别包含新的或未知的 ID,则视为错误。
- 新标准和级别的顺序被视为新界面顺序(但存在上述限制)。
添加一个用于更新评分标准的函数:
def update_rubric(service, course_id, coursework_id, rubric_id, body):
    """
    Updates the rubric on a coursework.
    """
    try:
        rubric = service.courses().courseWork().rubrics().patch(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id,
            body=body,
            updateMask='criteria'
        ).execute()
        return rubric
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
在此示例中,criteria 字段被指定为使用 updateMask 进行修改。
然后修改 main.py,以针对上述每条更新规则进行更改:
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()
    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
        # Get the latest rubric.
        rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
        criteria = rubric.get("criteria")
        """
        The "criteria" property should look like this:
        [
            {
                "id": "NkEyMdMyMzM2Nxkw",
                "title": "Argument",
                "description": "How well structured your argument is.",
                "levels": [
                    {
                        "id": "NkEyMdMyMzM2Nxkx",
                        "title": "Convincing",
                        "description": "A compelling case is made.",
                        "points": 30
                    },
                    {
                        "id": "NkEyMdMyMzM2Nxky",
                        "title": "Passable",
                        "description": "Missing some evidence.",
                        "points": 20
                    },
                    {
                        "id": "NkEyMdMyMzM2Nxkz",
                        "title": "Needs Work",
                        "description": "Not enough strong evidence..",
                        "points": 0
                    }
                ]
            },
            {
                "id": "NkEyMdMyMzM2Nxk0",
                "title": "Spelling",
                "description": "How well you spelled all the words.",
                "levels": [...]
            },
            {
                "id": "NkEyMdMyMzM2Nxk4",
                "title": "Grammar",
                "description": "How grammatically correct your sentences are.",
                "levels": [...]
            }
        ]
        """
        # Make edits. This example will make one of each type of change.
        # Add a new level to the first criteria. Levels must remain sorted by
        # points.
        new_level = {
            "title": "Profound",
            "description": "Truly unique insight.",
            "points": 50
        }
        criteria[0]["levels"].insert(0, new_level)
        # Remove the last criteria.
        del criteria[-1]
        # Update the criteria titles with numeric prefixes.
        for index, criterion in enumerate(criteria):
            criterion["title"] = f"{index}: {criterion['title']}"
        # Resort the levels from descending to ascending points.
        for criterion in criteria:
            criterion["levels"].sort(key=lambda level: level["points"])
        # Update the rubric with a patch call.
        new_rubric = update_rubric(
            service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID, YOUR_RUBRIC_ID, rubric)
        print(json.dumps(new_rubric, indent=4))
现在,教师应该可以在 Google 课堂中看到这些更改。
 图 4. 更新后的评分标准的视图。
图 4. 更新后的评分标准的视图。
查看使用评分准则评过分的提交内容
目前,API 无法使用评分准则为学生提交的内容评分,但您可以读取在 Google 课堂界面中使用评分准则评过分的提交内容的评分准则分数。
在 Google 课堂界面中,以学生的身份完成并提交示例作业。 然后,作为教师,您可以使用评分标准手动为作业评分。
 图 5. 教师在评分期间查看的评分准则。
图 5. 教师在评分期间查看的评分准则。
StudentSubmissions 且已使用评分标准评分的作业具有两个新属性:draftRubricGrades 和 assignedRubricGrades,分别表示教师在草稿和已分配评分状态下选择的分数和等级。
您可以使用现有的 studentSubmissions.get() 和 studentSubmissions.list() 方法来查看已评分的提交内容。
向 main.py 添加以下函数以列出学生提交的内容:
def get_latest_submission(service, course_id, coursework_id):
    """Retrieves the last submission for an assignment."""
    try:
        response = service.courses().courseWork().studentSubmissions().list(
            courseId = course_id,
            courseWorkId = coursework_id,
            pageSize=1
        ).execute()
        submissions = response.get("studentSubmissions", [])
        if not submissions:
            print(
                """No submissions found. Did you remember to turn in and grade
                   the assignment in the UI?""")
            return
        submission = submissions[0]
        return submission
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
然后,更新并运行 main.py 以查看提交作业的成绩。
if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)
    submission = get_latest_submission(
        service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(submission, indent=4))
draftRubricGrades 和 assignedRubricGrades 包含:
- 相应评分准则标准的 criterionId。
- 教师为每条评分标准分配的points。这可能来自所选的级别,但教师也可能已覆盖此设置。
- 为每条评分标准选择的等级的levelId。如果教师未选择等级,但仍为相应标准分配了分数,则此字段不存在。
这些列表仅包含教师选择等级或设置分数的条件对应的条目。例如,如果教师在评分期间选择仅与一个评分标准互动,即使评分标准包含多个评分标准,draftRubricGrades和 assignedRubricGrades 也只会包含一个项目。
删除评分准则
您可以使用标准 delete() 请求删除评分标准。以下代码显示了一个完整的示例函数,但由于评分已开始,您无法删除当前评分标准:
def delete_rubric(service, course_id, coursework_id, rubric_id):
    """Deletes the rubric on a coursework."""
    try:
        service.courses().courseWork().rubrics().delete(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id
        ).execute()
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error
导出和导入评分标准
评分准则可以手动导出到 Google 表格,供教师重复使用。
除了在代码中指定评分标准条件之外,还可以通过在评分标准正文中指定 sourceSpreadsheetId 而不是 criteria,从这些导出的工作表中创建和更新评分标准:
def create_rubric_from_sheet(service, course_id, coursework_id, sheet_id):
    """Creates an example rubric on a coursework."""
    try:
        body = {
            "sourceSpreadsheetId": sheet_id
        }
        rubric = service.courses().courseWork().rubrics().create(
            courseId=course_id, courseWorkId=coursework_id, body=body
            ).execute()
        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric
    except HttpError as error:
        print(f"An error occurred: {error}")
        return error