Nilai lampiran dan passback nilai

Ini adalah panduan keenam di add-on Classroom seri panduan kami.

Dalam panduan ini, Anda akan mengubah contoh dari langkah panduan sebelumnya untuk membuat lampiran jenis aktivitas yang dinilai. Anda juga meneruskan nilai kembali ke Google Classroom secara terprogram, yang muncul di nilai pengajar buku sebagai draf nilai.

Panduan ini sedikit berbeda dari yang lain dalam seri ini karena terdapat berikan dua kemungkinan pendekatan untuk mengembalikan nilai ke Classroom. Keduanya memiliki dampak yang berbeda pada developer dan pengguna pengalaman pengguna; pertimbangkan keduanya saat mendesain add-on Classroom. Baca halaman panduan Berinteraksi dengan lampiran untuk diskusi tambahan tentang opsi implementasinya.

Perhatikan bahwa fitur penilaian di API bersifat opsional. Model dasar dapat digunakan dengan lampiran jenis aktivitas apa pun.

Dalam panduan ini, Anda akan menyelesaikan hal-hal berikut:

  • Ubah permintaan pembuatan lampiran sebelumnya ke Classroom API untuk menetapkan penyebut nilai lampiran juga.
  • Beri nilai tugas yang dikirimkan siswa secara terprogram dan tetapkan pembilang nilai.
  • Terapkan dua pendekatan untuk lulus nilai kiriman kepada Classroom menggunakan kredensial pengajar login atau offline.

Setelah selesai, nilai akan muncul di buku nilai Classroom setelah perilaku passback dipicu. Waktu yang tepat ketika hal ini terjadi bergantung pada pendekatan implementasinya.

Untuk tujuan contoh ini, gunakan kembali aktivitas dari aktivitas sebelumnya panduan, di mana siswa melihat gambar tempat terkenal dan diminta untuk memasukkan namanya. Beri nilai penuh untuk lampiran jika siswa memasukkan nama yang benar, nol jika sebaliknya.

Memahami fitur penilaian add-on API Classroom

Add-on Anda dapat menyetel pembilang nilai dan penyebut nilai untuk lampiran. Keduanya ditetapkan menggunakan pointsEarned dan maxPoints di API. Kartu lampiran di UI Classroom ditampilkan nilai maxPoints saat telah ditetapkan.

Contoh beberapa lampiran dengan maxPoints di satu lampiran
penetapan

Gambar 1. UI pembuatan tugas dengan tiga kartu lampiran add-on yang memiliki maxPoints yang disetel.

Classroom add-ons API memungkinkan Anda mengonfigurasi setelan dan skor yang diperoleh untuk nilai lampiran. Ini tidak sama dengan tugas. Namun, setelan nilai tugas mengikuti setelan nilai lampiran dari lampiran dengan label Sinkronisasi nilai kartu lampirannya. Kapan "Sinkronisasi nilai" kumpulan lampiran pointsEarned untuk kiriman siswa, hal ini juga menetapkan draf nilai tugas siswa.

Biasanya, lampiran pertama yang ditambahkan ke tugas yang menyetel maxPoints menerima "Sinkronisasi nilai" label. Lihat UI pembuatan tugas contoh yang ditunjukkan pada Gambar 1 sebagai contoh "Sinkronisasi nilai" label. Perlu diketahui bahwa "Lampiran 1" kartu memiliki "Sinkronisasi nilai" label dan bahwa tugas menilai di kotak merah telah diperbarui menjadi 50 poin. Perhatikan juga bahwa meskipun Gambar 1 menampilkan tiga kartu lampiran, hanya satu kartu yang memiliki "Sinkronisasi nilai" label. Ini adalah batasan utama dari penerapan saat ini: hanya satu lampiran yang boleh memiliki "Sinkronisasi nilai" label.

Jika ada beberapa lampiran yang telah menetapkan maxPoints, menghapus lampiran dengan "Sinkronisasi nilai" tidak mengaktifkan "Sinkronisasi nilai" di salah satu lampiran yang tersisa. Menambahkan lampiran lain yang menyetel maxPoints memungkinkan Sinkronisasi Nilai pada lampiran baru, dan nilai tugas maksimum akan disesuaikan dengan yang cocok. Tidak ada mekanisme untuk secara terprogram melihat lampiran mana yang memiliki "Sinkronisasi nilai" label atau untuk melihat berapa banyak lampiran yang dimiliki tugas tertentu.

