这是关于 Google 课堂插件的第六个演示 演示系列视频
在本演示中,您将修改上一个演示步骤中的示例 来生成已评分的活动类型附件。您还发回了成绩 以编程方式上传到 Google 课堂,这些信息会显示在教师的成绩中 将图书保存为草稿成绩。
本演示与系列中的其他演示略有不同, 介绍了两种可能的方法将成绩传回 课堂。两者对开发者和用户有着截然不同的影响 体验;在设计 Google 课堂插件时,请考虑这两种情况。 请阅读我们的“与附件交互”指南页面, 实现选项。
请注意,API 中的评分功能是可选的。它们适用于 任何活动类型的附件。
在本演示中,您将完成以下操作:
- 将之前的附件创建请求修改为 Classroom API 还可设置附件的成绩分母。
- 以编程方式为学生提交的作业评分,并设置附件的 成绩分子。
- 采用两种方法将作业的成绩传递给 使用已登录或离线教师凭据的 Google 课堂。
完成后,成绩会显示在“课堂”成绩册中, 就会触发回传行为这种情况发生的确切时间取决于 实施方法。
在本示例中,重复使用上一个 activity 中的 activity 视频中,学生会看到著名地标的图片, 系统提示用户输入其名称如果学生符合以下条件,请为附件分配满分: 输入正确的名称,否则输入 0。
了解 Google 课堂插件 API 评分功能
您的插件可以为
附件。它们分别使用 pointsEarned
和 maxPoints
进行设置
值。Google 课堂界面中的附件卡片显示了
maxPoints
值(如果设置了该值)。
图 1. 作业创建界面,其中显示了三张插件附件卡片,
设置了 maxPoints
。
借助 Classroom 插件 API,您可以配置和使用
附件成绩的得分。这些与
作业的成绩。不过,作业成绩设置遵循的是
针对已启用成绩同步标签的附件的附件成绩设置
附件卡片。当系统显示“成绩同步”将 pointsEarned
的附件设置为
学生提交作业时,系统还会设置学生的作业初始成绩。
通常情况下,添加到作业中的第一个附件将设置
maxPoints
会收到“成绩同步”标签。查看作业创建界面
图 1 所示的“成绩同步”示例标签。请注意,
“附件 1”“成绩同步”卡片标签和作业成绩
已更新为 50 分。另请注意,虽然图 1
显示了三张附件卡片,只有一张卡片具有“成绩同步”标签。这是
当前实施的一个关键限制:一个附件只能有
“成绩同步”标签。
如果有多个附件设置了 maxPoints
,请移除
带有“成绩同步”的附件不会启用“成绩同步”在任意
剩余附件。添加其他设置 maxPoints
的附件会启用
系统会同步新附件的成绩,作业最高成绩将调整为
匹配。没有以编程方式查看哪个附件具有
“成绩同步”标签,也可以查看特定分配关系包含多少个附件。
设置附件的最高成绩
本部分介绍如何为附件成绩设置“分母”;那个
是指所有学生都能在各自的
提交内容。为此,请设置附件的 maxPoints
值。
只需对现有实施稍作修改即可启用
以及评分功能创建附件时,将 maxPoints
值添加到
同一个包含 studentWorkReviewUri
的 AddOnAttachment
对象,
teacherViewUri
和其他附件字段。
请注意,新作业的默认最高得分为 100。我们建议
将 maxPoints
设置为 100 以外的值,以便验证
正确设置成绩。将 maxPoints
设置为 50 作为演示:
Python
在构建 attachment
对象时添加 maxPoints
字段,只需
然后再向CREATE
courses.courseWork.addOnAttachments
端点。您可以在
webapp/attachment_routes.py
文件(如果遵循我们提供的示例)。
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.
"studentWorkReviewUri": {
"uri":
flask.url_for(
"view_submission", _scheme='https', _external=True)
},
# Sets the maximum points that a student can earn for this activity.
# This is the denominator in a fractional representation of a grade.
"maxPoints": 50,
# The title of the attachment.
"title": f"Attachment {attachment_count}",
}
在本演示中,您还需要将 maxPoints
值存储在
您的本地附件数据库;因此无需进行额外的 API 调用
以便日后对学生提交的作业进行评分但请注意
教师可以独立于插件更改作业成绩设置。发送
向 courses.courseWork
端点发出 GET
请求,以查看
maxPoints
值。执行此操作时,请将 itemId
传入
CourseWork.id
字段。
现在,更新您的数据库模型,以同时保存附件的 maxPoints
值。
我们建议使用 CREATE
响应中的 maxPoints
值:
Python
首先,向 Attachment
表添加 max_points
字段。您可以
添加到 webapp/models.py
文件中。
# Database model to represent an attachment.
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))
# The maximum number of points for this activity.
max_points = db.Column(db.Integer)
返回到 courses.courseWork.addOnAttachments
CREATE
请求。实体店
响应中返回的 maxPoints
值。
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,
# Store the maxPoints value returned in the response.
max_points=int(resp.get("maxPoints")))
db.session.add(new_attachment)
db.session.commit()
该附件现已达到最高成绩。您应该能够测试此行为 现在;向新作业中添加附件,并观察附件卡片是否 会显示“成绩同步”标签和作业的“积分”值更改。
在 Google 课堂中设置学生提交作业的成绩
本部分将介绍如何为附件成绩设置分子;也就是
单个学生的附件得分。为此,请先设置一个学生
提交内容的 pointsEarned
值。
现在,您需要做出一个重要决定:您的插件应如何
请求设置 pointsEarned
?
问题在于设置 pointsEarned
需要 teacher
OAuth 范围。
您不应向学生用户授予 teacher
范围;这可能会导致
学生与您的插件互动时会出现意外行为,例如加载
教师视图 iframe,而不是学生视图 iframe。因此,你有两个
设置 pointsEarned
的可选方案:
- 使用已登录教师的凭据。
- 使用存储的(离线)教师凭据。
以下部分讨论了每种方法的利弊, 来演示每种实现方式请注意,我们提供的示例仅 两种将成绩传递给 Google 课堂的方法;请参阅 下面的具体说明,了解如何在出现以下情况时选择方法: 运行所提供的示例:
Python
在顶部找到 SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS
声明
共 webapp/attachment_routes.py
个文件。将此值设置为 True
即可回传
使用已登录教师的凭据进行评分。将此值设置为 False
在学生提交
活动。
使用已登录教师的凭据设置成绩
使用已登录用户的凭据发出设置 pointsEarned
的请求。
这看起来应该非常直观,因为它反映了我们的
几乎不费力就可以实现
但请注意,教师只与学生的课程互动。 学生作业回顾 iframe 中提交的内容。其中有一些重要的 影响:
- 在教师参加前,Google 课堂中不会填充任何成绩 操作。
- 教师可能需要打开学生提交的每项内容, 学生成绩。
- Google 课堂收到成绩后会有短暂的延迟 及其在 Google 课堂界面中的外观。延迟时间为 通常为 5 到 10 秒,但最长可长达 30 秒。
这些因素结合在一起,就意味着教师 填充课程的成绩所需的大量耗时的手动工作。
如需实现此方法,请向现有 Student 额外添加一个 API 调用 “工作审核”路由。
提取学生提交内容和附件记录后,请评估
并存储最终成绩。在
AddOnAttachmentStudentSubmission
对象的 pointsEarned
字段。最后,
将PATCH
请求发送到
courses.courseWork.addOnAttachments.studentSubmissions
端点,
AddOnAttachmentStudentSubmission
实例。请注意,
还需要在 PATCH
请求的 updateMask
中指定 pointsEarned
:
Python
# 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)
grade = 0
# See if the student response matches the stored name.
if student_submission.student_response.lower(
) == attachment.image_caption.lower():
grade = attachment.max_points
# Create an instance of the Classroom service.
classroom_service = ch._credential_handler.get_classroom_service()
# Build an AddOnAttachmentStudentSubmission instance.
add_on_attachment_student_submission = {
# Specifies the student's score for this attachment.
"pointsEarned": grade,
}
# Issue a PATCH request to set the grade numerator for this attachment.
patch_grade_response = classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"],
attachmentId=flask.session["attachmentId"],
submissionId=flask.session["submissionId"],
# updateMask is a list of fields being modified.
updateMask="pointsEarned",
body=add_on_attachment_student_submission).execute()
使用离线教师凭据设置成绩
第二种设置成绩的方法需要使用存储的凭据
创建该附件的教师。此实现要求
您使用之前获得授权的教师刷新过的文件来构建凭据,
访问令牌,然后使用这些凭据设置 pointsEarned
。
这种方法的一个关键优势是,无需请求即可填充成绩 教师操作,避免出现问题 如上所述。结果就是最终用户感知到的评分体验 无缝、高效。此外,您还可以通过此方法 您发回成绩的时刻,例如在学生完成 或异步发生。
如需实现此方法,请完成以下任务:
- 修改用户数据库记录以存储访问令牌。
- 修改附件数据库记录以存储教师 ID。
- 检索教师的凭据,并(可选)构建新的 课堂服务实例。
- 设置提交内容的成绩。
在本演示中,请在学生完成时设置成绩 相应活动;也就是说,学生在“学生视图”中提交表单 。
修改用户数据库记录以存储访问令牌
要进行 API 调用,需要两个唯一的令牌:刷新令牌和
访问令牌。如果您到目前为止一直在按演示系列视频进行操作,
User
表架构应已存储刷新令牌。存储刷新
令牌就足够了,
您在身份验证流程中会收到一个访问令牌。
但是,您现在需要以
表示身份验证流程不可用。因此,您需要存储
访问令牌和刷新令牌将 User
表架构更新为
添加访问令牌:
Python
在我们提供的示例中,它位于 webapp/models.py
文件中。
# Database model to represent a user.
class User(db.Model):
# The user's identifying information:
id = db.Column(db.String(120), primary_key=True)
display_name = db.Column(db.String(80))
email = db.Column(db.String(120), unique=True)
portrait_url = db.Column(db.Text())
# The user's refresh token, which will be used to obtain an access token.
# Note that refresh tokens will become invalid if:
# - The refresh token has not been used for six months.
# - The user revokes your app's access permissions.
# - The user changes passwords.
# - The user belongs to a Google Cloud organization
# that has session control policies in effect.
refresh_token = db.Column(db.Text())
# An access token for this user.
access_token = db.Column(db.Text())
然后,更新创建或更新 User
记录的所有代码,以同时存储
访问令牌:
Python
在我们提供的示例中,它位于 webapp/credential_handler.py
文件中。
def save_credentials_to_storage(self, credentials):
# Issue a request for the user's profile details.
user_info_service = googleapiclient.discovery.build(
serviceName="oauth2", version="v2", credentials=credentials)
user_info = user_info_service.userinfo().get().execute()
flask.session["username"] = user_info.get("name")
flask.session["login_hint"] = user_info.get("id")
# See if we have any stored credentials for this user. If they have used
# the add-on before, we should have received login_hint in the query
# parameters.
existing_user = self.get_credentials_from_storage(user_info.get("id"))
# If we do have stored credentials, update the database.
if existing_user:
if user_info:
existing_user.id = user_info.get("id")
existing_user.display_name = user_info.get("name")
existing_user.email = user_info.get("email")
existing_user.portrait_url = user_info.get("picture")
if credentials and credentials.refresh_token is not None:
existing_user.refresh_token = credentials.refresh_token
# Update the access token.
existing_user.access_token = credentials.token
# If not, this must be a new user, so add a new entry to the database.
else:
new_user = User(
id=user_info.get("id"),
display_name=user_info.get("name"),
email=user_info.get("email"),
portrait_url=user_info.get("picture"),
refresh_token=credentials.refresh_token,
# Store the access token as well.
access_token=credentials.token)
db.session.add(new_user)
db.session.commit()
修改附件数据库记录以存储教师 ID
要为活动设置成绩,请调用将 pointsEarned
设置为
教师。您可以通过以下几种方法完成此操作:
- 存储教师凭据与课程 ID 的本地映射。但请注意 同一位教师未必总会与某门课程相关联。
- 向 Classroom API
courses
端点发出GET
个请求,以 获取当前教师。然后,查询本地用户记录以查找 匹配的教师凭据。 - 创建插件附件时,请将教师 ID 存储在本地
附件数据库。然后,从
attachmentId
传递给了学生视图 iframe。
此示例展示了最后一个选项, 学生完成活动附件。
将教师 ID 字段添加到数据库的 Attachment
表中:
Python
在我们提供的示例中,它位于 webapp/models.py
文件中。
# Database model to represent an attachment.
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))
# The maximum number of points for this activity.
max_points = db.Column(db.Integer)
# The ID of the teacher that created the attachment.
teacher_id = db.Column(db.String(120))
然后,将创建或更新 Attachment
记录的所有代码更新为:
存储创建者的 ID:
Python
在我们提供的示例中,它位于create_attachments
webapp/attachment_routes.py
文件。
# Store the attachment 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,
max_points=int(resp.get("maxPoints")),
teacher_id=flask.session["login_hint"])
db.session.add(new_attachment)
db.session.commit()
检索教师的凭据
找到投放学生视图 iframe 的路线。存储后立即 在本地数据库中检索学生的回复,检索老师的 存储凭据鉴于 为在前面两个步骤做准备。您还可以使用它们来构建新的 教师用户的 Google 课堂服务实例:
Python
在我们提供的示例中,它位于 load_activity_attachment
方法中的
webapp/attachment_routes.py
文件。
# Create an instance of the Classroom service using the tokens for the
# teacher that created the attachment.
# We're assuming that there are already credentials in the session, which
# should be true given that we are adding this within the Student View
# route; we must have had valid credentials for the student to reach this
# point. The student credentials will be valid to construct a Classroom
# service for another user except for the tokens.
if not flask.session.get("credentials"):
raise ValueError(
"No credentials found in session for the requested user.")
# Make a copy of the student credentials so we don't modify the original.
teacher_credentials_dict = deepcopy(flask.session.get("credentials"))
# Retrieve the requested user's stored record.
teacher_record = User.query.get(attachment.teacher_id)
# Apply the user's tokens to the copied credentials.
teacher_credentials_dict["refresh_token"] = teacher_record.refresh_token
teacher_credentials_dict["token"] = teacher_record.access_token
# Construct a temporary credentials object.
teacher_credentials = google.oauth2.credentials.Credentials(
**teacher_credentials_dict)
# Refresh the credentials if necessary; we don't know when this teacher last
# made a call.
if teacher_credentials.expired:
teacher_credentials.refresh(Request())
# Request the Classroom service for the specified user.
teacher_classroom_service = googleapiclient.discovery.build(
serviceName=CLASSROOM_API_SERVICE_NAME,
version=CLASSROOM_API_VERSION,
credentials=teacher_credentials)
设置提交内容的成绩
此处的步骤与使用已登录教师的 凭据。但请注意,与教师进行通话时 在上一步中检索到的凭据:
Python
# Issue a PATCH request as the teacher to set the grade numerator for this
# attachment.
patch_grade_response = teacher_classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"],
attachmentId=flask.session["attachmentId"],
submissionId=flask.session["submissionId"],
# updateMask is a list of fields being modified.
updateMask="pointsEarned",
body=add_on_attachment_student_submission).execute()
测试插件
与上一个演示类似,使用活动类型创建作业 以教师身份添加附件,以学生身份提交回复,然后打开 学生作业回顾 iframe 中提交的内容。您应该能够看到 根据实施方法的不同显示不同的时间:
- 如果您选择在学生完成活动时发回成绩, 在打开 学生作业回顾 iframe。您也可以在遇到以下情况时在学生列表中看到它: 打开相应作业,然后在“成绩”部分学生的作业旁边的复选框 查看 iframe。
- 如果您在教师打开“学生的作业”时选择发回成绩 查看 iframe,成绩应显示在“成绩”中该框 iframe 加载。如上文所述,此过程最多可能需要 30 秒。 此后,特定学生的成绩也应显示在 其他 Google 课堂成绩册视图。
确认系统为学生显示正确的分数。
恭喜!您已准备好继续执行下一步:创建附件 Google 课堂之外。