Wprowadzenie do ocen cząstkowych

rubric to szablon, którego nauczyciele mogą używać do oceniania prac uczniów. Interfejs Classroom API umożliwia Ci działanie w imieniu nauczyciela w celu zarządzania tymi kartami, a także odczytywanie ocen z kart w przesłanych przez uczniów pracach.

Wygląd oceny cząstkowej w interfejsie Classroom Rysunek 1. Przykład oceny cząstkowej w projekcie w Classroom

Ten przewodnik wyjaśnia podstawowe pojęcia i funkcje interfejsu Rubrics API. Zapoznaj się z tymi artykułami w Centrum pomocy, aby dowiedzieć się więcej o ogólnej strukturze karty oceny i o tym, jak ocenić kartę w interfejsie Classroom.

Wymagania wstępne

W tym przewodniku przyjęto założenie, że masz:

Autoryzowanie danych logowania do aplikacji na komputer

Aby uwierzytelnić się jako użytkownik i uzyskać dostęp do danych użytkownika w aplikacji, musisz utworzyć co najmniej 1 identyfikator klienta OAuth 2.0. Identyfikator klienta wskazuje konkretną aplikację na serwerach OAuth Google. Jeśli Twoja aplikacja działa na kilku platformach, musisz utworzyć osobny identyfikator klienta dla każdej z nich.

  1. W konsoli Google Cloud otwórz stronę Dane logowania.
  2. Kliknij Utwórz dane logowania > Identyfikator klienta OAuth.
  3. Kliknij Typ aplikacji > Aplikacja na komputer.
  4. W polu Nazwa wpisz nazwę danych logowania. Ta nazwa jest wyświetlana tylko w konsoli Google Cloud. Na przykład „Klient Rubrics”.
  5. Kliknij Utwórz. Pojawi się ekran z nowym klientem OAuth, na którym wyświetlane są nowy identyfikator klienta i tajny klucz klienta.
  6. Kliknij Pobierz plik JSON, a następnie OK. Nowo utworzone dane logowania pojawią się w sekcji Identyfikatory klienta OAuth 2.0.
  7. Zapisz pobrany plik JSON jako credentials.json i przenieś go do katalogu roboczego.
  8. Kliknij Utwórz dane logowania > Klucz interfejsu API i zapisz klucz interfejsu API.

Więcej informacji znajdziesz w artykule Tworzenie danych logowania.

Konfigurowanie zakresów OAuth

W zależności od zakresów OAuth projektu może być konieczne skonfigurowanie dodatkowych zakresów.

  1. Przejdź do ekranu zgody OAuth.
  2. Aby otworzyć ekran Zakresy, kliknij Edytuj aplikację > Zapisz i kontynuuj.
  3. Kliknij Dodaj lub usuń zakresy.
  4. Jeśli nie masz tych uprawnień, dodaj je:
    • https://www.googleapis.com/auth/classroom.coursework.students
    • https://www.googleapis.com/auth/classroom.courses
  5. Następnie kliknij Aktualizuj > Zapisz i kontynuuj > Zapisz i kontynuuj > Powrót do panelu.

Więcej informacji znajdziesz w artykule Konfigurowanie ekranu zgody OAuth.

Zakres classroom.coursework.students umożliwia odczyt i zapis kryteriów oceny (oraz dostęp do CourseWork), a zakres classroom.courses umożliwia odczyt i zapis kursów.

Zakresy wymagane w przypadku danej metody są wymienione w dokumentacji referencyjnej dotyczącej tej metody. Przykładem są courses.courseWork.rubrics.createzakresy autoryzacji. Wszystkie zakresy Classroom znajdziesz w sekcji Zakresy OAuth 2.0 dla interfejsów API Google.

Konfigurowanie próbki

W katalogu roboczym zainstaluj bibliotekę klienta Google dla Pythona:

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

Utwórz plik o nazwie main.py, który tworzy bibliotekę klienta i uwierzytelnia użytkownika, używając klucza API zamiast YOUR_API_KEY:

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)

Uruchom skrypt za pomocą python main.py. Powinien pojawić się komunikat z prośbą o zalogowanie się i wyrażenie zgody na zakresy uprawnień OAuth.

Utwórz zadanie

