Uma rubric
é um modelo que os professores podem usar para avaliar as
entregas dos estudantes. A API Classroom permite que você aja em nome do
professor para gerenciar essas rubricas e ler as notas das rubricas nos
envios dos alunos.
Figura 1. Exemplo de rubrica em uma atividade do Google Sala de Aula.
Este guia explica os conceitos básicos e a funcionalidade da API Rubrics. Consulte estes artigos da Central de Ajuda para saber mais sobre a estrutura geral de uma rubrica e como a avaliação de rubricas é feita na interface do Google Sala de Aula.
Pré-requisitos
Este guia pressupõe que você já tenha:
- Python 3.8.6 ou mais recente
- A ferramenta de gerenciamento de pacotes pip
- um projeto do Google Cloud;
- Uma conta do Google Workspace for Education com o Google Sala de Aula ativado e uma licença do Google Workspace for Education Plus atribuída a ela. Você pode solicitar uma conta de demonstração de desenvolvedor com upgrade se não tiver uma.
- Uma turma de teste com pelo menos uma conta de estudante de teste. Se você não tiver uma turma do Google Sala de Aula para testar, crie uma na interface e adicione um estudante de teste.
Autorizar credenciais para um aplicativo para computador
Para fazer a autenticação como usuário final e acessar os dados do usuário no app, crie um ou mais IDs do cliente OAuth 2.0. Um ID do cliente é usado para identificar um único app nos servidores OAuth do Google. Se o app for executado em várias plataformas, crie um ID do cliente separado para cada plataforma.
- Navegue até a página de credenciais do Google Cloud no console do Google Cloud.
- Clique em Criar credenciais > ID do cliente OAuth.
- Clique em Tipo de aplicativo > App para computador.
- No campo Nome, digite um nome para a credencial. Esse nome é mostrado apenas no console do Google Cloud. Por exemplo, "Rubrics client".
- Clique em Criar. A tela do cliente OAuth criado aparece, mostrando o novo ID e a chave secreta do cliente.
- Clique em Fazer o download do JSON e depois em OK. A credencial recém-criada aparece em "IDs do cliente OAuth 2.0".
- Salve o arquivo JSON salvo como
credentials.json
e mova-o para seu diretório de trabalho. - Clique em Criar credenciais > Chave de API e anote a chave de API.
Consulte Criar credenciais de acesso para saber mais.
Configurar escopos do OAuth
Dependendo dos escopos OAuth atuais do seu projeto, talvez seja necessário configurar escopos adicionais.
- Navegue até a tela de consentimento do OAuth.
- Clique em Editar app > Salvar e continuar para acessar a tela "Escopos".
- Clique em Adicionar ou remover escopos.
- Adicione os seguintes escopos, se ainda não tiver:
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.courses
- Em seguida, clique em "Atualizar" > Salvar e continuar > Salvar e continuar > Voltar ao painel.
Consulte Configurar a tela de consentimento OAuth para saber mais.
O escopo classroom.coursework.students
permite acesso de leitura e gravação a
rúbricas (junto com o acesso a CourseWork
), e o escopo classroom.courses
permite ler e gravar cursos.
Os escopos necessários para um determinado método estão listados na documentação de referência
do método. Consulte Escopos de autorização courses.courseWork.rubrics.create
como exemplo. Confira todos os escopos do Classroom em Escopos do OAuth 2.0 para APIs
do Google.
Configurar a amostra
No diretório de trabalho, instale a biblioteca de cliente do Google para Python:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
Crie um arquivo chamado main.py
que gere a biblioteca de cliente e autorize o
usuário, usando a chave de API no lugar de 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)
Execute o script usando python main.py
. Você vai receber uma solicitação para fazer login e
consentir com os escopos do OAuth.
Criar uma atividade
Uma rubrica é associada a uma tarefa, ou CourseWork
, e só é
relevante no contexto dessa CourseWork
. As rubricas só podem ser criadas pelo
projeto do Google Cloud que criou o item CourseWork
pai. Para os
fins deste guia, crie uma nova atribuição CourseWork
com um script.
Adicione o seguinte a 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
Agora atualize main.py
para extrair o course_id
da classe de teste que você acabou de
criar, crie uma nova atividade de exemplo e extraia a
coursework_id
da atividade:
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.
Salve o course_id
e o coursework_id
. Elas são necessárias para todas as operações CRUD
de rubricas.
Agora você tem um CourseWork
de exemplo no Google Sala de Aula.
Figura 2. Exemplo de atividade no Google Sala de Aula.
Verificar a qualificação do usuário
Para criar e atualizar rubricas, o usuário que faz a solicitação e o proprietário do curso correspondente precisam ter uma licença do Google Workspace for Education Plus atribuída a eles. O Google Sala de Aula oferece suporte a um endpoint de qualificação do usuário para que os desenvolvedores possam determinar os recursos a que o usuário tem acesso.
Atualize e execute main.py
para confirmar que a conta de teste tem acesso à
capacidade de rubricas:
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.')
Criar uma rubrica
Agora você já pode começar a gerenciar rubricas.
Uma rubrica pode ser criada em um CourseWork
com uma chamada create()
contendo
o objeto completo da rubrica, em que as propriedades de ID para critérios e níveis são
omitidas (elas são geradas na criação).
Adicione a seguinte função a 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
Em seguida, atualize e execute main.py
para criar a rubrica de exemplo usando os IDs Course
e CourseWork
anteriores:
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))
Alguns pontos sobre a representação da rubrica:
- A ordem do critério e do nível é refletida na interface do Google Sala de Aula.
- Os níveis pontuados (aqueles com a propriedade
points
) precisam ser classificados por pontos em ordem crescente ou decrescente (não podem ser ordenados aleatoriamente). - Os professores podem classificar novamente os critérios e os níveis com pontuação (mas não os níveis sem pontuação) na interface, e isso muda a ordem dos dados.
Consulte as limitações para mais informações sobre a estrutura das rubricas.
Na interface, você vai encontrar a rubrica na atividade.
Figura 3. Exemplo de rubrica em uma atividade do Google Sala de Aula.
Ler uma rubrica
As rubricas podem ser lidas com os métodos padrão list()
e get()
.
Uma atividade pode ter no máximo uma rubrica. Por isso, list()
pode parecer
não ser intuitivo, mas é útil se você ainda não tiver o ID da rubrica. Se não
houver uma rubrica associada a uma CourseWork
, a resposta list()
vai estar vazia.
Adicione a seguinte função a 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
Atualize e execute main.py
para buscar a rubrica que você adicionou:
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.
Anote a propriedade id
na rubrica para etapas posteriores.
Get()
funciona bem quando você tem o ID da rubrica. O uso de get()
na função
pode ter esta aparência:
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
Essa implementação retorna um 404 se não houver uma rubrica.
Atualizar uma rubrica
As atualizações de uma rubrica são feitas com chamadas patch()
. Devido à estrutura
complexa de uma rubrica, as atualizações precisam ser feitas com um padrão de leitura-modificação-gravação,
em que toda a propriedade criteria
é substituída.
As regras de atualização são as seguintes:
- Os critérios ou níveis adicionados sem um ID são considerados adicionais.
- Os critérios ou níveis ausentes de antes são considerados exclusões.
- Os critérios ou níveis com um ID existente, mas dados modificados são considerados edições. As propriedades não modificadas são deixadas como estão.
- Os critérios ou níveis fornecidos com IDs novos ou desconhecidos são considerados erros.
- A ordem dos novos critérios e níveis é considerada a nova ordem da interface (com as limitações mencionadas).
Adicione uma função para atualizar uma rubrica:
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
Neste exemplo, o campo criteria
é especificado para modificação com um
updateMask
.
Em seguida, modifique main.py
para fazer uma mudança em cada uma das regras de atualização
mencionadas:
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))
As mudanças vão aparecer para o professor no Google Sala de Aula.
Figura 4. Visualização da rubrica atualizada.
Conferir trabalhos enviados com nota
Por enquanto, os envios dos estudantes não podem ser avaliados com uma rubrica pela API, mas você pode ler as notas de rubrica dos envios que foram avaliados com uma rubrica na interface do Google Sala de Aula.
Como estudante na interface do Google Sala de Aula, conclua e envie a atividade de exemplo. Em seguida, como professor, avalie a atividade manualmente usando a rubrica.
Figura 5. Visualização da rubrica pelo professor durante a avaliação.
As StudentSubmissions
que foram avaliadas com uma rubrica têm duas novas
propriedades: draftRubricGrades
e assignedRubricGrades
, que representam os
pontos e níveis escolhidos pelo professor durante o rascunho e os estados de avaliação
atribuídos, respectivamente.
Você pode usar os métodos studentSubmissions.get()
e
studentSubmissions.list()
para conferir os envios com nota.
Adicione a seguinte função a main.py
para listar os envios dos estudantes:
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
Em seguida, atualize e execute main.py
para conferir as notas de envio.
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
e assignedRubricGrades
contêm:
- O
criterionId
dos critérios de rubrica correspondentes. - A
points
que o professor atribuiu para cada critério. Isso pode ser do nível selecionado, mas o professor também pode ter substituído essa informação. - O
levelId
do nível escolhido para cada critério. Se o professor não escolher um nível, mas ainda atribuir pontos para o critério, esse campo não estará presente.
Essas listas contêm apenas entradas para os critérios em que um professor
selecionou um nível ou definiu pontos. Por exemplo, se um professor escolher
interagir apenas com um critério durante a avaliação, o draftRubricGrades
e
assignedRubricGrades
terão apenas um item, mesmo que a rubrica tenha muitos
critérios.
Excluir uma rubrica
Uma rubrica pode ser excluída com uma solicitação delete()
padrão. O código abaixo
mostra um exemplo de função para fins de completude, mas, como a avaliação já
começou, não é possível excluir a rubrica atual:
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
Exportar e importar rubricas
As rubricas podem ser exportadas manualmente para as Planilhas Google para reutilização pelos professores.
Além de especificar os critérios da rubrica no código, é possível criar e
atualizar rubricas dessas planilhas exportadas especificando o
sourceSpreadsheetId
no corpo da rubrica em vez de 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