Voti degli allegati e pass-back dei voti

Questa è la sesta procedura dettagliata della serie dettagliata dei componenti aggiuntivi di Classroom.

In questa procedura dettagliata, modificherai l'esempio del passaggio della procedura dettagliata precedente per produrre un allegato di tipo attività con valutazione. Puoi anche passare nuovamente un voto a Google Classroom in modo programmatico, operazione che viene visualizzata nel registro dell'insegnante come voto provvisorio.

Questa procedura dettagliata differisce leggermente dagli altri della serie in quanto esistono due possibili approcci per ritrasmettere i voti a Classroom. Entrambe le opzioni hanno un impatto distinto sull'esperienza degli sviluppatori e degli utenti; prendi in considerazione entrambe le cose quando progetti il componente aggiuntivo di Classroom. Per ulteriori informazioni sulle opzioni di implementazione, consulta la pagina della guida sull'interazione con gli allegati.

Tieni presente che le funzionalità di valutazione nell'API sono facoltative. Possono essere utilizzati con qualsiasi allegato di tipo attività.

Nel corso di questa procedura dettagliata, completerai quanto segue:

  • Modifica le richieste di creazione degli allegati precedenti all'API Classroom per impostare anche il denominatore del voto per l'allegato.
  • Assegna in modo programmatico un punteggio ai contenuti inviati dallo studente e imposta il numeratore del voto dell'allegato.
  • Implementa due approcci per passare il voto dei contenuti inviati a Classroom utilizzando le credenziali di un insegnante mentre l'insegnante ha effettuato l'accesso oppure offline.

Al termine, i voti vengono visualizzati nel registro di Classroom dopo l'attivazione del comportamento di pass-back. Il momento esatto in cui ciò accade dipende dall'approccio all'implementazione.

Ai fini di questo esempio, riutilizza l'attività della procedura dettagliata precedente, in cui allo studente viene mostrata l'immagine di un famoso punto di riferimento e viene chiesto di inserirne il nome. Assegna un voto completo all'allegato se lo studente inserisce il nome corretto, altrimenti zero.

Comprendere la funzionalità di valutazione dell'API Classroom componenti aggiuntivi

Il componente aggiuntivo può impostare sia il numeratore del voto sia il denominatore del voto per un allegato. Queste vengono rispettivamente impostate utilizzando i valori pointsEarned e maxPoints nell'API. Una scheda degli allegati nell'interfaccia utente di Classroom mostra il valore maxPoints una volta impostato.

Esempio di più allegati con maxPoints
su un compito

Figura 1. L'UI di creazione dei compiti con tre schede di allegati aggiuntivi per cui è impostato maxPoints.

L'API Classroom add-ons consente di configurare le impostazioni e impostare il punteggio ottenuto per i voti degli allegati. Non corrispondono ai voti del compito. Tuttavia, le impostazioni dei voti del compito seguono le impostazioni dei voti degli allegati dell'allegato che ha l'etichetta Sincronizzazione voti nella scheda degli allegati. Quando l'allegato "Sincronizzazione voti" imposta pointsEarned per l'invio di uno studente, imposta anche il voto provvisorio dello studente per il compito.

In genere, il primo allegato aggiunto al compito che imposta maxPoints riceve l'etichetta "Sincronizzazione voti". Vedi l'esempio di UI per la creazione dei compiti mostrato nella Figura 1 per un esempio dell'etichetta "Sincronizzazione voti". Tieni presente che la scheda "Allegato 1" ha l'etichetta "Sincronizzazione voti" e che il voto del compito nella casella rossa è stato aggiornato a 50 punti. Inoltre, tieni presente che, sebbene la Figura 1 mostra tre schede allegati, solo una scheda ha l'etichetta "Sincronizzazione voti". Questo è un limite fondamentale dell'implementazione attuale: un solo allegato può avere l'etichetta "Sincronizzazione voti".

Se sono presenti più allegati in cui è impostato maxPoints, la rimozione dell'allegato con "Sincronizzazione voti" non attiva l'opzione "Sincronizzazione voti" su nessuno degli allegati rimanenti. L'aggiunta di un altro allegato che imposta maxPoints attiva la sincronizzazione dei voti sul nuovo allegato e il voto massimo del compito viene modificato di conseguenza. Non esiste un meccanismo per vedere a livello di programmazione quale allegato ha l'etichetta "Sincronizzazione voti" né per vedere quanti allegati ha un determinato compito.

Impostare il voto massimo per un allegato

Questa sezione descrive l'impostazione del denominatore per il voto dell'allegato, ovvero il punteggio massimo possibile che tutti gli studenti possono ottenere per i compiti consegnati. Per farlo, imposta il valore maxPoints dell'allegato.

