rubric
是一种模板,教师可以在评分学生提交的作业时使用。借助 Classroom API,您可以代表教师管理这些评分标准,以及读取学生提交内容的评分标准成绩。
图 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 控制台中显示。例如“标题客户端”。
- 点击创建。系统随即会显示“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 范围中查看所有 Google 课堂范围。
配置示例
在工作目录中,安装 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 课堂中示例作业的视图。
检查用户是否符合条件
若要创建和更新评分标准,提交请求的用户和相应的课程所有者都必须已获配 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.')
创建评分准则
现在,您可以开始管理评分标准了。
您可以使用包含完整评分标准对象的 create()
调用在 CourseWork
上创建评分标准,其中省略了标准和等级的 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 课堂作业示例评分标准。
阅读评分准则
您可以使用标准的 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. 更新后的评分标准。
查看使用评分准则评分的提交内容
目前,API 无法使用评分准则为学生提交的内容评分,但您可以在 Google 课堂界面中读取已使用评分准则评分的提交内容的评分准则成绩。
以学生身份在 Google 课堂界面中,完成并上交示例作业。然后,作为教师,使用评分准则手动评分。
图 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