rubric
は、教師が生徒の提出物を採点する際に使用できるテンプレートです。Classroom API を使用すると、教師に代わってルーブリックを管理できます。
図 1. Classroom の課題にあるサンプル ルーブリックの画面。
このガイドでは、ルーブリック API の基本概念と機能について説明します。ルーブリックの一般的な構造と、Classroom UI でのルーブリック採点の仕組みについては、ヘルプセンターの記事をご覧ください。
前提条件
このガイドは、次のものがあることを前提としています。
- Python 3.8.6 以降
- pip パッケージ管理ツール
- Google Cloud プロジェクト。
- Google Classroom が有効になっている Google Workspace for Education アカウント。
- 1 つ以上のテスト生徒アカウントがあるテストクラス。テストに使用できる Classroom クラスがない場合は、UI でクラスを作成し、テスト用生徒を追加します。
デスクトップ アプリケーションの認証情報を承認する
エンドユーザーとして認証を行い、アプリ内でユーザーデータにアクセスするには、1 つ以上の OAuth 2.0 クライアント ID を作成する必要があります。クライアント ID は、Google の OAuth サーバーで 1 つのアプリを識別するために使用されます。アプリを複数のプラットフォームで実行する場合は、プラットフォームごとに個別のクライアント ID を作成する必要があります。
- Google Cloud コンソールで GCP の [認証情報] ページに移動します。
- [認証情報を作成] > [OAuth クライアント ID] をクリックします。
- [アプリケーションの種類] > [デスクトップ アプリ] をクリックします。
- [名前] フィールドに、認証情報の名前を入力します。この名前は Google Cloud コンソールにのみ表示されます。例: 「ルーブリック プレビュー クライアント」。
- [作成] をクリックします。OAuth クライアントの作成画面が表示され、新しいクライアント ID とクライアント シークレットが表示されます。
- [JSON をダウンロード]、[OK] の順にクリックします。新しく作成された認証情報が [OAuth 2.0 クライアント ID] に表示されます。
- ダウンロードした JSON ファイルを
credentials.json
として保存し、ファイルを作業ディレクトリに移動します。 - [認証情報を作成] > [API キー] をクリックし、API キーをメモします。
詳細については、アクセス認証情報を作成するをご覧ください。
OAuth スコープを構成する
プロジェクトの既存の OAuth スコープによっては、追加のスコープを構成する必要があります。
- OAuth 同意画面に移動します。
- [Edit App] > [Save and Continue] をクリックして、[Scopes] 画面に移動します。
- [Add or Remove Scopes] をクリックします。
- 次のスコープをまだ追加していない場合は追加します。
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
認可スコープをご覧ください。Classroom のすべてのスコープについては、Google API 用の OAuth 2.0 スコープをご覧ください。API はまだプレビュー版であるため、ルーブリックについてはここでは説明しません。
サンプルを構成する
作業ディレクトリに Python 用 Google クライアント ライブラリをインストールします。
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
クライアント ライブラリをビルドし、YOUR_API_KEY
の代わりに API キーを使用してユーザーを認可する main.py
というファイルを作成します。
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 操作で必要になります。
これで、Classroom にサンプル CourseWork
が作成されました。
図 2. Classroom で表示されたサンプル課題の画面。
ルーブリックを作成する
これでルーブリックを管理する準備が整いました。
ルーブリック全体を含む 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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).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
ID と CourseWork
ID を使用してサンプル ルーブリックを作成します。
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
print(json.dumps(rubric, indent=4))
ルーブリックの表示に関するいくつかのポイントは次のとおりです。
- 基準とレベルの順序が Classroom の UI に反映されます。
- スコア付きレベル(
points
プロパティを含むレベル)は、昇順または降順に並べ替える必要があります(ランダムに並べ替えることはできません)。 - 教師は、UI で基準とスコアレベル(スコアなしレベルを除く)を並べ替えることができ、それによってデータ内の順序を変更することができます。
ルーブリックの構造に関する注意事項については、制限事項をご覧ください。
UI に戻ると、課題のルーブリックが表示されます。
図 3. Classroom の課題にあるサンプル ルーブリックの画面。
ルーブリックを読む
ルーブリックは、標準の List
メソッドと Get
メソッドで読み取ることができます。
1 つの課題に指定できるルーブリックは 1 つまでなので、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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).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
プロパティに留意してください。
Get
は、ルーブリック ID がある場合に役立ちます。代わりに関数で 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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).execute()
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
この実装では、ルーブリックがない場合、404 が返されます。
ルーブリックを更新する
ルーブリックを更新するには、Patch
呼び出しを使用します。ルーブリックの構造が複雑なため、criteria
プロパティ全体を置き換えて、読み取り、変更、書き込みのパターンで更新する必要があります。
更新ルールは次のとおりです。
- ID なしで追加された条件またはレベルは追加と見なされます。
- 前に存在しない条件またはレベルは削除とみなされます。
- 既存の ID があるがデータが修正されている条件またはレベルは、編集とみなされます。変更されていないプロパティはそのまま残ります。
- 新しい ID または不明な ID が指定された条件またはレベルは、エラーと見なされます。
- 新しい基準とレベルの順序は、新しい UI の順序とみなされます(前述の制限事項があります)。
ルーブリックを更新する関数を追加します。
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',
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).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)
# 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))
これで、Classroom で教師に変更が反映されます。
図 4. 更新されたルーブリックのビュー。
ルーブリックで採点した提出物を表示する
現時点では、API でルーブリックを使用して生徒の提出物を採点することはできませんが、ルーブリックを使用して採点された提出物については、Classroom UI でルーブリックの成績を読み取ることができます。
生徒として Classroom の UI でサンプルの課題を提出します。 次に、教師がルーブリックを使って手動で課題を採点します。
図 5. 教師による採点中のルーブリックの表示
ルーブリックを使用して採点された生徒の提出物には、draftRubricGrades
と assignedRubricGrades
という 2 つの新しいプロパティがあります。これらは、それぞれ下書き中に教師が選択した点とレベル、および割り当てられた採点状態を表します。
また、ルーブリックが関連付けられている生徒の提出物には、採点前でも rubricId
フィールドが含まれます。これは、CourseWork
に関連付けられた最新のルーブリックを表します。教師がルーブリックを削除してから再作成すると、この値は変更される可能性があります。
既存の 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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).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
。教師がレベルを選択していないにもかかわらず、評価基準の点が割り当てられている場合、このフィールドは表示されません。
これらのリストには、教師がレベルまたは点数を選択した基準に対応するエントリのみが含まれます。たとえば、教師が採点時に 1 つの基準のみを操作するように選択した場合、ルーブリックに多くの基準があっても、draftRubricGrades
と assignedRubricGrades
には 1 つの項目しか含まれません。
ルーブリックを削除する
ルーブリックは、標準の 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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).execute()
except HttpError as error:
print(f"An error occurred: {error}")
return error
ルーブリックのエクスポートとインポート
ルーブリックは、教師が Google スプレッドシートに手動でエクスポートして再利用できます。
コード内でルーブリックの条件を指定するだけでなく、ルーブリックの本文で criteria
ではなく sourceSpreadsheetId
を指定することで、エクスポートされたシートからルーブリックを作成および更新することもできます。
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,
# Specify the preview version. Rubrics CRUD capabilities are
# supported in V1_20231110_PREVIEW and later.
previewVersion="V1_20231110_PREVIEW"
).execute()
print(f"Rubric created with ID {rubric.get('id')}")
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
フィードバック
問題が見つかった場合やご意見がありましたら、フィードバックをお送りください。