Per attivare le funzionalità di valutazione è necessaria solo una piccola modifica alla nostra implementazione esistente. Quando crei un collegamento, aggiungi il valore maxPoints nello stesso oggetto AddOnAttachment che contiene i campi studentWorkReviewUri, teacherViewUri e altri campi dell'allegato.

Tieni presente che il punteggio massimo predefinito per un nuovo compito è 100. Ti consigliamo di impostare maxPoints su un valore diverso da 100, in modo da poter verificare che i voti siano impostati correttamente. Imposta maxPoints su 50 come dimostrazione:

Python

Aggiungi il campo maxPoints durante la creazione dell'oggetto attachment, appena prima di inviare una richiesta CREATE all' endpoint courses.courseWork.addOnAttachments. Puoi trovarlo nel file webapp/attachment_routes.py se segui l'esempio fornito.

attachment = {
    # Specifies the route for a teacher user.
    "teacherViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True),
    },
    # Specifies the route for a student user.
    "studentViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True)
    },
    # Specifies the route for a teacher user when the attachment is
    # loaded in the Classroom grading view.
    "studentWorkReviewUri": {
        "uri":
            flask.url_for(
                "view_submission", _scheme='https', _external=True)
    },
    # Sets the maximum points that a student can earn for this activity.
    # This is the denominator in a fractional representation of a grade.
    "maxPoints": 50,
    # The title of the attachment.
    "title": f"Attachment {attachment_count}",
}

Ai fini di questa dimostrazione, memorizzi anche il valore maxPoints nel tuo database locale degli allegati. In questo modo, eviterai di dover effettuare un'ulteriore chiamata all'API in un secondo momento durante la valutazione dei compiti degli studenti. Tuttavia, tieni presente che è possibile che gli insegnanti modifichino le impostazioni dei voti dei compiti indipendentemente dal tuo componente aggiuntivo. Invia una richiesta GET all'endpoint courses.courseWork per visualizzare il valore maxPoints a livello di assegnazione. Nel farlo, passa itemId nel campo CourseWork.id.

Ora aggiorna il modello del tuo database in modo che contenga anche il valore maxPoints dell'allegato. Ti consigliamo di utilizzare il valore maxPoints della risposta CREATE:

Python

Innanzitutto, aggiungi un campo max_points alla tabella Attachment. Puoi trovarlo nel file webapp/models.py se segui l'esempio fornito.

