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

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

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

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

Предварительные условия

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

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

Чтобы пройти аутентификацию в качестве конечного пользователя и получить доступ к пользовательским данным в вашем приложении, вам необходимо создать один или несколько идентификаторов клиента 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 областей авторизации . Вы можете увидеть все области действия Класса в областях OAuth 2.0 для Google API .

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

В своем рабочем каталоге установите клиентскую библиотеку 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. Вид примера задания в Классе.

Проверьте соответствие пользователя требованиям

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

Теперь изменения должны быть отражены для преподавателя в Классе.

Просмотр обновленной рубрики в пользовательском интерфейсе Класса Рисунок 4. Вид обновленной рубрики.

Просмотр материалов, оцененных по рубрикам

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

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

Просмотр оценок по критериям в пользовательском интерфейсе Класса Рисунок 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