Voti degli allegati e pass-back dei voti

Questa è la sesta procedura dettagliata della serie di procedure dettagliate sui componenti aggiuntivi di Classroom.

In questa procedura dettagliata, modificherai l'esempio del passaggio precedente per produrre un allegato di tipo attività con voto. Inoltre, trasmetterai un voto a Google Classroom a livello di programmazione, che verrà visualizzato nel registro dei voti dell'insegnante come voto in bozza.

Questa procedura dettagliata è leggermente diversa dalle altre della serie in quanto sono presenti due possibili approcci per trasmettere i voti a Classroom. Entrambi hanno un impatto distinto sulle esperienze di sviluppatori e utenti; prendili in considerazione entrambi durante la progettazione del componente aggiuntivo di Classroom. Per ulteriori informazioni sulle opzioni di implementazione, consulta la pagina della guida Interagire con gli allegati per ulteriori discussioni su.

Tieni presente che le funzionalità di valutazione dell'API sono facoltative. Possono essere utilizzate 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 dell'allegato.
  • Assegna un punteggio a livello di programmazione ai contenuti inviati dallo studente e imposta il numeratore del voto dell'allegato.
  • Implementa due approcci per trasmettere il voto dei contenuti inviati a Classroom utilizzando le credenziali dell'insegnante con accesso o offline.

Al termine, i voti vengono visualizzati nel registro dei voti di Classroom dopo l'attivazione del comportamento di passback. Il momento esatto in cui ciò avviene dipende dall'approccio di implementazione.

Ai fini di questo esempio, riutilizza l'attività della procedura dettagliata precedente, in cui a uno studente viene mostrata l'immagine di un famoso monumento e gli viene chiesto di inserirne il nome. Assegna il punteggio massimo all'allegato se lo studente inserisce il nome corretto, altrimenti zero.

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

Il componente aggiuntivo può impostare sia il numeratore che il denominatore del voto per un allegato. Questi valori vengono impostati rispettivamente utilizzando i valori pointsEarned e maxPoints nell'API. Una scheda allegato nell'interfaccia utente di Classroom mostra il valore maxPoints quando è stato impostato.

Esempio di più allegati con maxPoints in un compito

Figura 1. L'interfaccia utente di creazione dei compiti con tre schede allegati di componenti aggiuntivi con maxPoints impostato.

L'API dei componenti aggiuntivi di Classroom consente di configurare le impostazioni e impostare il punteggio ottenuto per i voti degli allegati. Questi non sono gli stessi dei voti dei compiti. Tuttavia, le impostazioni dei voti dei compiti seguono le impostazioni dei voti degli allegati dell'allegato con l'etichetta Sincronizzazione dei voti sulla relativa scheda allegato. Quando l'allegato "Sincronizzazione dei voti" imposta pointsEarned per i contenuti inviati da uno studente, imposta anche il voto in bozza dello studente per il compito.

In genere, il primo allegato aggiunto al compito che imposta maxPoints riceve l'etichetta "Sincronizzazione dei voti". Per un esempio dell'etichetta "Sincronizzazione dei voti", consulta l'esempio dell'interfaccia utente di creazione dei compiti mostrato nella Figura 1. Tieni presente che la scheda "Allegato 1" ha l'etichetta "Sincronizzazione dei voti" e che il voto del compito nella casella rossa è stato aggiornato a 50 punti. Tieni presente inoltre che, sebbene la Figura 1 mostri tre schede allegati, solo una scheda ha l'etichetta "Sincronizzazione dei voti". Si tratta di una limitazione fondamentale dell'implementazione attuale: solo un allegato può avere l'etichetta "Sincronizzazione dei voti".

Se sono presenti più allegati con maxPoints impostato, la rimozione dell' allegato con "Sincronizzazione dei voti" non attiva "Sincronizzazione dei 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 dei voti" né per vedere quanti allegati ha un determinato compito.

Impostare il voto massimo di un allegato

Questa sezione descrive come impostare il denominatore per il voto di un allegato, ovvero il punteggio massimo possibile che tutti gli studenti possono ottenere per i loro contenuti inviati. Per farlo, imposta il valore maxPoints dell'allegato.