# Database model to represent an attachment.
class Attachment(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The image filename to store.
    image_filename = db.Column(db.String(120))

    # The image caption to store.
    image_caption = db.Column(db.String(120))

    # The maximum number of points for this activity.
    max_points = db.Column(db.Integer)

Torna alla richiesta courses.courseWork.addOnAttachments CREATE. Archivia il valore maxPoints restituito nella risposta.

new_attachment = Attachment(
    # The new attachment's unique ID, returned in the CREATE response.
    attachment_id=resp.get("id"),
    image_filename=key,
    image_caption=value,
    # Store the maxPoints value returned in the response.
    max_points=int(resp.get("maxPoints")))
db.session.add(new_attachment)
db.session.commit()

Ora l'allegato ha un voto massimo. Ora dovresti essere in grado di verificare questo comportamento. Aggiungi un allegato a un nuovo compito e osserva che la scheda degli allegati mostra l'etichetta"Sincronizzazione voti" e il valore "Punti" del compito cambia.

Impostare un voto inviato da uno studente in Classroom

Questa sezione descrive l'impostazione del numeratore per il voto di un allegato, ovvero il punteggio di un singolo studente per l'allegato. Per farlo, imposta il valore pointsEarned per l'invio di uno studente.

Ora devi prendere una decisione importante: in che modo il componente aggiuntivo deve inviare una richiesta per impostare pointsEarned?

Il problema è che l'impostazione di pointsEarned richiede l'teacherambito OAuth. Non devi concedere l'ambito teacher agli utenti studenti. Questo potrebbe causare comportamenti imprevisti nell'interazione degli studenti con il componente aggiuntivo, ad esempio il caricamento dell'iframe della vista insegnante anziché di quello della vista studente. Per questo motivo, hai due opzioni per l'impostazione di pointsEarned:

  • Con le credenziali dell'insegnante che ha eseguito l'accesso.
  • Utilizzo delle credenziali degli insegnanti archiviate (offline).

Le sezioni seguenti illustrano i compromessi di ciascun approccio prima di illustrare ciascuna implementazione. Tieni presente che gli esempi forniti mostrano entrambi gli approcci per passare un voto in Classroom; consulta le istruzioni specifiche per la lingua riportate di seguito per scoprire come selezionare un approccio quando esegui gli esempi forniti:

Python

Individua la dichiarazione SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS nella parte superiore del file webapp/attachment_routes.py. Imposta questo valore su True per ritrasmettere i voti utilizzando le credenziali dell'insegnante che ha effettuato l'accesso. Imposta questo valore su False per ritrasmettere i voti utilizzando le credenziali memorizzate quando lo studente invia l'attività.

Impostare i voti utilizzando le credenziali dell'insegnante che ha effettuato l'accesso

Utilizza le credenziali dell'utente che ha eseguito l'accesso per inviare la richiesta di impostare pointsEarned. Questo approccio dovrebbe sembrare molto intuitivo, in quanto rispecchia il resto dell'implementazione finora e richiede uno sforzo minimo.

Tuttavia, tieni presente che l'insegnante interagisce solo con i contenuti inviati dallo studente nell'iframe della revisione del lavoro dello studente. Ciò comporta alcune importanti implicazioni:

  • Classroom non contiene voti finché l'insegnante non interviene nell'interfaccia utente di Classroom.
  • Un insegnante potrebbe dover aprire tutti i contenuti inviati dagli studenti per compilare tutti i voti degli studenti.
  • C'è un breve ritardo tra la ricezione del voto in Classroom e la sua visualizzazione nella UI di Classroom. In genere, il ritardo è compreso tra 5 e 10 secondi, ma può durare fino a 30 secondi.

La combinazione di questi fattori significa che gli insegnanti potrebbero dover svolgere un lavoro manuale considerevole e dispendioso in termini di tempo per compilare completamente i voti di un corso.

Per implementare questo approccio, aggiungi un'altra chiamata API al percorso esistente per la revisione del lavoro dello studente.

Dopo aver recuperato i record dei contenuti inviati e degli allegati dello studente, valuta i contenuti inviati e archivia il voto risultante. Imposta il voto nel campo pointsEarned di un oggetto AddOnAttachmentStudentSubmission. Infine, invia una richiesta PATCH all'endpoint courses.courseWork.addOnAttachments.studentSubmissions con l'istanza AddOnAttachmentStudentSubmission nel corpo della richiesta. Tieni presente che dobbiamo anche specificare pointsEarned nel campo updateMask nella nostra richiesta PATCH:

Python

# Look up the student's submission in our database.
student_submission = Submission.query.get(flask.session["submissionId"])

# Look up the attachment in the database.
attachment = Attachment.query.get(student_submission.attachment_id)

grade = 0

# See if the student response matches the stored name.
if student_submission.student_response.lower(
) == attachment.image_caption.lower():
    grade = attachment.max_points

# Create an instance of the Classroom service.
classroom_service = ch._credential_handler.get_classroom_service()

# Build an AddOnAttachmentStudentSubmission instance.
add_on_attachment_student_submission = {
    # Specifies the student's score for this attachment.
    "pointsEarned": grade,
}

# Issue a PATCH request to set the grade numerator for this attachment.
patch_grade_response = classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    attachmentId=flask.session["attachmentId"],
    submissionId=flask.session["submissionId"],
    # updateMask is a list of fields being modified.
    updateMask="pointsEarned",
    body=add_on_attachment_student_submission).execute()

Impostare i voti utilizzando le credenziali degli insegnanti offline

Il secondo approccio per impostare i voti richiede l'utilizzo di credenziali memorizzate per l'insegnante che ha creato l'allegato. Per questa implementazione devi creare le credenziali utilizzando i token di aggiornamento e di accesso di un insegnante precedentemente autorizzato, quindi utilizzare queste credenziali per impostare pointsEarned.

Un vantaggio fondamentale di questo approccio è che i voti vengono compilati senza richiedere un'azione da parte dell'insegnante nell'interfaccia utente di Classroom, evitando i problemi menzionati sopra. Il risultato è che gli utenti finali percepiscono l'esperienza di valutazione come semplice ed efficiente. Inoltre, questo approccio ti permette di scegliere il momento in cui ritrasmettere i voti, ad esempio quando gli studenti completano l'attività o in modo asincrono.

Completa le seguenti attività per implementare questo approccio:

  1. Modifica i record del database utente per archiviare un token di accesso.
  2. Modificare i record del database degli allegati per archiviare un ID insegnante.
  3. Recupera le credenziali dell'insegnante e, facoltativamente, crea una nuova istanza del servizio Classroom.
  4. Imposta il voto di un invio.

Ai fini di questa dimostrazione, imposta il voto quando lo studente completa l'attività, ovvero quando lo studente invia il modulo nel percorso Visualizzazione studente.

