Начните работу с рубриками

rubric — это шаблон, который учителя могут использовать при оценке работ учащихся. API Classroom позволяет вам управлять этими критериями от имени учителя, а также просматривать оценки, выставленные учащимся.

Просмотр рубрики в интерфейсе класса Рисунок 1. Вид примера критерия оценки задания в классе.

В этом руководстве объясняются основные концепции и функции API рубрик. Ознакомьтесь со статьями Справочного центра, чтобы узнать об общей структуре рубрик и о том, как выполняется оценка по ним в интерфейсе Classroom.

Предпосылки

В этом руководстве предполагается, что у вас есть следующее:

Авторизация учетных данных для настольного приложения

Для аутентификации конечного пользователя и доступа к его данным в вашем приложении вам необходимо создать один или несколько идентификаторов клиента OAuth 2.0. Идентификатор клиента используется для идентификации одного приложения на серверах Google OAuth. Если ваше приложение работает на нескольких платформах, необходимо создать отдельный идентификатор клиента для каждой платформы.

  1. Перейдите на страницу учетных данных Google Cloud в консоли Google Cloud.
  2. Нажмите Создать учетные данные > Идентификатор клиента OAuth .
  3. Нажмите Тип приложения > Приложение для ПК .
  4. В поле «Имя» введите имя учётной записи. Оно отображается только в консоли Google Cloud. Например, «Клиент рубрик».
  5. Нажмите «Создать» . Появится экран создания клиента OAuth с вашим новым идентификатором клиента и секретным кодом клиента.
  6. Нажмите «Загрузить JSON» , а затем «ОК» . Новые учётные данные появятся в разделе «Идентификаторы клиентов OAuth 2.0».
  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 можно найти в разделе «Области действия OAuth 2.0 для API Google» .

Настройте образец

В вашем рабочем каталоге установите клиентскую библиотеку Google для Python:

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

Создайте файл main.py , который создает клиентскую библиотеку и авторизует пользователя, используя ваш ключ API вместо 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)

Запустите скрипт с помощью python main.py Вам будет предложено войти в систему и дать согласие на использование OAuth.

Создать задание

Критерий оценки связан с заданием ( CourseWork ) и имеет смысл только в контексте этого CourseWork . Критерии оценки могут быть созданы только в рамках проекта Google Cloud, создавшего родительский элемент CourseWork . Для целей данного руководства создайте новое задание 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 с рубриками.

Теперь у вас должен быть образец CourseWork в классе.

Просмотр задания в интерфейсе класса Рисунок 2. Вид примера задания в Classroom.

Проверить право пользователя

Для создания и обновления критериев оценки необходимо, чтобы как пользователь, подающий запрос, так и владелец соответствующего курса имели назначенную им лицензию Google Workspace for Education Plus . Classroom поддерживает конечную точку соответствия пользователей требованиям, позволяющую разработчикам определять, к каким возможностям у пользователя есть доступ.

Обновите и запустите main.py , чтобы убедиться, что ваша тестовая учетная запись имеет доступ к возможностям рубрик:

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

Создать рубрику

Теперь вы готовы приступить к управлению рубриками.

Рубрику можно создать в CourseWork с помощью вызова create() содержащего полный объект рубрики, в котором опущены свойства идентификатора для критериев и уровней (они генерируются при создании).

Добавьте следующую функцию в 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
            ).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 и CourseWork , полученные ранее:

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

Некоторые моменты относительно рубрикации представления:

  • Критерий и порядок уровней отражены в пользовательском интерфейсе класса.
  • Уровни с очками (те, которые имеют свойство points ) должны быть отсортированы по очкам либо в порядке возрастания, либо в порядке убывания (их нельзя упорядочивать случайным образом).
  • Преподаватели могут пересортировать критерии и оцененные уровни (но не неоцененные уровни) в пользовательском интерфейсе, и это изменяет их порядок в данных.

Дополнительные сведения о структуре рубрик см. в разделе «Ограничения» .

Вернувшись в пользовательский интерфейс, вы должны увидеть критерии оценки задания.

Просмотр рубрики в интерфейсе класса Рисунок 3. Вид образца критерия оценки задания в классе.

Прочитать рубрику

Рубрики можно считывать с помощью стандартных методов list() и get() .

В задании может быть не более одной рубрики, поэтому list() может показаться нелогичным, но это полезно, если у вас ещё нет идентификатора рубрики. Если рубрика, связанная с 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
            ).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() хорошо работает, когда у вас есть идентификатор рубрики. Использование 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
        ).execute()

        return rubric

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

Эта реализация возвращает ошибку 404, если критерий отсутствует.

Обновить рубрику

Обновления рубрики выполняются с помощью вызовов метода patch() . Из-за сложной структуры рубрики обновления должны выполняться по схеме «чтение-изменение-запись», при которой заменяется всё свойство criteria .

Правила обновления следующие:

  1. Критерии или уровни, добавленные без идентификатора, считаются добавлениями .
  2. Критерии или уровни, отсутствовавшие ранее, считаются удалениями .
  3. Критерии или уровни с существующим идентификатором, но изменёнными данными, считаются изменениями . Неизменённые свойства остаются как есть.
  4. Критерии или уровни, предоставленные с новыми или неизвестными идентификаторами, считаются ошибками .
  5. Порядок новых критериев и уровней считается новым порядком пользовательского интерфейса (с вышеупомянутыми ограничениями ).

Добавьте функцию обновления рубрики:

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

В этом примере поле criteria указано для изменения с помощью updateMask .

Затем измените main.py , чтобы внести изменения для каждого из вышеупомянутых правил обновления:

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

Изменения теперь должны быть отражены для учителя в Classroom.

Вид обновленной рубрики в интерфейсе класса Рисунок 4. Вид обновленной рубрики.

Просмотреть заявки, оцененные по рубрикам

На данный момент работы учащихся нельзя оценивать по критериям API, но вы можете ознакомиться с оценками по критериям для работ, оцененных по критериям в пользовательском интерфейсе класса.

Как учащийся, выполните и сдайте образец задания в интерфейсе «Класс». Затем, как учитель, вручную оцените задание, используя предложенные критерии .

Просмотр оценки по критериям в пользовательском интерфейсе Classroom Рисунок 5. Взгляд учителя на критерии оценки.

StudentSubmissions , оцененные с использованием рубрики, имеют два новых свойства: draftRubricGrades и assignedRubricGrades , представляющие собой баллы и уровни, выбранные преподавателем во время чернового и назначенного состояний оценки соответственно.

Для просмотра оцененных работ можно использовать существующие методы 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
        ).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 уровня, выбранного для каждого критерия. Если преподаватель не выбрал уровень, но всё же присвоил баллы по критерию, это поле отсутствует.

Эти списки содержат только записи для критериев, по которым учитель либо выбрал уровень, либо установил баллы. Например, если учитель выбирает для оценки только один критерий, draftRubricGrades и assignedRubricGrades будет только один элемент, даже если в рубрике много критериев.

Удалить рубрику

Критерий оценки можно удалить с помощью стандартного запроса 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
        ).execute()

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

Рубрики экспорта и импорта

Рубрики можно вручную экспортировать в таблицы Google для повторного использования учителями.

Помимо указания критериев рубрики в коде, можно создавать и обновлять рубрики из этих экспортированных таблиц, указав sourceSpreadsheetId в теле рубрики вместо 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