Premiers pas avec les grilles d'évaluation

Une rubric est un modèle que les enseignants peuvent utiliser pour noter les devoirs des élèves. L'API Classroom vous permet d'agir au nom de l'enseignant pour gérer ces grilles d'évaluation, ainsi que pour lire les notes des grilles d'évaluation dans les devoirs des élèves.

Grille d'évaluation dans l'interface utilisateur de Classroom Figure 1. Exemple de grille d'évaluation dans un devoir Classroom.

Ce guide explique les concepts de base et les fonctionnalités de l'API Rubrics. Consultez ces articles du Centre d'aide pour en savoir plus sur la structure générale d'une grille d'évaluation et sur la façon de noter avec une grille d'évaluation dans l'interface utilisateur de Classroom.

Prérequis

Ce guide suppose que vous disposez des éléments suivants :

Autoriser les identifiants pour une application de bureau

Pour vous authentifier en tant qu'utilisateur final et accéder aux données utilisateur dans votre application, vous devez créer un ou plusieurs ID client OAuth 2.0. Un ID client sert à identifier une application unique auprès des serveurs OAuth de Google. Si votre application s'exécute sur plusieurs plates-formes, vous devez créer un ID client distinct pour chacune d'elles.

  1. Accédez à la page Identifiants Google Cloud dans la console Google Cloud.
  2. Cliquez sur Créer des identifiants > ID client OAuth.
  3. Cliquez sur Type d'application > Application de bureau.
  4. Dans le champ Nom, saisissez un nom pour l'identifiant. Ce nom n'apparaît que dans la console Google Cloud. Par exemple, "Client Rubrics".
  5. Cliquez sur Créer. L'écran "Client OAuth créé" s'affiche, indiquant votre nouvel ID client et votre code secret du client.
  6. Cliquez sur Télécharger au format JSON, puis sur OK. L'identifiant nouvellement créé s'affiche sous "ID client OAuth 2.0".
  7. Enregistrez le fichier JSON téléchargé sous le nom credentials.json, puis déplacez-le vers votre répertoire de travail.
  8. Cliquez sur Créer des identifiants > Clé API , puis notez la clé API.

Pour en savoir plus, consultez la section Créer des identifiants d'accès.

Configurer les champs d'application OAuth

En fonction des champs d'application OAuth existants de votre projet, vous devrez peut-être configurer des champs d'application supplémentaires.

  1. Accédez à l'écran de consentement OAuth.
  2. Cliquez sur Modifier l'application > Enregistrer et continuer pour accéder à l'écran "Champs d'application".
  3. Cliquez sur Ajouter ou supprimer des niveaux d'accès.
  4. Ajoutez les champs d'application suivants si vous ne les avez pas déjà :
    • https://www.googleapis.com/auth/classroom.coursework.students
    • https://www.googleapis.com/auth/classroom.courses
  5. Cliquez ensuite sur Mettre à jour > Enregistrer et continuer > Enregistrer et continuer > Retour au tableau de bord.

Pour en savoir plus, consultez la section Configurer l'écran de consentement OAuth.

Le champ d'application classroom.coursework.students permet d'accéder en lecture et en écriture aux grilles d'évaluation (ainsi qu'à CourseWork), et le champ d'application classroom.courses permet de lire et d'écrire des cours.

Les champs d'application requis pour une méthode donnée sont listés dans la documentation de référence de la méthode. Consultez par exemple les champs d'application d'autorisation courses.courseWork.rubrics.create Vous pouvez voir tous les champs d'application Classroom dans la section Champs d'application OAuth 2.0 pour les API Google.

Configurer l'exemple

Dans votre répertoire de travail, installez la bibliothèque cliente Google pour Python :

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

Créez un fichier nommé main.py qui crée la bibliothèque cliente et autorise l'utilisateur, en utilisant votre clé API à la place 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)

Exécutez le script à l'aide de python main.py. Vous devriez être invité à vous connecter et à accepter les champs d'application OAuth.

Créer une attribution

