添付ファイルの成績と成績のパスバック

これは Classroom アドオンに関する 6 番目のチュートリアルです 説明します。

このチュートリアルでは、前のステップの例を変更します 採点済みのアクティビティ タイプの添付ファイルを生成します。また、成績を返却します。 プログラムで Google Classroom にエクスポート(教師の成績に表示される) 仮成績として扱うこともできます

このチュートリアルは、このシリーズの他のチュートリアルとは若干異なり、 試験に合格するためのアプローチを 2 つ紹介します。 Classroom。どちらも開発者とユーザーに明確な影響を及ぼす 体験することClassroom アドオンを設計する際に、その両方を考慮してください。 その他の説明については、添付ファイルの操作ガイドのページをご覧ください。 実装オプションを確認します

なお、API の採点機能はオプションです。ラベルは activity-type アタッチメント

このチュートリアルでは、次のことを行います。

  • 以前の添付ファイル作成リクエストを Classroom API を使用して添付ファイルの点数も設定します。
  • 生徒の提出物にプログラムでスコアを付け、添付ファイルの 成績の分子。
  • 提出物の成績を Classroom では、教師のログイン認証情報またはオフライン認証情報を使用します。

採点が完了すると、Classroom の採点簿に成績が表示され、 パスバック動作が発生しますこれが発生する正確なタイミングは、 理解することが重要です。

この例では、前のタスクのアクティビティを再利用します。 チュートリアルでは、有名なランドマークの画像を受講者に見せて、 名前の入力を求められます。生徒が添付ファイルに完全な点数を割り当てます。 正しい名前を入力し、それ以外の場合はゼロを入力します。

Classroom アドオン API の採点機能を理解する

アドオンでは、課題の点数と満点の両方を設定できます 添付します。これらはそれぞれ pointsEarnedmaxPoints を使用して設定されます。 値を渡す必要があります。Classroom UI の添付ファイル カード。 maxPoints の値が設定されている場合にその値を返します。

maxPoints が 1 つのアタッチメントに複数のアタッチメントの例
割り当て

図 1. 課題作成 UI に 3 つのアドオン添付カードが表示されている maxPoints が設定されている。

Classroom アドオン API を使用すると、 付属品の成績で獲得したスコア。これらは、 課題の成績です。ただし、課題の成績設定は [成績の同期] ラベルがオンになっている添付ファイルの成績設定 添付ファイルカードが表示されます。[成績の同期] がpointsEarned添付ファイルのセット 生徒の課題に対する仮成績も設定されます。

通常は、割り当てに最初に追加された添付ファイルを maxPoints さんが「成績の同期」を受信します指定します。割り当て作成 UI の表示 「成績の同期」の例を図 1 に示します。指定します。注: 「添付ファイル 1」[成績の同期] が表示されます。課題の成績が 50 ポイントに更新されています。また、図 1 では、 3 つの添付ファイル カードが表示されているが、「成績の同期」が付いているカードは 1 枚のみ指定します。これは、 というのが現在の実装の主な制限です。1 つのアタッチメントのみが 「成績の同期」label に移動できます。

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 の値も保存します。 ローカルの Attachment データベースに エクスポートされます追加の API 呼び出しを行う必要がなくなります。 生徒の提出物を採点する ことができますただし、Google Chat の アドオンとは独立して課題の成績の設定が教師によって変更される。送信 courses.courseWork エンドポイントGET リクエストを送り、 課題レベルの maxPoints 値。その際、itemIdCourseWork.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()

これで添付ファイルの成績の上限に達しました。この動作をテストして、 今すぐ新しい課題に添付ファイルを追加し、添付ファイルカードが [成績の同期] が表示されます。課題の「点数」と役立ちます。

Classroom で生徒の提出物の成績を設定する

このセクションでは、添付ファイルの成績の分子の設定について説明します。つまり 添付ファイルに対する個々の生徒のスコア。そのためには、生徒を 送信の pointsEarned 値。

そこで、重要な決定を下すこととなりました。アドオンの pointsEarned を設定するリクエストは何ですか?

問題は、pointsEarned の設定に teacher OAuth スコープが必要であることです。 teacher スコープを生徒のユーザーに付与しないでください。その結果 アプリケーション コードの読み込みなど、生徒がアドオンを操作したときに予期しない動作が発生する 生徒用の表示の iframe ではなく、教師用の表示の iframeしたがって、2 つの pointsEarned の設定方法に関する選択肢は次のとおりです。

  • ログインしている教師の認証情報を使用する。
  • 保存されている(オフラインの)教師の認証情報を使用する。

以降のセクションでは、各アプローチのトレードオフについて説明します。 各実装のデモを行います。ここで紹介した例では、 Classroom に成績を渡すための両方の方法詳しくは、 適切なアプローチを選ぶ方法については、 次のサンプルを実行します。

Python