Per abilitare le funzionalità di valutazione è necessaria solo una piccola modifica all'implementazione esistente. Quando crei un allegato, aggiungi il maxPoints valore in lo stesso AddOnAttachment oggetto che contiene 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 a titolo dimostrativo:

Python

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

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, memorizza anche il valore maxPoints nel database degli allegati locale. In questo modo, non dovrai effettuare una chiamata API aggiuntiva in un secondo momento durante la valutazione dei contenuti inviati dagli studenti. Tieni presente, tuttavia, 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 a livello di compito maxPoints. Quando lo fai, passa itemId nel campo CourseWork.id.

Ora aggiorna il modello di 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. Se segui l'esempio fornito, puoi trovare questo campo 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)

Torna alla richiesta CREATE di courses.courseWork.addOnAttachments. Memorizza 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()

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

Impostare il voto dei contenuti inviati da uno studente in Classroom

Questa sezione descrive come impostare il numeratore per il voto di un allegato, ovvero il punteggio di un singolo studente per l'allegato. Per farlo, imposta il valore pointsEarned dei contenuti inviati da uno studente per l'allegato.

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'ambito OAuth teacher. Non devi concedere l'ambito teacher agli utenti studenti. In questo modo, quando gli studenti interagiscono con il componente aggiuntivo, potrebbero verificarsi comportamenti imprevisti, ad esempio il caricamento dell'iframe della visualizzazione dell'insegnante anziché dell'iframe della visualizzazione dello studente. Pertanto, hai due opzioni per impostare pointsEarned:

  • Utilizzando le credenziali dell'insegnante con accesso.
  • Utilizzando le credenziali dell'insegnante memorizzate (offline).

Le sezioni seguenti illustrano i compromessi di ogni approccio prima di dimostrare ogni implementazione. Tieni presente che gli esempi forniti dimostrano entrambi gli approcci per trasmettere un voto a Classroom. Consulta le istruzioni specifiche per la lingua riportate di seguito per scoprire come selezionare un approccio quando esegui gli esempi forniti:

Python

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

Impostare i voti utilizzando le credenziali dell'insegnante con accesso

Utilizza le credenziali dell'utente con accesso per inviare la richiesta di impostazione di pointsEarned. Questo dovrebbe sembrare abbastanza intuitivo in quanto rispecchia il resto dell'implementazione finora e richiede poco sforzo per essere realizzato.

Tuttavia, tieni presente che l'insegnante interagisce solo con i contenuti inviati dallo studente nell'iframe di revisione dei lavori degli studenti. Questo ha alcune implicazioni importanti:

  • Nessun voto viene inserito in Classroom finché l'insegnante non esegue un'azione nell'interfaccia utente di Classroom.
  • Un insegnante potrebbe dover aprire tutti i contenuti inviati dagli studenti per inserire tutti i voti degli studenti.
  • Si verifica un breve ritardo tra la ricezione del voto da parte di Classroom e la sua visualizzazione nell'interfaccia utente di Classroom. Il ritardo è in genere di 5-10 secondi, ma può arrivare fino a 30 secondi.

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

Per implementare questo approccio, aggiungi una chiamata API aggiuntiva alla route di revisione dei lavori degli studenti esistente.

Dopo aver recuperato i record dei contenuti inviati dagli studenti e degli allegati, valuta i contenuti inviati dallo studente e memorizza il voto risultante. Imposta il voto nel pointsEarned campo di un AddOnAttachmentStudentSubmission oggetto. Infine, invia una PATCH richiesta all' courses.courseWork.addOnAttachments.studentSubmissions endpoint con l' AddOnAttachmentStudentSubmission istanza nel corpo della richiesta. Tieni presente che dobbiamo anche specificare pointsEarned in 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 dell'insegnante offline

Il secondo approccio all'impostazione dei voti richiede l'utilizzo di credenziali memorizzate per l'insegnante che ha creato l'allegato. Questa implementazione richiede di creare le credenziali utilizzando i token di accesso e di aggiornamento di un insegnante autorizzato in precedenza e poi di utilizzare queste credenziali per impostare pointsEarned.