Menetapkan nilai maksimum lampiran

Bagian ini menjelaskan cara menetapkan penyebut untuk nilai lampiran; sehingga adalah, skor maksimum yang dapat dicapai semua siswa untuk kiriman. Untuk melakukannya, setel nilai maxPoints lampiran.

Hanya diperlukan perubahan kecil pada implementasi yang ada untuk memungkinkan berbagai fitur penilaian. Saat membuat lampiran, tambahkan nilai maxPoints di objek AddOnAttachment yang sama yang berisi studentWorkReviewUri, teacherViewUri, dan kolom lampiran lainnya.

Perhatikan bahwa skor maksimum default untuk tugas baru adalah 100. Saran kami menyetel maxPoints ke nilai selain 100 sehingga Anda dapat memverifikasi bahwa nilai disetel dengan benar. Tetapkan maxPoints ke 50 sebagai demonstrasi:

Python

Tambahkan kolom maxPoints saat membuat objek attachment, cukup sebelum mengeluarkan permintaan CREATE ke endpoint courses.courseWork.addOnAttachments. Anda dapat menemukannya di webapp/attachment_routes.py jika mengikuti contoh yang kami berikan.

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}",
}

Untuk tujuan demonstrasi ini, Anda juga menyimpan nilai maxPoints di basis data Lampiran lokal Anda; sehingga Anda tidak perlu melakukan panggilan API tambahan nanti ketika menilai tugas yang dikirimkan siswa. Namun, ada kemungkinan pengajar mengubah setelan nilai tugas secara terpisah dari add-on Anda. Kirim permintaan GET ke endpoint courses.courseWork untuk melihat nilai maxPoints tingkat tugas. Saat melakukannya, teruskan itemId di Kolom CourseWork.id.

Sekarang perbarui model database Anda agar juga menyimpan nilai maxPoints lampiran. Sebaiknya gunakan nilai maxPoints dari respons CREATE:

Python

Pertama, tambahkan kolom max_points ke tabel Attachment. Anda dapat menemukannya dalam file webapp/models.py jika mengikuti contoh yang kami berikan.

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

Kembali ke permintaan courses.courseWork.addOnAttachments CREATE. Menyimpan nilai maxPoints yang ditampilkan dalam respons.

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

Lampiran sekarang memiliki nilai maksimum. Anda seharusnya dapat menguji perilaku ini sekarang; menambahkan lampiran ke tugas baru, dan mengamati bahwa kartu lampiran menunjukkan "Sinkronisasi nilai" label dan "Poin" tugas perubahan nilai.

Menetapkan nilai kiriman siswa di Classroom

Bagian ini menjelaskan cara menetapkan pembilang untuk nilai lampiran; yaitu, skor masing-masing siswa untuk lampiran tersebut. Untuk melakukannya, tetapkan siswa nilai pointsEarned kiriman.

Anda kini memiliki keputusan penting yang harus dibuat: bagaimana seharusnya add-on mengeluarkan minta untuk menetapkan pointsEarned?

Masalahnya adalah, menyetel pointsEarned memerlukan cakupan OAuth teacher. Anda tidak boleh memberikan cakupan teacher kepada pengguna siswa; hal ini bisa mengakibatkan perilaku yang tidak terduga saat siswa berinteraksi dengan add-on Anda, seperti memuat iframe Tampilan Pengajar, bukan iframe Tampilan Siswa. Oleh karena itu Anda memiliki dua pilihan tentang cara menetapkan pointsEarned:

  • Menggunakan kredensial pengajar yang login.
  • Menggunakan kredensial pengajar yang disimpan (offline).

Bagian berikut membahas konsekuensi dari setiap pendekatan sebelum yang mendemonstrasikan setiap implementasinya. Perhatikan bahwa contoh yang kami berikan menunjukkan kedua pendekatan untuk memberikan nilai ke Classroom; lihat petunjuk khusus bahasa di bawah ini untuk melihat cara memilih pendekatan saat menjalankan contoh yang diberikan:

Python

Temukan deklarasi SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS di bagian atas dari webapp/attachment_routes.py file. Setel nilai ini ke True untuk diteruskan kembali nilai menggunakan kredensial pengajar yang login. Tetapkan nilai ini ke False untuk meneruskan nilai menggunakan kredensial yang disimpan. Ketika siswa menyerahkan aktivitas Anda.

Menetapkan nilai menggunakan kredensial pengajar yang login

Gunakan kredensial pengguna yang login untuk mengajukan permintaan guna menyetel pointsEarned. Ini seharusnya tampak cukup intuitif karena mencerminkan seluruh implementasi sejauh ini, dan hanya perlu upaya untuk mewujudkannya.

Namun, pertimbangkan bahwa pengajar hanya berinteraksi dengan bahasa siswa kiriman dalam iframe Ulasan Tugas Siswa. Hal ini memiliki beberapa implikasi:

  • Tidak ada nilai yang terisi di Classroom hingga pengajar mengerjakannya tindakan di UI Classroom.
  • Pengajar mungkin harus membuka setiap kiriman siswa untuk mengisi semua nilai siswa.
  • Ada penundaan singkat antara penerimaan nilai di Classroom dan tampilannya di UI Classroom. Keterlambatannya adalah biasanya lima sampai sepuluh detik, tetapi bisa sampai 30 detik.

Kombinasi faktor-faktor ini berarti bahwa guru mungkin harus pekerjaan manual yang memakan waktu dan cukup banyak untuk mengisi sepenuhnya nilai kelas.

Untuk menerapkan pendekatan ini, tambahkan satu panggilan API tambahan ke Siswa yang ada Rute Peninjauan Pekerjaan.

Setelah mengambil catatan lampiran dan kiriman siswa, evaluasi kiriman siswa dan menyimpan nilai yang dihasilkan. Tetapkan nilai di Kolom pointsEarned dari objek AddOnAttachmentStudentSubmission. Terakhir, kirimkan permintaan PATCH ke endpoint courses.courseWork.addOnAttachments.studentSubmissions dengan Instance AddOnAttachmentStudentSubmission dalam isi permintaan. Perhatikan bahwa kita juga harus menentukan pointsEarned dalam updateMask dalam permintaan PATCH kita:

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

Menetapkan nilai menggunakan kredensial pengajar offline

Pendekatan kedua untuk menetapkan nilai mengharuskan penggunaan kredensial tersimpan untuk pengajar yang membuat lampiran tersebut. Penerapan ini mengharuskan Anda membuat kredensial menggunakan {i>refresh<i} pengajar yang diotorisasi sebelumnya dan token akses, lalu gunakan kredensial ini untuk menetapkan pointsEarned.

Keuntungan penting dari pendekatan ini adalah nilai diisi tanpa memerlukan tindakan pengajar di UI Classroom, menghindari masalah yang disebutkan di atas. Hasilnya, pengguna akhir mempersepsikan pengalaman penilaian lancar dan efisien. Selain itu, pendekatan ini memungkinkan Anda untuk memilih momen di mana Anda lulus kembali nilai, seperti saat siswa menyelesaikan aktivitas atau secara asinkron.

Selesaikan tugas berikut untuk menerapkan pendekatan ini:

  1. Mengubah catatan database Pengguna untuk menyimpan token akses.
  2. Mengubah kumpulan data Lampiran untuk menyimpan ID pengajar.
  3. Ambil kredensial pengajar dan (secara opsional) buat kredensial baru Instance layanan Classroom.
  4. Menetapkan nilai kiriman.

Untuk tujuan demonstrasi ini, tetapkan nilai saat siswa menyelesaikan aktivitas tersebut; artinya, ketika siswa mengirimkan formulir dalam Tampilan Siswa rute perjalanan.

Mengubah record database Pengguna untuk menyimpan token akses

Dua token unik diperlukan untuk melakukan panggilan API, yaitu token refresh dan token akses. Jika Anda telah mengikuti rangkaian panduan sejauh ini, Skema tabel User seharusnya sudah menyimpan token refresh. Menyimpan pembaruan token ini sudah cukup jika Anda hanya melakukan panggilan API dengan pengguna yang sudah login, Anda menerima token akses sebagai bagian dari alur otentikasi.