上部の SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS 宣言を見つけます。 /webapp/attachment_routes.py 個のファイル。返すには、この値を True に設定します ログインした教師の認証情報を使用して成績を分類します。この値を False に設定します。 生徒が課題を提出する際に、保存されている認証情報を使用して成績を返す できます。

ログインしている教師の認証情報を使用して成績を設定する

ログインしているユーザーの認証情報を使用して、pointsEarned を設定するリクエストを発行します。 これは実装の他の部分を反映しているため、直感的に理解できるはずです。 簡単に実現できます

ただし、教師は生徒の生徒とのみやり取りすることを考慮してください。 生徒の提出物の確認の iframe で提出できます。これには重要な 影響:

  • 教師が課題を受けるまで、Classroom に成績が入力されない できます。
  • 教師は生徒の提出物をすべて開き、すべてのデータを入力する必要がある 向上します
  • Classroom が成績を受け取るまでにわずかな遅延が発生する Classroom UI での表示。この遅延は、 通常は 5 ~ 10 秒ですが、30 秒になることもあります。

これらの要因が組み合わさると、教師は クラスの成績を全面的に反映させるという、かなりの時間のかかる手作業が必要になります。

このアプローチを実装するには、既存の受講者に API 呼び出しを 1 つ追加します。 ワーク レビューのルート。

生徒の提出物と添付ファイルのレコードを取得したら、以下を評価します。 成績を保存できます。採点するには AddOnAttachmentStudentSubmission オブジェクトpointsEarned フィールド。最後に、 PATCH リクエストを courses.courseWork.addOnAttachments.studentSubmissions エンドポイントと、 リクエスト本文の AddOnAttachmentStudentSubmission インスタンス。なお、 また、PATCH リクエストの updateMaskpointsEarned を指定する必要もあります。

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()

教師のオフライン用認証情報を使用して成績を設定する

2 つ目の方法では、保存されている認証情報を使用して成績を設定する 該当する教師のメールアドレスが表示されます。この実装では、次が必要です。 以前に承認された教師の更新と更新を使用して、認証情報を この認証情報を使用して pointsEarned を設定します。

このアプローチの大きな利点は、何もしなくても成績が入力されることです。 教師のアクションを Classroom UI 内で行い、問題を回避 上記をご覧ください。その結果、エンドユーザーは 実現できることですさらにこの方法では 教師が成績を返却する瞬間(生徒が課題を解き終えたときなど) 非同期で実行できます。

この方法を実装するには、次のタスクを完了します。

  1. アクセス トークンを保存するようにユーザー データベースのレコードを変更する。
  2. 添付ファイルのデータベース レコードを変更して教師 ID を保存する。
  3. 教師の認証情報を取得し、必要に応じて新しい認証情報を Classroom サービス インスタンス。
  4. 提出物の成績を設定する。

このデモでは、生徒の完了時に成績を設定します。 アクティビティこれは、生徒が生徒用ビューでフォームを送信したときです。 あります

アクセス トークンを保存するようにユーザー データベースのレコードを変更する

API を呼び出すには、更新トークンアクセス トークン。これまで一連のチュートリアルをご覧になっていれば、 User テーブル スキーマには更新トークンがすでに格納されているはずです。更新の保存 ログインしているユーザーでのみ API 呼び出しを行う場合は、 認証フローの一環としてアクセス トークンを受け取ります。

ただし、この場合はログインしているユーザーとは別のユーザーとして電話をかける必要があります。 つまり、認証フローが利用できません。そのため、既存のデータセットを 更新トークンと併用できます。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 をローカル 添付ファイルデータベースです次に、教師の認証情報を Google Cloud コンソールから attachmentId が生徒ビューの iframe に渡されます。

この例は最後のオプションを示しています。これは、 生徒の提出物を記録します。

データベースの Attachment テーブルに教師 ID フィールドを追加します。

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()

教師の認証情報を取得する

Student View iframe へのルートを確認します。保存した直後 ローカル データベースに生徒の回答を保存する場合は、教師の 認証情報を取得できます。これは単純明快なはずですが、 前の 2 つのステップで準備作業を行います。また、これらを使用して新しい 教師ユーザーの Classroom サービスのインスタンスです。

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 で提出できます。Chronicle の 評価が表示されるタイミングは、実装方法によって異なります。

  • 生徒がアクティビティを完了したときに成績を返却することを選択した場合は、 開かないうちに、UI に仮成績が表示されているはずです。 生徒の提出物の確認の iframe。生徒の一覧にも表示されます。 課題を開き、[成績] タブで[生徒の提出物] の横にある iframe を確認する。
  • 教師が生徒の提出物を開いたときに成績を返却することを選択した場合 iframe を確認すると、成績が [成績] タブに表示されます。プロンプトの iframe が読み込まれます。上記のとおり、この処理には最長で 30 秒ほどかかることがあります。 その後、特定の生徒の成績も Classroom の採点簿の他のビュー。

生徒に正しいスコアが表示されていることを確認します。

これで、次のステップ(添付ファイルの作成)に進む準備が整いました できます