rubric — это шаблон, который преподаватели могут использовать при оценивании работ учащихся. API Classroom позволяет вам действовать от имени преподавателя, управляя этими рубриками, а также считывая оценки, выставленные учащимися по этим рубрикам.
Рисунок 1. Пример критериев оценки задания для работы в классе.
В этом руководстве объясняются основные понятия и функциональность API рубрик. См. эти статьи в Справочном центре, чтобы узнать об общей структуре рубрики и о том, как осуществляется выставление оценок в пользовательском интерфейсе Classroom.
Предварительные требования
В этом руководстве предполагается, что у вас есть следующее:
- Python 3.8.6 или более поздняя версия
- Инструмент управления пакетами pip
- Проект Google Cloud .
Для работы вам потребуется учетная запись Google Workspace for Education с включенным Google Classroom и назначенной лицензией Google Workspace for Education Plus . Если у вас ее нет, вы можете запросить расширенную демонстрационную учетную запись разработчика.
Тестовый класс, содержащий как минимум одну учетную запись тестового ученика. Если у вас нет класса Classroom, который можно использовать для тестирования, создайте его в пользовательском интерфейсе и добавьте тестового ученика .
Авторизация учетных данных для настольного приложения
Для аутентификации в качестве конечного пользователя и доступа к пользовательским данным в вашем приложении необходимо создать один или несколько идентификаторов клиента OAuth 2.0. Идентификатор клиента используется для идентификации отдельного приложения на серверах OAuth Google. Если ваше приложение работает на нескольких платформах, необходимо создать отдельный идентификатор клиента для каждой платформы.
- Перейдите на страницу «Учетные данные Google Cloud» в консоли Google Cloud.
- Нажмите «Создать учетные данные» > «Идентификатор клиента OAuth» .
- Выберите «Тип приложения» > «Настольное приложение» .
- В поле «Имя» введите имя для учетных данных. Это имя отображается только в консоли Google Cloud. Например, «Клиент Rubrics».
- Нажмите «Создать» . Появится экран создания клиента OAuth, на котором отобразятся ваш новый идентификатор клиента и секретный ключ клиента.
- Нажмите «Скачать JSON» , а затем «ОК» . Созданные учетные данные отобразятся в разделе «Идентификаторы клиентов OAuth 2.0».
- Сохраните загруженный JSON-файл как
credentials.jsonи переместите его в свою рабочую директорию. - Нажмите «Создать учетные данные» > «Ключ API» и запишите ключ API.
Подробнее см. раздел «Создание учетных данных доступа» .
Настройка областей действия OAuth
В зависимости от существующих в вашем проекте областей действия OAuth, вам может потребоваться настроить дополнительные области действия.
- Перейдите на экран подтверждения авторизации OAuth .
- Чтобы перейти к экрану «Осциллографы», нажмите «Редактировать приложение» > «Сохранить и продолжить» .
- Нажмите «Добавить или удалить области действия» .
- Добавьте следующие области видимости, если у вас их еще нет:
-
https://www.googleapis.com/auth/classroom.coursework.students -
https://www.googleapis.com/auth/classroom.courses
-
- Затем нажмите «Обновить» > «Сохранить и продолжить» > «Сохранить и продолжить» > «Вернуться на панель управления» .
Подробнее см. в разделе «Настройка экрана согласия OAuth» .
Область видимости classroom.coursework.students обеспечивает доступ на чтение и запись к критериям оценки (а также доступ к CourseWork ), а область видимости classroom.courses позволяет читать и писать курсы.
Требуемые для данного метода области действия указаны в справочной документации к этому методу. В качестве примера см. области действия авторизации courses.courseWork.rubrics.create . Все области действия Classroom можно посмотреть в разделе «Области действия 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 в Classroom.
Рисунок 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() , содержащей полный объект рубрики, при этом свойства ID для критериев и уровней опускаются (они генерируются при создании).
Добавьте следующую функцию в 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 целиком.
Правила обновления следующие:
- Критерии или уровни, добавленные без идентификатора, считаются дополнениями .
- Критерии или уровни, отсутствовавшие ранее, считаются удаленными .
- Критерии или уровни с существующим идентификатором, но измененными данными, считаются изменениями . Неизмененные свойства остаются без изменений.
- Критерии или уровни, для которых указаны новые или неизвестные идентификаторы, считаются ошибками .
- Порядок новых критериев и уровней считается новым порядком пользовательского интерфейса (с учетом вышеупомянутых ограничений ).
Добавить функцию для обновления критериев оценки:
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