Une grille d'évaluation est associée à une attribution, ou CourseWork, et n'est pertinente que dans le contexte de cette CourseWork. Les grilles d'évaluation ne peuvent être créées que par le projet Google Cloud qui a créé l'élément parent CourseWork item. Pour les besoins de ce guide, créez une attribution CourseWork avec un script.

Ajoutez le code suivant à 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

Mettez maintenant à jour main.py pour récupérer le course_id du cours test que vous venez de créer, créez un exemple d'attribution et récupérez le coursework_id de l'attribution :

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.

Enregistrez le course_id et le coursework_id. Ils sont nécessaires pour toutes les opérations CRUD sur les grilles d'évaluation.

Vous devriez maintenant disposer d'un exemple de CourseWork dans Classroom.

Affichage d'un devoir dans l'interface utilisateur Classroom Figure 2. Exemple d'attribution dans Classroom.

Vérifier l'éligibilité de l'utilisateur

Pour créer et mettre à jour des grilles d'évaluation, l'utilisateur qui effectue la requête et le propriétaire du cours correspondant doivent disposer d'une licence Google Workspace for Education Plus. Classroom est compatible avec un point de terminaison d'éligibilité des utilisateurs pour permettre aux développeurs de déterminer les fonctionnalités auxquelles un utilisateur a accès.

Mettez à jour et exécutez main.py pour confirmer que votre compte test a accès à la fonctionnalité de grilles d'évaluation :

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

Créer une grille d'évaluation

Vous êtes maintenant prêt à commencer à gérer les grilles d'évaluation.

Une grille d'évaluation peut être créée sur un CourseWork avec un create() appel contenant l'objet de grille d'évaluation complet, où les propriétés d'ID pour les critères et les niveaux sont omises (elles sont générées lors de la création).

Ajoutez la fonction suivante à 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

Mettez ensuite à jour et exécutez main.py pour créer l'exemple de grille d'évaluation, en utilisant vos Course et CourseWork ID précédents :

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

Quelques points concernant la représentation de la grille d'évaluation :

  • L'ordre des critères et des niveaux est reflété dans l'interface utilisateur de Classroom.
  • Les niveaux notés (ceux avec la propriété points) doivent être triés par points par ordre croissant ou décroissant (ils ne peuvent pas être triés de manière aléatoire).
  • Les enseignants peuvent trier à nouveau les critères et les niveaux notés (mais pas les niveaux non notés) dans l'interface utilisateur, ce qui modifie leur ordre dans les données.

Pour en savoir plus sur la structure des grilles d'évaluation, consultez la section Limites.

De retour dans l'interface utilisateur, vous devriez voir la grille d'évaluation dans l'attribution.

Grille d'évaluation dans l'interface utilisateur de Classroom Figure 3. Exemple de grille d'évaluation dans un devoir Classroom.

Lire une grille d'évaluation

Les grilles d'évaluation peuvent être lues avec les méthodes standards list() et get().

Il ne peut y avoir qu'une seule grille d'évaluation dans une attribution. Par conséquent, list() peut sembler peu intuitif, mais il est utile si vous ne disposez pas déjà de l'ID de la grille d'évaluation. Si aucune grille d'évaluation n'est associée à un CourseWork, la réponse list() est vide.

Ajoutez la fonction suivante à 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

Mettez à jour et exécutez main.py pour récupérer la grille d'évaluation que vous avez ajoutée :

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.

Notez la propriété id dans la grille d'évaluation pour les étapes ultérieures.

Get() fonctionne bien lorsque vous disposez de l'ID de la grille d'évaluation. L'utilisation de get() dans la fonction peut ressembler à ceci :

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

Cette implémentation renvoie une erreur 404 s'il n'y a pas de grille d'évaluation.

Mettre à jour une grille d'évaluation

Les mises à jour d'une grille d'évaluation sont effectuées avec des appels patch(). En raison de la structure complexe d'une grille d'évaluation, les mises à jour doivent être effectuées avec un modèle de lecture-modification-écriture, où l'intégralité de la propriété criteria est remplacée.