Rubryka jest powiązana z projektem lub CourseWork i ma znaczenie tylko w kontekście tego CourseWork. Klasyfikacje może tworzyć tylko projekt Google Cloud, który utworzył element nadrzędny CourseWork. Na potrzeby tego przewodnika utwórz nowe zadanie CourseWork ze skryptem.

Dodaj do pliku main.py te elementy:

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

Zaktualizuj teraz main.py, aby pobrać course_id klasy testu, którą właśnie utworzyłeś/utworzyłaś. Utwórz nowy przykładowy projekt i pobierz jego 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.

Zapisz course_idcoursework_id. Są one potrzebne do wszystkich operacji CRUD dotyczących kryteriów.

W Classroom powinien teraz pojawić się przykładowy CourseWork.

Wygląd projektu w interfejsie Classroom Rysunek 2. Widok przykładowego projektu w Classroom

Sprawdzanie, czy użytkownik spełnia wymagania

Aby tworzyć i aktualizować kryteria, zarówno użytkownik przesyłający prośbę, jak i właściciel kursu muszą mieć przypisaną licencję Google Workspace for Education Plus. Classroom obsługuje punkt końcowy dotyczący dostępności, aby umożliwić deweloperom określenie funkcji, do których ma dostęp użytkownik.

Zaktualizuj i uruchom main.py, aby sprawdzić, czy konto testowe ma dostęp do kryteriów:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()

    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
      print('User eligible for rubrics creation.')

Tworzenie oceny cząstkowej

Teraz możesz zacząć zarządzać schematami oceniania.

Oceny cząstkowej można utworzyć w usługach CourseWork za pomocą wywołania create() zawierającego pełny obiekt oceny cząstkowej, w którym pominięto właściwości identyfikatora dla kryteriów i poziomów (są one generowane podczas tworzenia).

Dodaj do pliku main.py tę funkcję:

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
            ).execute()
        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

Następnie zaktualizuj i uruchom main.py, aby utworzyć przykładową kartę oceny, używając wcześniejszych identyfikatorów CourseCourseWork:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()

    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
      rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
      print(json.dumps(rubric, indent=4))

Kilka informacji o reprezentowaniu kryteriów:

  • Kolejność kryteriów i poziomów jest odzwierciedlona w interfejsie Classroom.
  • Poziomy punktacji (te z właściwością points) muszą być posortowane według punktów w kolejności rosnącej lub malejącej (nie mogą być posortowane losowo).
  • Nauczyciele mogą ponownie sortować kryteria i poziomy z punktacją (ale nie bezpunktowe poziomy) w interfejsie, co zmienia ich kolejność w danych.

Więcej informacji o ograniczeniach dotyczących struktury ocen cząstkowych znajdziesz w sekcji Ograniczenia.

W interfejsie powinna się wyświetlić karta oceny projektu.

Wygląd oceny cząstkowej w interfejsie Classroom Rysunek 3. Przykład oceny cząstkowej w projekcie w Classroom

Czytanie oceny cząstkowej

Oceny cząstkowe można odczytać za pomocą standardowych metod list() i get().

W zadaniu może być maksymalnie jedna karta, więc list() może wydawać się nieintuicyjna, ale jest przydatna, jeśli nie masz jeszcze identyfikatora karty. Jeśli nie ma kryteriów powiązanych z CourseWork, odpowiedź list() będzie pusta.

Dodaj do pliku main.py tę funkcję:

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

Zaktualizuj i uruchom main.py, aby pobrać dodaną ocenę cząstkową:

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.

Zapisz w swoim kryterium usługę id, aby wykorzystać ją w kolejnych krokach.

Get() działa dobrze, gdy masz identyfikator kryteriów. Użycie funkcji get() może wyglądać tak:

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

        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

Ta implementacja zwraca kod 404, jeśli nie ma żadnej rubryki.

Aktualizowanie oceny cząstkowej

Zmiany w metrykach wprowadza się za pomocą wywołań patch(). Ze względu na złożoną strukturę kryteriów aktualizacje muszą być wykonywane za pomocą wzorca odczyt-modyfikuj-zapisz, w którym zastępowana jest cała właściwość criteria.