Modifica i record del database utente per archiviare il token di accesso

Per effettuare chiamate API, sono necessari due token univoci: il token di aggiornamento e il token di accesso. Se hai seguito la procedura dettagliata finora, lo schema della tabella User dovrebbe già archiviare un token di aggiornamento. L'archiviazione del token di aggiornamento è sufficiente quando effettui chiamate API solo con l'utente che ha eseguito l'accesso, poiché ricevi un token di accesso nell'ambito del flusso di autenticazione.

Tuttavia, ora devi effettuare chiamate come utente diverso dall'utente che ha eseguito l'accesso, il che significa che il flusso di autenticazione non è disponibile. Devi quindi archiviare il token di accesso insieme al token di aggiornamento. Aggiorna lo schema della tabella User per includere un token di accesso:

Python

Nell'esempio fornito, si trova nel file webapp/models.py.

# Database model to represent a user.
class User(db.Model):
    # The user's identifying information:
    id = db.Column(db.String(120), primary_key=True)
    display_name = db.Column(db.String(80))
    email = db.Column(db.String(120), unique=True)
    portrait_url = db.Column(db.Text())

    # The user's refresh token, which will be used to obtain an access token.
    # Note that refresh tokens will become invalid if:
    # - The refresh token has not been used for six months.
    # - The user revokes your app's access permissions.
    # - The user changes passwords.
    # - The user belongs to a Google Cloud organization
    #   that has session control policies in effect.
    refresh_token = db.Column(db.Text())

    # An access token for this user.
    access_token = db.Column(db.Text())

Quindi, aggiorna tutti i codici che creano o aggiornano un record User per archiviare anche il token di accesso:

Python

Nell'esempio fornito, si trova nel file webapp/credential_handler.py.

def save_credentials_to_storage(self, credentials):
    # Issue a request for the user's profile details.
    user_info_service = googleapiclient.discovery.build(
        serviceName="oauth2", version="v2", credentials=credentials)
    user_info = user_info_service.userinfo().get().execute()
    flask.session["username"] = user_info.get("name")
    flask.session["login_hint"] = user_info.get("id")

    # See if we have any stored credentials for this user. If they have used
    # the add-on before, we should have received login_hint in the query
    # parameters.
    existing_user = self.get_credentials_from_storage(user_info.get("id"))

    # If we do have stored credentials, update the database.
    if existing_user:
        if user_info:
            existing_user.id = user_info.get("id")
            existing_user.display_name = user_info.get("name")
            existing_user.email = user_info.get("email")
            existing_user.portrait_url = user_info.get("picture")

        if credentials and credentials.refresh_token is not None:
            existing_user.refresh_token = credentials.refresh_token
            # Update the access token.
            existing_user.access_token = credentials.token

    # If not, this must be a new user, so add a new entry to the database.
    else:
        new_user = User(
            id=user_info.get("id"),
            display_name=user_info.get("name"),
            email=user_info.get("email"),
            portrait_url=user_info.get("picture"),
            refresh_token=credentials.refresh_token,
            # Store the access token as well.
            access_token=credentials.token)

        db.session.add(new_user)

    db.session.commit()

Modificare i record del database degli allegati per archiviare un ID insegnante

Per impostare un voto per un'attività, effettua una chiamata per impostare pointsEarned come insegnante nel corso. Puoi farlo in diversi modi:

  • Archivia una mappatura locale delle credenziali degli insegnanti agli ID corso. Tieni presente, tuttavia, che lo stesso insegnante potrebbe non essere sempre associato a un determinato corso.
  • Invia richieste GET all'endpoint courses dell'API Classroom per recuperare gli insegnanti correnti. Poi, esegui una query nei record degli utenti locali per individuare le credenziali degli insegnanti corrispondenti.
  • Quando si crea un allegato di un componente aggiuntivo, memorizza un ID insegnante nel database degli allegati locali. Quindi, recupera le credenziali dell'insegnante dal attachmentId passato all'iframe della vista studente.

Questo esempio mostra l'ultima opzione perché stai impostando i voti quando lo studente completa un allegato di un'attività.

Aggiungi un campo ID insegnante alla tabella Attachment del database:

Python

Nell'esempio fornito, si trova nel file webapp/models.py.

