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

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

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

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

Предпосылки

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

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

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

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

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

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

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

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

Просмотр рубрики в пользовательском интерфейсе Classroom Рисунок 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. Критерии или уровни с существующим ID, но измененными данными считаются правками . Неизмененные свойства остаются как есть.
  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, выполните и сдайте свой пример задания . Затем как учитель вручную оцените задание, используя критерий оценки .

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