Reguły aktualizacji:

  1. Kryteria lub poziomy dodane bez identyfikatora są uważane za dodatki.
  2. Kryteria lub poziomy, których brak w poprzedniej wersji, są uważane za usunięte.
  3. Kryteria lub poziomy z istniejącym identyfikatorem, ale ze zmienionymi danymi są uznawane za modyfikacje. Niezmienione właściwości pozostają bez zmian.
  4. Kryteria lub poziomy podane z nowymi lub nieznanymi identyfikatorami są uznawane za błędy.
  5. Kolejność nowych kryteriów i poziomów jest uznawana za nową kolejność w interfejsie (z wymienionymi wyżej ograniczeniami).

Dodaj funkcję aktualizowania kryteriów:

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'
        ).execute()

        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

W tym przykładzie pole criteria jest oznaczone do modyfikacji za pomocą parametru updateMask.

Następnie zmień main.py, aby wprowadzić zmianę w przypadku każdej z wymienionych wyżej reguł aktualizacji:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    capability = service.userProfiles().checkUserCapability(
        userId='me',
        # Specify the preview version. checkUserCapability is
        # supported in V1_20240930_PREVIEW and later.
        previewVersion="V1_20240930_PREVIEW",
        capability="CREATE_RUBRIC").execute()

    if not capability.get('allowed'):
      print('User ineligible for rubrics creation.')
      # TODO(developer): in a production app, this signal could be used to
      # proactively hide any rubrics related features from users or encourage
      # them to upgrade to the appropriate license.
    else:
        # 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))

Zmiany powinny być teraz widoczne dla nauczyciela w Classroom.

Wygląd zaktualizowanej tabeli oceniania w interfejsie Classroom Rysunek 4. Wyświetlanie zaktualizowanej tabeli

Wyświetlanie zadań ocenionych za pomocą kryteriów

Obecnie interfejs API nie umożliwia oceniania zadań uczniów za pomocą ocen cząstkowych, ale możesz odczytać oceny cząstkowe zadań, które zostały ocenione za pomocą oceny cząstkowej w interfejsie Classroom.

Jako uczeń w interfejsie Classroom wykonaj i oddaj przykładowy projekt. Następnie jako nauczyciel ręcznie oceń projekt za pomocą kryteriów.

Wyświetlanie oceny cząstkowej w interfejsie Classroom Rysunek 5. Widok oceny cząstkowej dla nauczyciela podczas oceniania.

StudentSubmissions, które zostały ocenione za pomocą kryteriów, mają 2 nowe właściwości: draftRubricGrades i assignedRubricGrades, które odpowiadają odpowiednio punktom i poziomom wybranym przez nauczyciela w stanie projektu i przypisanej oceny.

Do wyświetlania ocenionych prac możesz używać dotychczasowych metod studentSubmissions.get() i studentSubmissions.list().

Dodaj do main.py tę funkcję, aby wyświetlić listę zadań przesłanych przez uczniów:

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

Następnie zaktualizuj i uruchom main.py, aby wyświetlić oceny przesłanych danych.

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 i assignedRubricGrades zawierają:

  • criterionId odpowiednich kryteriów oceny cząstkowej.
  • points przypisany przez nauczyciela do każdego kryterium. Może to być spowodowane wybranym poziomem, ale nauczyciel mógł też nadpisać tę wartość.
  • levelId poziomu wybranego dla każdego kryterium. Jeśli nauczyciel nie wybrał poziomu, ale przypisał punkty za kryterium, to pole nie będzie widoczne.

Te listy zawierają tylko wpisy dotyczące kryteriów, w których nauczyciel wybrał poziom lub ustawił punkty. Jeśli na przykład nauczyciel zdecyduje się podczas oceniania na interakcję tylko z jednym kryterium, draftRubricGrades i assignedRubricGrades będą zawierać tylko jeden element, nawet jeśli kryteria kryteriów jest wiele.

Usuwanie oceny cząstkowej

Kryteria można usunąć za pomocą standardowego żądania delete(). Poniższy kod pokazuje przykładową funkcję, ale ponieważ ocena już się rozpoczęła, nie możesz usunąć bieżącej oceny cząstkowej:

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

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

Eksportowanie i importowanie ocen cząstkowych

Oceny cząstkowe można ręcznie eksportować do Arkuszy Google, aby nauczyciele mogli ich używać ponownie.

Oprócz określania kryteriów oceny cząstkowej w kodzie możesz tworzyć i aktualizować oceny cząstkowe na podstawie tych wyeksportowanych arkuszy, określając w treści oceny cząstkowej wartość sourceSpreadsheetId zamiast wartości criteria:

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

        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error