Namun, sekarang Anda perlu melakukan panggilan sebagai seseorang selain pengguna yang login, artinya aliran otentikasi tidak tersedia. Dengan demikian, Anda perlu menyimpan token akses bersama token refresh. Perbarui skema tabel User Anda untuk menyertakan token akses:

Python

Dalam contoh yang kami berikan, nilainya ada dalam file 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())

Kemudian, perbarui kode apa pun yang membuat atau memperbarui data User untuk menyimpan juga token akses:

Python

Dalam contoh yang kami berikan, nilainya ada dalam file 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()

Mengubah data database Lampiran untuk menyimpan ID pengajar

Untuk menetapkan nilai aktivitas, lakukan panggilan untuk menetapkan pointsEarned sebagai pengajar dalam kursus ini. Ada beberapa cara untuk melakukannya:

  • Simpan pemetaan lokal kredensial pengajar ke ID mata pelajaran. Namun, perhatikan guru yang sama mungkin tidak selalu terkait dengan mata pelajaran tertentu.
  • Kirimkan permintaan GET ke endpoint courses Classroom API untuk mengetahui pengajar saat ini. Kemudian, lakukan kueri catatan pengguna lokal untuk menemukan kredensial pengajar yang cocok.
  • Saat membuat lampiran add-on, simpan ID pengajar di file lokal database lampiran. Kemudian, ambil kredensial pengajar dari attachmentId diteruskan ke iframe Tampilan Siswa.

Contoh ini mendemonstrasikan opsi terakhir karena Anda menyetel nilai ketika siswa menyelesaikan lampiran aktivitas.

Tambahkan kolom ID pengajar ke tabel Attachment database Anda:

Python

Dalam contoh yang kami berikan, nilainya ada dalam file 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))

Kemudian, perbarui kode apa pun yang membuat atau memperbarui data Attachment agar juga simpan ID kreator:

Python

Dalam contoh yang kami berikan, ini ada dalam metode create_attachments di File 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()

Mengambil kredensial pengajar

Temukan rute yang menayangkan iframe Tampilan Siswa. Segera setelah menyimpan respons siswa di database lokal Anda, ambil respons pengajar kredensial dari penyimpanan lokal Anda. Seharusnya tidak sulit, mengingat persiapan dalam dua langkah sebelumnya. Anda juga dapat menggunakannya untuk membangun instance layanan Classroom untuk pengguna pengajar:

Python

Dalam contoh yang kami berikan, ini ada dalam metode load_activity_attachment di file 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)

Menetapkan nilai kiriman

Prosedur di sini sama dengan Prosedur menggunakan kredensial. Namun, perhatikan bahwa Anda harus melakukan panggilan dengan pengajar kredensial yang diambil di langkah sebelumnya:

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

Menguji add-on

Serupa dengan panduan sebelumnya, buat tugas dengan jenis aktivitas lampiran sebagai pengajar, mengirimkan respons sebagai siswa, lalu membuka kiriman dalam iframe Ulasan Tugas Siswa. Anda akan dapat melihat nilai muncul pada waktu yang berbeda bergantung pada pendekatan penerapan Anda:

  • Jika Anda memilih untuk melewati nilai saat siswa menyelesaikan aktivitas itu, Anda seharusnya sudah melihat draf nilainya di UI sebelum membuka iframe Ulasan Tugas Siswa. Anda juga dapat melihatnya di daftar siswa saat membuka tugas, dan di "Nilai" kotak di samping Tugas Siswa Tinjau iframe.
  • Jika Anda memilih untuk meneruskan nilai saat pengajar membuka Tugas Siswa Tinjau iframe, nilai seharusnya muncul di "Nilai" kotak segera setelah iframe dimuat. Seperti disebutkan di atas, proses ini dapat memerlukan waktu hingga 30 detik. Setelah itu, nilai untuk siswa tersebut juga akan muncul di bagian tampilan buku nilai Classroom lainnya.

Pastikan skor yang benar muncul untuk siswa tersebut.

Selamat! Anda siap untuk melanjutkan ke langkah berikutnya: membuat lampiran di luar Google Classroom.