Les règles de mise à jour sont les suivantes :

  1. Les critères ou les niveaux ajoutés sans ID sont considérés comme des ajouts.
  2. Les critères ou les niveaux manquants sont considérés comme des suppressions.
  3. Les critères ou les niveaux avec un ID existant, mais des données modifiées sont considérés comme des modifications. Les propriétés non modifiées sont laissées telles quelles.
  4. Les critères ou les niveaux fournis avec des ID nouveaux ou inconnus sont considérés comme des erreurs.
  5. L'ordre des nouveaux critères et niveaux est considéré comme le nouvel ordre de l'interface utilisateur (avec les limites mentionnées ci-dessus).

Ajoutez une fonction pour mettre à jour une grille d'évaluation :

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

Dans cet exemple, le champ criteria est spécifié pour être modifié avec un updateMask.

Modifiez ensuite main.py pour apporter une modification à chacune des règles de mise à jour mentionnées ci-dessus :

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

Les modifications devraient maintenant être visibles pour l'enseignant dans Classroom.

Affichage d'une grille d'évaluation mise à jour dans l'interface utilisateur de Classroom Figure 4. Affichage de la grille d'évaluation mise à jour.

Afficher les devoirs notés avec une grille d'évaluation

Pour le moment, les devoirs des élèves ne peuvent pas être notés avec une grille d'évaluation par l'API, mais vous pouvez lire les notes des grilles d'évaluation pour les devoirs qui ont été notés avec une grille d'évaluation dans l'interface utilisateur de Classroom.

En tant qu'élève dans l'interface utilisateur de Classroom, effectuez et remettez votre exemple d'attribution. Ensuite, en tant qu'enseignant, notez manuellement le devoir à l'aide de la grille d'évaluation.

Affichage d'une note de grille d'évaluation dans l'interface utilisateur de Classroom Figure 5. Vue de l'enseignant de la grille d'évaluation lors de la notation.

StudentSubmissions qui ont été notés avec une grille d’évaluation comportent deux nouvelles propriétés : draftRubricGrades et assignedRubricGrades, qui représentent respectivement les points et les niveaux choisis par l’enseignant lors des états de notation brouillon et attribué.

Vous pouvez utiliser les méthodes studentSubmissions.get() et studentSubmissions.list() existantes pour afficher les devoirs notés.

Ajoutez la fonction suivante à main.py pour lister les devoirs des élèves :

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

Mettez ensuite à jour et exécutez main.py pour afficher les notes des devoirs.

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 et assignedRubricGrades contiennent les éléments suivants :

  • Le criterionId des critères de la grille d'évaluation correspondants.
  • Les points attribués par l'enseignant pour chaque critère. Cela peut provenir du niveau sélectionné, mais l'enseignant peut également l'avoir remplacé.
  • Le levelId du niveau choisi pour chaque critère. Si l'enseignant n'a pas choisi de niveau, mais a quand même attribué des points pour le critère, ce champ n'est pas présent.

Ces listes ne contiennent que des entrées pour les critères dans lesquels un enseignant a sélectionné un niveau ou défini des points. Par exemple, si un enseignant choisit d'interagir uniquement avec un critère lors de la notation, draftRubricGrades et assignedRubricGrades ne comporteront qu'un seul élément, même si la grille d'évaluation comporte de nombreux critères.

Supprimer une grille d'évaluation

Une grille d'évaluation peut être supprimée avec une requête delete() standard. Le code suivant montre un exemple de fonction pour plus de clarté, mais comme la notation a déjà commencé, vous ne pouvez pas supprimer la grille d'évaluation actuelle :

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

Exporter et importer des grilles d'évaluation

Les grilles d'évaluation peuvent être exportées manuellement vers Google Sheets pour être réutilisées par les enseignants.

En plus de spécifier des critères de grille d'évaluation dans le code, il est possible de créer et de mettre à jour des grilles d'évaluation à partir de ces feuilles exportées en spécifiant le sourceSpreadsheetId dans un corps de grille d'évaluation au lieu 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