Comienza a usar las rúbricas

Un objeto rubric es una plantilla que los profesores pueden usar cuando califican las entregas de los alumnos. La API de Classroom te permite actuar en nombre del profesor para administrar estas rúbricas.

Vista de una rúbrica en la IU de Classroom Figura 1: Vista de una rúbrica de muestra 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 de rúbricas en la IU de Classroom.

Requisitos previos

En esta guía, se supone que tienes lo siguiente:

Autoriza 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 la app se ejecuta en varias plataformas, debes crear un ID de cliente distinto para cada una.

  1. Ve a la página Credenciales de GCP en Google Cloud Console.
  2. Haz clic en Crear credenciales > ID de cliente de OAuth.
  3. Haz clic en Tipo de aplicación > Aplicación de escritorio.
  4. 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 vista previa de rúbricas".
  5. Haz clic en Crear. Aparecerá la pantalla de creación del cliente OAuth que muestra tu ID de cliente nuevo y el secreto del cliente.
  6. Haz clic en Download JSON y, luego, en OK. La credencial creada recientemente aparece en los ID de cliente de OAuth 2.0.
  7. Guarda el archivo JSON descargado como credentials.json y muévelo a tu directorio de trabajo.
  8. 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 permisos de OAuth

Según los permisos de OAuth existentes de tu proyecto, es posible que debas configurar permisos adicionales.

  1. Navega a la pantalla de consentimiento de OAuth.
  2. Haz clic en Edit App > Save and Continue para ir a la pantalla Scopes.
  3. Haz clic en Add or Remove Scopes.
  4. 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
  5. Luego, haz clic en Actualizar > Guardar y continuar > Guardar y continuar > Volver al panel.

Consulta Configura la pantalla de consentimiento de OAuth para obtener más información.

El alcance classroom.coursework.students habilita el acceso de lectura y escritura a las rúbricas (junto con el acceso a CourseWork), y el alcance classroom.courses permite cursos de lectura y escritura.

Los alcances necesarios para un método determinado se enumeran en la documentación de referencia del método. Consulta los permisos de autorización de courses.courseWork.rubrics.create como ejemplo. Puedes ver todos los permisos de Classroom en los permisos de OAuth 2.0 para las API de Google. Las rúbricas no se mencionan aquí ya que la API aún está en versión preliminar.

Configura la muestra

En el 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 mediante el uso de 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 pedirá que accedas y dé tu consentimiento a los permisos de OAuth.

Crear una tarea

Una rúbrica está asociada con una tarea, o CourseWork, y solo es significativa en el contexto de esa CourseWork. Solo el proyecto de Google Cloud que creó el elemento CourseWork superior puede crear rúbricas. A los fines de esta guía, crea una asignación CourseWork nueva 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 asignación de muestra nueva 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 las operaciones de CRUD de todas las rúbricas.

Ahora deberías tener una muestra de CourseWork en Classroom.

Vista de una tarea en la IU de Classroom Figura 2: Vista de una tarea de ejemplo en Classroom.

Crea una rúbrica

Ya estás listo para comenzar a administrar rúbricas.

Se puede crear una rúbrica en una CourseWork con una llamada Create que contenga el objeto de la rúbrica completo, donde se omiten las propiedades del ID para los criterios y los niveles (se generan durante 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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).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 ID de Course y CourseWork anteriores:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(rubric, indent=4))

Estos son algunos puntos sobre la representación en las rúbricas:

  • Los criterios y el orden de los niveles se reflejan en la IU de Classroom.
  • Los niveles de 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 reordenar los criterios y los niveles de puntuación (pero no los niveles sin puntuación) en la IU, y eso cambia su orden en los datos.

Consulta las limitaciones para obtener más advertencias sobre la estructura de las rúbricas.

De vuelta en la interfaz de usuario, deberías ver la rúbrica en la tarea.

Vista de una rúbrica en la IU de Classroom Figura 3: Vista de una rúbrica de muestra en una tarea de Classroom.

Leer una rúbrica

Las rúbricas se pueden leer con los métodos estándar List y Get.

Puede haber como máximo una rúbrica 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 está 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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).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 su lugar, 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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).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 de Patch. Debido a la estructura compleja de una rúbrica, las actualizaciones deben realizarse con un patrón de lectura-modificación-escritura, en el que se reemplaza toda la propiedad criteria.

Las reglas de actualización son las siguientes:

  1. Los criterios o niveles agregados sin un ID se consideran incorporaciones.
  2. Los criterios o niveles que faltan de antes se consideran eliminaciones.
  3. Los criterios o niveles con un ID existente, pero los datos modificados se consideran ediciones. Las propiedades sin modificar se dejan como están.
  4. Los criterios o niveles proporcionados con IDs nuevos o desconocidos se consideran errores.
  5. El orden de los criterios y niveles nuevos se considera el orden de la IU nuevo (con las limitaciones antes mencionadas).

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',
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()

        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

En este ejemplo, se especifica el campo criteria para la 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)

    # 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 ahora deberían reflejarse para el profesor en Classroom.

Vista de una rúbrica actualizada en la IU de Classroom Figura 4: Vista de la rúbrica actualizada.

Ver entregas calificadas por rúbricas

Por ahora, las entregas de los alumnos no se pueden calificar con una rúbrica en la API, pero puedes leer las calificaciones de las rúbricas para las entregas que se calificaron con una rúbrica en la IU de Classroom.

Como alumno en la IU de Classroom, completa y entrega tu tarea de ejemplo. Luego, como profesor, califica la tarea manualmente con la rúbrica.

Vista de una calificación por rúbrica en la IU de Classroom Figura 5: Vista del profesor de la rúbrica durante la calificación.

Las entregas de los alumnos que se calificaron con una rúbrica tienen dos propiedades nuevas: draftRubricGrades y assignedRubricGrades, que representan los puntos y niveles que eligió el profesor durante el borrador y los estados de calificación asignados, respectivamente.

Además, las entregas de los alumnos con una rúbrica asociada contienen un campo rubricId, incluso antes de calificarlas. Representa la rúbrica más reciente asociada con la CourseWork, y este valor puede cambiar si los profesores borran y vuelven a crear una rúbrica.

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 enumerar las entregas de los alumnos:

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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).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 la entrega.

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.
  • La points que el profesor asignó a cada criterio. Esto puede ser del nivel seleccionado, pero el profesor también podría haberlo reemplazado.
  • La 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 con 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

Una rúbrica se puede borrar con una solicitud estándar de Delete. En el siguiente código, se muestra una función de ejemplo de finalizació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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

Importa y exporta 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, puedes crear y actualizar las 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,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).execute()

        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

Comentarios

Si detectas algún problema o quieres aportar algún comentario, comparte tus comentarios.