Un rubric
es una plantilla que los profesores pueden usar cuando califican las tareas de los estudiantes. La API de Classroom te permite actuar en nombre del profesor para administrar estas rúbricas, así como leer las calificaciones de las rúbricas en las entregas de los estudiantes.
Figura 1: Vista de una rúbrica de ejemplo en una tarea de Classroom.
En esta guía, se explican los conceptos básicos y la funcionalidad de la API de Rubrics. Consulta estos artículos del Centro de ayuda para obtener información sobre la estructura general de una rúbrica y cómo se realiza la calificación con rúbricas en la IU de Classroom.
Requisitos previos
En esta guía, se da por sentado que tienes lo siguiente:
- Python 3.8.6 o una versión posterior
- La herramienta de administración de paquetes pip
- Un proyecto de Google Cloud
- Una cuenta de Google Workspace for Education con Google Classroom habilitado y una licencia de Google Workspace for Education Plus asignada Si no tienes una, puedes solicitar una cuenta de demo de desarrollador actualizada.
- Una clase de prueba con al menos una cuenta de estudiante de prueba Si no tienes una clase de Classroom que puedas usar para las pruebas, crea una en la IU y agrega un estudiante de prueba.
Cómo autorizar credenciales para una aplicación de escritorio
Para autenticarte como usuario final y acceder a los datos del usuario en tu app, debes crear uno o más IDs de cliente de OAuth 2.0. Un ID de cliente se usa con el fin de identificar una sola app para los servidores de OAuth de Google. Si tu app se ejecuta en varias plataformas, debes crear un ID de cliente independiente para cada una.
- Navega a la página de credenciales de Google Cloud en la consola de Google Cloud.
- Haz clic en Crear credenciales > ID de cliente de OAuth.
- Haz clic en Tipo de aplicación > App de escritorio.
- En el campo Nombre, escribe un nombre para la credencial. Este nombre solo se muestra en la consola de Google Cloud. Por ejemplo, “Cliente de rúbricas”.
- Haz clic en Crear. Aparecerá la pantalla Se creó el cliente de OAuth, que muestra tu nuevo ID de cliente y secreto de cliente.
- Haz clic en Descargar JSON y, luego, en Aceptar. La credencial creada recientemente aparecerá en IDs de cliente de OAuth 2.0.
- Guarda el archivo JSON descargado como
credentials.json
y muévelo a tu directorio de trabajo. - Haz clic en Crear credenciales > Clave de API y anota la clave de API.
Consulta Crea credenciales de acceso para obtener más información.
Configura los permisos de OAuth
Según los permisos de OAuth existentes de tu proyecto, es posible que debas configurar permisos adicionales.
- Navega a la pantalla de consentimiento de OAuth.
- Haz clic en Editar app > Guardar y continuar para ir a la pantalla Permisos.
- Haz clic en Agregar o quitar alcances.
- Agrega los siguientes permisos si aún no los tienes:
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.courses
- Luego, haz clic en Actualizar > Guardar y continuar > Guardar y continuar > Volver al panel.
Consulta Cómo configurar la pantalla de consentimiento de OAuth para obtener más información.
El permiso classroom.coursework.students
habilita el acceso de lectura y escritura a las rúbricas (junto con el acceso a CourseWork
), y el permiso classroom.courses
permite leer y escribir cursos.
Los permisos necesarios para un método determinado se enumeran en la documentación de referencia del método. Consulta los alcances de autorización de courses.courseWork.rubrics.create
como ejemplo. Puedes ver todos los permisos de Classroom en Permisos de OAuth 2.0 para las APIs de Google.
Configura la muestra
En tu directorio de trabajo, instala la biblioteca cliente de Google para Python:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
Crea un archivo llamado main.py
que compile la biblioteca cliente y autorice al usuario con tu clave de API en 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)
Ejecuta la secuencia de comandos con python main.py
. Se te solicitará que accedas y que otorgues consentimiento para los permisos de OAuth.
Crea una asignación
Una rúbrica se asocia con una tarea, o CourseWork
, y solo tiene sentido en el contexto de esa CourseWork
. Solo el proyecto de Google Cloud que creó el CourseWork
elemento superior puede crear rúbricas. Para los fines de esta guía, crea una nueva tarea de CourseWork
con una secuencia de comandos.
Agrega lo siguiente 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
Ahora, actualiza main.py
para recuperar el course_id
de la clase de prueba que acabas de crear, crea una nueva tarea de ejemplo y recupera el coursework_id
de la tarea:
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.
Guarda course_id
y coursework_id
. Estos son necesarios para todas las operaciones de CRUD de las rúbricas.
Ahora deberías tener un CourseWork
de muestra en Classroom.
Figura 2: Vista de una tarea de ejemplo en Classroom.
Verifica la elegibilidad del usuario
Para crear y actualizar rúbricas, es necesario que el usuario que realiza la solicitud y el propietario del curso correspondiente tengan asignada una licencia de Google Workspace for Education Plus. Classroom admite un extremo de elegibilidad del usuario para permitir que los desarrolladores determinen a qué funciones tiene acceso un usuario.
Actualiza y ejecuta main.py
para confirmar que tu cuenta de prueba tenga acceso a la función de rúbricas:
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.')
Crea una rúbrica
Ya está todo listo para que comiences a administrar las rúbricas.
Se puede crear una rúbrica en un CourseWork
con una llamada a create()
que contenga el objeto de rúbrica completo, en el que se omiten las propiedades de ID para los criterios y los niveles (se generan en la creación).
Agrega la siguiente función 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
Luego, actualiza y ejecuta main.py
para crear la rúbrica de ejemplo con los IDs Course
y CourseWork
que usaste antes:
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))
Estos son algunos puntos sobre la representación de la rúbrica:
- El orden de los criterios y los niveles se refleja en la IU de Classroom.
- Los niveles con puntuación (aquellos con la propiedad
points
) deben ordenarse por puntos en orden ascendente o descendente (no se pueden ordenar de forma aleatoria). - Los profesores pueden volver a ordenar los criterios y los niveles con calificación (pero no los niveles sin calificación) en la IU, lo que cambia su orden en los datos.
Consulta las limitaciones para obtener más información sobre la estructura de las rúbricas.
En la IU, deberías ver la rúbrica en la tarea.
Figura 3: Vista de una rúbrica de ejemplo en una tarea de Classroom.
Lee una rúbrica
Las rúbricas se pueden leer con los métodos estándar list()
y get()
.
Puede haber una rúbrica como máximo en una tarea, por lo que list()
puede parecer poco intuitivo, pero es útil si aún no tienes el ID de la rúbrica. Si no hay una rúbrica asociada con un CourseWork
, la respuesta list()
estará vacía.
Agrega la siguiente función 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
Actualiza y ejecuta main.py
para recuperar la rúbrica que agregaste:
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.
Anota la propiedad id
en la rúbrica para los pasos posteriores.
Get()
funciona bien cuando tienes el ID de la rúbrica. En cambio, usar get()
en la función podría verse de la siguiente manera:
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
Esta implementación muestra un error 404 si no hay una rúbrica.
Cómo actualizar una rúbrica
Las actualizaciones de una rúbrica se realizan con llamadas a patch()
. Debido a la estructura compleja de una rúbrica, las actualizaciones deben realizarse con un patrón de lectura, modificación y escritura, en el que se reemplaza toda la propiedad criteria
.
Las reglas de actualización son las siguientes:
- Los criterios o niveles agregados sin un ID se consideran adiciones.
- Los criterios o niveles que falten de antes se consideran eliminaciones.
- Los criterios o niveles con un ID existente, pero con datos modificados se consideran ediciones. Las propiedades no modificadas se dejan como están.
- Los criterios o niveles proporcionados con IDs nuevos o desconocidos se consideran errores.
- El orden de los criterios y niveles nuevos se considera el nuevo orden de la IU (con las limitaciones mencionadas anteriormente).
Agrega una función para actualizar una rúbrica:
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
En este ejemplo, el campo criteria
se especifica para su modificación con un updateMask
.
Luego, modifica main.py
para realizar un cambio en cada una de las reglas de actualización antes 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))
Los cambios deberían reflejarse para el profesor en Classroom.
Figura 4: Vista de la rúbrica actualizada.
Cómo ver las entregas calificadas con rúbrica
Por el momento, la API no puede calificar los envíos de los estudiantes con una rúbrica, pero puedes leer las calificaciones de rúbrica de los envíos que se calificaron con una rúbrica en la IU de Classroom.
Como estudiante en la IU de Classroom, completa y entrega la tarea de ejemplo. Luego, como profesor, califica la tarea de forma manual con la rúbrica.
Figura 5: Vista del profesor de la rúbrica durante la calificación
Los StudentSubmissions
que se calificaron con una rúbrica tienen dos propiedades nuevas: draftRubricGrades
y assignedRubricGrades
, que representan los puntos y los niveles que eligió el profesor durante el borrador y los estados de calificación asignados, respectivamente.
Puedes usar los métodos existentes studentSubmissions.get()
y studentSubmissions.list()
para ver las entregas calificadas.
Agrega la siguiente función a main.py
para mostrar las entregas de los estudiantes:
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
Luego, actualiza y ejecuta main.py
para ver las calificaciones de las entregas.
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
y assignedRubricGrades
contienen lo siguiente:
- El
criterionId
de los criterios de la rúbrica correspondientes. - El
points
que el profesor asignó para cada criterio. Esto puede deberse al nivel seleccionado, pero el profesor también podría haber reemplazado esta información. - El
levelId
del nivel elegido para cada criterio. Si el profesor no eligió un nivel, pero aún así asignó puntos para el criterio, este campo no estará presente.
Estas listas solo contienen entradas para los criterios en los que un profesor seleccionó un nivel o estableció puntos. Por ejemplo, si un profesor elige interactuar solo con un criterio durante la calificación, draftRubricGrades
y assignedRubricGrades
solo tendrían un elemento, incluso si la rúbrica tiene muchos criterios.
Cómo borrar una rúbrica
Se puede borrar una rúbrica con una solicitud estándar de delete()
. En el siguiente código, se muestra una función de ejemplo para completar la información, pero, como ya comenzó la calificación, no puedes borrar la rúbrica actual:
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
Cómo exportar y también importar rúbricas
Las rúbricas se pueden exportar manualmente a Hojas de cálculo de Google para que los profesores las vuelvan a usar.
Además de especificar los criterios de la rúbrica en el código, es posible crear y actualizar rúbricas a partir de estas hojas exportadas si especificas sourceSpreadsheetId
en el cuerpo de la rúbrica en lugar 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