ルーブリックを使ってみる

rubric は、教師が生徒の提出物を採点する際に使用できるテンプレートです。Classroom API を使用すると、教師に代わってこれらのルーブリックを管理できます。

Classroom UI でのルーブリックの表示 図 1. Classroom の課題のサンプル ルーブリックの表示。

このガイドでは、ルーブリック API の基本概念と機能について説明します。ルーブリックの一般的な構造と、Classroom UI でのルーブリックの評価方法については、ヘルプセンターの記事をご覧ください。

前提条件

このガイドは、以下のものがあることを前提としています。

デスクトップ アプリケーションの認証情報を承認する

エンドユーザーとして認証を行い、アプリ内でユーザーデータにアクセスするには、1 つ以上の OAuth 2.0 クライアント ID を作成する必要があります。クライアント ID は、Google の OAuth サーバーで個々のアプリを識別するために使用します。アプリが複数のプラットフォームで実行される場合は、プラットフォームごとに個別のクライアント ID を作成する必要があります。

  1. Google Cloud コンソールで Google Cloud の [認証情報] ページに移動します。
  2. [認証情報を作成] > [OAuth クライアント ID] をクリックします。
  3. [アプリケーションの種類] > [デスクトップ アプリ] をクリックします。
  4. [名前] フィールドに、認証情報の名前を入力します。この名前は Google Cloud コンソールにのみ表示されます。例: 「Rubrics Preview client」。
  5. [作成] をクリックします。[OAuth クライアントを作成しました] 画面が表示され、新しいクライアント ID とクライアント シークレットが表示されます。
  6. [JSON をダウンロード] をクリックし、[OK] をクリックします。新しく作成された認証情報が [OAuth 2.0 クライアント ID] の下に表示されます。
  7. ダウンロードした JSON ファイルを credentials.json として保存し、作業ディレクトリに移動します。
  8. [認証情報を作成] > [API キー] をクリックし、API キーをメモします。

詳細については、アクセス認証情報を作成するをご覧ください。

OAuth スコープを構成する

プロジェクトの既存の OAuth スコープによっては、追加のスコープの構成が必要になる場合があります。

  1. OAuth 同意画面に移動します。
  2. [アプリを編集] > [保存して次へ] をクリックして、[スコープ] 画面に移動します。
  3. [スコープを追加または削除] をクリックします。
  4. 次のスコープがまだない場合は追加します。
    • https://www.googleapis.com/auth/classroom.coursework.students
    • https://www.googleapis.com/auth/classroom.courses
  5. [更新] > [保存して次へ] > [保存して次へ] > [ダッシュボードに戻る] の順にクリックします。

詳細については、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_idcoursework_id を保存します。これらは、すべての評価基準の CRUD オペレーションに必要です。

これで、Classroom にサンプルの CourseWork が作成されます。

Classroom UI での課題の表示 図 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 に戻ると、課題に評価基準が表示されます。

Classroom UI でのルーブリックの表示 図 3. Classroom の課題のサンプル ルーブリックの表示。

ルーブリックを読み取る

評価基準は、標準の List メソッドと Get メソッドで読み取ることができます。

課題には最大 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 プロパティ全体が置き換えられます。

更新ルールは次のとおりです。

  1. ID なしで追加された条件またはレベルは、追加と見なされます。
  2. 以前に存在しなかった条件やレベルは、削除と見なされます。
  3. 既存の ID が設定されているがデータが変更されている基準またはレベルは、編集と見なされます。変更されていないプロパティはそのまま残ります。
  4. 新しい ID または不明な ID が指定された条件またはレベルは、エラーとみなされます。
  5. 新しい基準とレベルの順序が、新しい 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 の教師に反映されます。

Classroom の UI で更新されたルーブリック 図 4. 更新された評価基準の表示。

ルーブリックで採点された提出物を表示する

現時点では、生徒の提出物を API でルーブリックを使って採点することはできませんが、ルーブリックを使って採点された提出物のルーブリック成績は Classroom UI で確認できます。

Classroom の UI で、生徒として課題のサンプルを提出します。 次に、教師が手動でルーブリックを使用して課題を採点します。

Classroom UI でのルーブリック評価の表示 図 5. 採点中に教師に表示されるルーブリック。

ルーブリックで採点された生徒の提出物には、draftRubricGradesassignedRubricGrades という 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))

draftRubricGradesassignedRubricGrades には以下が含まれます。

  • 対応する評価基準の criterionId
  • 教師が各基準に割り当てた points。これは、選択したレベルによるものですが、教師が上書きした可能性もあります。
  • 各評価基準に選択したレベルの levelId。教師がレベルを選択しなかったが、基準にポイントを割り当てた場合は、このフィールドは表示されません。

これらのリストには、教師がレベルまたは設定点を選択した基準のエントリのみが含まれます。たとえば、教師が採点中に 1 つの基準のみを操作する場合、採点基準に多くの基準が含まれていても、draftRubricGradesassignedRubricGrades には 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

フィードバック

問題やご意見がございましたら、フィードバックをお寄せください。