Começar a usar rubricas

Uma rubric é um modelo que os professores podem usar ao avaliar os trabalhos dos estudantes. A API Classroom permite que você atue em nome do professor para gerenciar essas rubricas, além de ler as notas dos trabalhos dos estudantes.

Visualização de uma rubrica na interface do Google Sala de Aula Figura 1. Visualização de uma rubrica de exemplo 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 com rubrica é feita na interface do Google Sala de Aula.

Pré-requisitos

Este guia pressupõe que você tenha o seguinte:

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.

  1. Navegue até a página de credenciais do Google Cloud no console do Google Cloud.
  2. Clique em Criar credenciais > ID do cliente OAuth.
  3. Clique em Tipo de aplicativo > App para computador.
  4. No campo Nome, digite um nome para a credencial. Esse nome só é mostrado no console do Google Cloud. Por exemplo, "Cliente de rubricas".
  5. Clique em Criar. A tela "Cliente OAuth criado" aparece, mostrando o novo ID do cliente e a chave secreta do cliente.
  6. Clique em Fazer o download do JSON e em OK. A credencial recém-criada aparece em "IDs do cliente OAuth 2.0".
  7. Salve o arquivo JSON baixado como credentials.json e mova-o para o diretório de trabalho.
  8. 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 do OAuth atuais do seu projeto, talvez seja necessário configurar outros escopos.

  1. Acesse a tela de permissão do OAuth.
  2. Clique em Editar app > Salvar e continuar para acessar a tela "Escopos".
  3. Clique em Adicionar ou remover escopos.
  4. Adicione os seguintes escopos se você ainda não os tiver:
    • https://www.googleapis.com/auth/classroom.coursework.students
    • https://www.googleapis.com/auth/classroom.courses
  5. Em seguida, clique em Atualizar > Salvar e continuar > Salvar e continuar > Voltar ao painel.

Consulte Configurar a tela de permissão do OAuth para saber mais.

O escopo classroom.coursework.students permite acesso de leitura e gravação a rubricas (além do acesso a CourseWork), e o escopo classroom.courses permite ler e gravar cursos.

Os escopos necessários para um determinado método são listados na documentação de referência do método. Consulte courses.courseWork.rubrics.create escopos de autorização como exemplo. Você pode conferir todos os escopos do Google Sala de Aula 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 cria a biblioteca de cliente e autoriza o usuário, usando sua 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 atividade ou CourseWork e só é significativa no contexto desse CourseWork. As rubricas só podem ser criadas pelo projeto do Google Cloud que criou o item pai CourseWork. Para fins deste guia, crie uma nova atividade 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 recuperar o course_id da classe de teste que você acabou de criar, criar uma nova atividade de exemplo e recuperar o 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. Eles são necessários para todas as operações CRUD de rubricas.

Agora você tem um CourseWork de exemplo no Google Sala de Aula.

Visualização de uma atividade na interface do Google Sala de Aula Figura 2. Visualização de uma atividade de exemplo 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 permitir que os desenvolvedores determinem os recursos a que um usuário tem acesso.

Atualize e execute main.py para confirmar se sua conta de teste tem acesso ao recurso 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 create() chamada contendo o objeto de rubrica completo, em que as propriedades de ID para critérios e níveis são omitidas (elas são geradas na criação).

Adicione a função a seguir ao arquivo 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 seus Course e CourseWork IDs de 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))

Alguns pontos sobre a representação da rubrica:

  • A ordem de critérios e níveis é refletida na interface do Google Sala de Aula.
  • Os níveis com pontuação (aqueles com a propriedade points) precisam ser classificados por pontos em ordem crescente ou decrescente (não podem ser ordenados aleatoriamente).
  • Os professores podem reordenar critérios e níveis com pontuação (mas não níveis sem pontuação) na interface, e isso muda a ordem deles nos dados.

Consulte as limitações para mais ressalvas sobre a estrutura das rubricas.

De volta à interface, você verá a rubrica na atividade.

Visualização de uma rubrica na interface do Google Sala de Aula Figura 3. Visualização de uma rubrica de exemplo 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().

Pode haver no máximo uma rubrica em uma atividade, então list() pode parecer não intuitivo, mas é útil se você ainda não tiver o ID da rubrica. Se não houver uma rubrica associada a um CourseWork, a resposta list() estará vazia.

Adicione a função a seguir ao arquivo 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. Usar get() na função pode ser assim:

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 erro 404 se não houver rubrica.

Atualizar uma rubrica

As atualizações de uma rubrica são feitas com patch() chamadas. 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:

  1. Os critérios ou níveis adicionados sem um ID são considerados adições.
  2. Os critérios ou níveis ausentes de antes são considerados exclusões.
  3. Os critérios ou níveis com um ID atual, mas dados modificados são considerados edições. As propriedades não modificadas são deixadas no estado em que se encontram.
  4. Os critérios ou níveis fornecidos com IDs novos ou desconhecidos são considerados erros.
  5. A ordem dos novos critérios e níveis é considerada a nova ordem da interface (com as limitações mencionadas acima).

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 acima:

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 agora serão refletidas para o professor no Google Sala de Aula.

Visualização de uma rubrica atualizada na interface do Google Sala de Aula Figura 4. Visualização da rubrica atualizada.

Ver trabalhos enviados com rubrica

Por enquanto, os trabalhos dos estudantes não podem ser avaliados com uma rubrica pela API, mas você pode ler as notas de rubrica dos trabalhos que foram avaliados com uma rubrica na interface do Google Sala de Aula.

Como estudante na interface do Google Sala de Aula, conclua e entregue sua atividade de exemplo. Em seguida, como professor, avalie manualmente a atividade usando a rubrica.

Visualização de uma nota de rubrica na interface do Google Sala de Aula Figura 5. Visualização do professor da rubrica durante a avaliação.

StudentSubmissions que foram avaliados com uma rubrica têm duas novas propriedades: draftRubricGrades e assignedRubricGrades, que representam os pontos e níveis escolhidos pelo professor durante os estados de avaliação de rascunho e atribuído, respectivamente.

Você pode usar os métodos studentSubmissions.get() e studentSubmissions.list() atuais para conferir os trabalhos enviados avaliados.

Adicione a função a seguir ao arquivo main.py para listar os trabalhos 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 dos trabalhos enviados.

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))

Os draftRubricGrades e assignedRubricGrades contêm:

  • O criterionId dos critérios de rubrica correspondentes.
  • Os points que o professor atribuiu a cada critério. Isso pode ser do nível selecionado, mas o professor também pode ter substituído isso.
  • O levelId do nível escolhido para cada critério. Se o professor não escolheu um nível, mas ainda atribuiu pontos ao critério, esse campo não está 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, os 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 a seguir mostra uma função de exemplo para fins de integridade, 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 manualmente exportadas para o Planilhas Google para serem reutilizadas pelos professores.

Além de especificar critérios de rubrica no código, é possível criar e atualizar rubricas dessas planilhas exportadas especificando o sourceSpreadsheetId em um corpo de 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