# Database model to represent an attachment.
class Attachment(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The image filename to store.
    image_filename = db.Column(db.String(120))

    # The image caption to store.
    image_caption = db.Column(db.String(120))

    # The maximum number of points for this activity.
    max_points = db.Column(db.Integer)

    # The ID of the teacher that created the attachment.
    teacher_id = db.Column(db.String(120))

Quindi, aggiorna tutti i codici che creano o aggiornano un record Attachment per memorizzare anche l'ID dell'autore:

Python

Nell'esempio fornito, è nel metodo create_attachments del file webapp/attachment_routes.py.

# Store the attachment by id.
new_attachment = Attachment(
    # The new attachment's unique ID, returned in the CREATE response.
    attachment_id=resp.get("id"),
    image_filename=key,
    image_caption=value,
    max_points=int(resp.get("maxPoints")),
    teacher_id=flask.session["login_hint"])
db.session.add(new_attachment)
db.session.commit()

Recupera le credenziali dell'insegnante

Trova il percorso che pubblica l'iframe della vista studente. Subito dopo aver archiviato la risposta dello studente nel tuo database locale, recupera le credenziali dell'insegnante dal tuo spazio di archiviazione locale. Questa procedura dovrebbe essere immediata, visto la preparazione nei due passaggi precedenti. Puoi usarli anche per creare una nuova istanza del servizio Classroom per l'utente insegnante:

Python

Nell'esempio fornito, è nel metodo load_activity_attachment del file webapp/attachment_routes.py.

# Create an instance of the Classroom service using the tokens for the
# teacher that created the attachment.

# We're assuming that there are already credentials in the session, which
# should be true given that we are adding this within the Student View
# route; we must have had valid credentials for the student to reach this
# point. The student credentials will be valid to construct a Classroom
# service for another user except for the tokens.
if not flask.session.get("credentials"):
    raise ValueError(
        "No credentials found in session for the requested user.")

# Make a copy of the student credentials so we don't modify the original.
teacher_credentials_dict = deepcopy(flask.session.get("credentials"))

# Retrieve the requested user's stored record.
teacher_record = User.query.get(attachment.teacher_id)

# Apply the user's tokens to the copied credentials.
teacher_credentials_dict["refresh_token"] = teacher_record.refresh_token
teacher_credentials_dict["token"] = teacher_record.access_token

# Construct a temporary credentials object.
teacher_credentials = google.oauth2.credentials.Credentials(
    **teacher_credentials_dict)

# Refresh the credentials if necessary; we don't know when this teacher last
# made a call.
if teacher_credentials.expired:
    teacher_credentials.refresh(Request())

# Request the Classroom service for the specified user.
teacher_classroom_service = googleapiclient.discovery.build(
    serviceName=CLASSROOM_API_SERVICE_NAME,
    version=CLASSROOM_API_VERSION,
    discoveryServiceUrl=f"https://classroom.googleapis.com/$discovery/rest?labels=ADD_ONS_ALPHA&key={GOOGLE_API_KEY}",
    credentials=teacher_credentials)

Impostare il voto di un invio

La procedura che trovi qui è identica a quella per utilizzare le credenziali dell'insegnante che ha effettuato l'accesso. Tuttavia, tieni presente che devi effettuare la chiamata con le credenziali dell'insegnante recuperate nel passaggio precedente:

Python

# Issue a PATCH request as the teacher to set the grade numerator for this
# attachment.
patch_grade_response = teacher_classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    attachmentId=flask.session["attachmentId"],
    submissionId=flask.session["submissionId"],
    # updateMask is a list of fields being modified.
    updateMask="pointsEarned",
    body=add_on_attachment_student_submission).execute()

Testa il componente aggiuntivo

Come nella procedura dettagliata precedente, crea un compito con un allegato di tipo attività come insegnante, invia una risposta come studente, quindi apri il compito nell'iframe della revisione del lavoro dello studente. Dovresti riuscire a vedere il voto comparire in momenti diversi a seconda dell'approccio all'implementazione:

  • Se hai scelto di ritrasmettere il voto quando lo studente ha completato l'attività, dovresti già vedere il voto provvisorio nell'interfaccia utente prima di aprire l'iframe della revisione del lavoro dello studente. Puoi anche visualizzarlo nell'elenco degli studenti quando apri il compito e nella casella "Voto" accanto all'iframe della revisione del lavoro dello studente.
  • Se scegli di ritrasmettere un voto quando l'insegnante apre l'iframe della revisione del lavoro dello studente, il voto dovrebbe apparire nella casella "Voto" subito dopo il caricamento dell'iframe. Come menzionato in precedenza, questa operazione può richiedere fino a 30 secondi. Dopodiché, il voto per lo studente specifico dovrebbe apparire anche nelle altre visualizzazioni del registro di Classroom.

Verifica che venga visualizzato il punteggio corretto per lo studente.

Complimenti! Puoi procedere con il passaggio successivo: creare allegati al di fuori di Google Classroom.