Un vantaggio fondamentale di questo approccio è che i voti vengono inseriti senza richiedere l'intervento dell'insegnante nell'interfaccia utente di Classroom, evitando i problemi menzionati sopra. Il risultato è che gli utenti finali percepiscono l'esperienza di valutazione come fluida ed efficiente. Inoltre, questo approccio ti consente di scegliere il momento in cui trasmettere 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 degli utenti per memorizzare un token di accesso.
  2. Modifica i record del database degli allegati per memorizzare un ID insegnante.
  3. Recupera le credenziali dell'insegnante e (facoltativamente) crea una nuova istanza del servizio Classroom.
  4. Imposta il voto dei contenuti inviati.

Ai fini di questa dimostrazione, imposta il voto quando lo studente completa l'attività, ovvero quando invia il modulo nella route della visualizzazione dello studente.

Modificare i record del database degli utenti per memorizzare 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 serie di procedure dettagliate finora, lo schema della tabella User dovrebbe già memorizzare un token di aggiornamento. La memorizzazione del token di aggiornamento è sufficiente quando effettui chiamate API solo con l'utente con accesso, poiché ricevi un token di accesso come parte del flusso di autenticazione.

Tuttavia, ora devi effettuare chiamate come utente diverso da quello con accesso, il che significa che il flusso di autenticazione non è disponibile. Pertanto, devi memorizzare il token di accesso insieme al token di aggiornamento. Aggiorna lo schema della tabella User in modo da includere un token di accesso:

Python

Nell'esempio fornito, questo 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 il codice che crea o aggiorna un record User in modo da memorizzare anche il token di accesso:

Python

Nell'esempio fornito, questo 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 memorizzare un ID insegnante

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

  • Memorizza una mappatura locale delle qualifiche dell'insegnante agli ID dei corsi. 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 ottenere gli insegnanti attuali. Quindi, esegui una query sui record utente locali per individuare le credenziali dell'insegnante corrispondenti.
  • Quando crei un allegato di un componente aggiuntivo, memorizza un ID insegnante nel database degli allegati locale. Quindi, recupera le credenziali dell'insegnante da attachmentId passato all'iframe della visualizzazione dello studente.

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

Aggiungi un campo ID insegnante alla tabella Attachment del database:

Python

Nell'esempio fornito, questo 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 il codice che crea o aggiorna un record Attachment in modo da memorizzare anche l'ID del creatore:

Python

Nell'esempio fornito, questo si trova 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()

Recuperare le credenziali dell'insegnante

Trova la route che gestisce l'iframe della visualizzazione dello studente. Subito dopo aver memorizzato la risposta dello studente nel database locale, recupera le credenziali dell'insegnante dalla memoria locale. Questa operazione dovrebbe essere semplice, data la preparazione nei due passaggi precedenti. Puoi anche utilizzarli per creare una nuova istanza del servizio Classroom per l'utente insegnante:

Python

Nell'esempio fornito, questo si trova 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,
    credentials=teacher_credentials)

Impostare il voto dei contenuti inviati

La procedura da questo punto in poi è identica a quella dell'utilizzo delle credenziali dell'insegnante con 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()

Testare il componente aggiuntivo

Analogamente alla procedura dettagliata precedente, crea un compito con un allegato di tipo attività come insegnante, invia una risposta come studente, quindi apri i contenuti inviati nell'iframe di revisione dei lavori degli studenti. Dovresti essere in grado di vedere il voto visualizzato in momenti diversi a seconda dell'approccio di implementazione:

  • Se hai scelto di trasmettere un voto quando lo studente ha completato l'attività, dovresti già vedere il voto in bozza nell'interfaccia utente prima di aprire l'iframe di revisione dei lavori degli studenti. Puoi anche visualizzarlo nell'elenco degli studenti quando apri il compito e nella casella "Voto" accanto all'iframe di revisione dei lavori degli studenti.
  • Se hai scelto di trasmettere un voto quando l'insegnante apre l'iframe di revisione dei lavori degli studenti, il voto dovrebbe essere visualizzato nella casella "Voto" subito dopo il caricamento dell'iframe. Come indicato sopra, questa operazione può richiedere fino a 30 secondi. Successivamente, il voto per lo studente specifico dovrebbe essere visualizzato anche nelle altre visualizzazioni del registro dei voti di Classroom.

Verifica che venga visualizzato il punteggio corretto per lo studente.

Complimenti! Ora puoi procedere al passaggio successivo: creare allegati al di fuori di Google Classroom.