Este é o sexto tutorial sobre os complementos do Google Sala de Aula série de tutoriais.
Neste tutorial, você vai modificar o exemplo da etapa anterior. para produzir um anexo do tipo atividade com nota. Você também passa uma nota ao Google Sala de Aula de forma programática, que aparece na nota do professor como uma nota temporária.
Este tutorial é um pouco diferente de outros da série, porque há apresentar duas abordagens possíveis para passar notas de volta para Google Sala de Aula. Ambos têm impactos distintos sobre o desenvolvedor e o usuário experiências considere ambos ao criar seu complemento do Google Sala de Aula. Leia nossa página do guia de interação com anexos para uma discussão adicional sobre as opções de implementação.
Os recursos de avaliação na API são opcionais. Eles podem ser usados com qualquer anexo do tipo de atividade.
Neste tutorial, você vai fazer o seguinte:
- Modifique as solicitações anteriores de criação de anexos para API Classroom para também definir o denominador de nota do anexo.
- Dê uma pontuação programática ao envio do estudante e defina a configuração numerador de notas.
- Implementar duas abordagens para passar a nota do trabalho para Google Sala de Aula usando credenciais de professor que fez login ou off-line.
Após a conclusão, as notas aparecem no boletim de notas do Google Sala de Aula após o comportamento de passback for acionado. O momento exato em que isso acontece depende a abordagem de implementação.
Para este exemplo, reutilize a atividade do exemplo anterior em que um estudante vê a imagem de um ponto turístico famoso e o nome dele será solicitado. Atribua notas completas para o anexo se o aluno insere o nome correto, caso contrário zero.
Entender o recurso de avaliação da API de complementos do Google Sala de Aula
Seu complemento pode definir o numerador e o denominador de nota de uma
anexo. Elas são definidas usando pointsEarned
e maxPoints
, respectivamente.
valores na API. Um card de anexo na interface do Google Sala de Aula mostra
o valor maxPoints
quando tiver sido definido.
Figura 1. A interface de criação de atividades com três cartões de anexo de complementos que
têm maxPoints
definido.
Com a API Classroom Add-ons, é possível definir as configurações
a pontuação recebida para as notas Anexo. Eles não são iguais aos
atividades. No entanto, as configurações de nota das tarefas seguem
as configurações de nota do anexo com o marcador Sincronização de notas ativado.
o cartão de anexo. Quando a opção "Sincronizar notas" o anexo define pointsEarned
para um
envio de um aluno, ele também define a nota temporária dele para a tarefa.
Geralmente, o primeiro anexo
adicionado à tarefa que define
maxPoints
recebe a mensagem "Sincronização de notas". rótulo. Conferir a interface de criação de atividades
mostrado na Figura 1 para um exemplo da sincronização de notas rótulo. Observe que
o "Anexo 1" o card tem a opção "Sincronizar notas". e que a nota da tarefa
na caixa vermelha foi atualizada para 50 pontos. Observe também que, embora a Figura 1
mostra três cards de anexo, apenas um card tem a "Sincronização de notas" rótulo. Isso é
uma limitação importante da implementação atual: somente um anexo pode ter
a opção "Sincronizar notas" rótulo.
Se houver vários anexos com maxPoints
definido, remova o
anexo com a opção "Sincronizar notas" não ativa a opção "Sincronizar notas" em qualquer um dos
anexos restantes. Adicionar outro anexo que define maxPoints
ativa
As notas são sincronizadas no novo anexo, e a nota máxima da tarefa é ajustada para
são correspondentes. Não existe um mecanismo para ver programaticamente qual anexo tem a
"Sincronização de notas" marcador nem para ver quantos anexos uma atividade específica tem.
Definir a nota máxima de um anexo
Esta seção descreve a configuração do denominador de uma nota de anexo. que
é a pontuação máxima que todos os estudantes podem atingir no
envios. Para fazer isso, defina o valor maxPoints
do anexo.
É necessária apenas uma pequena modificação na implementação atual para ativar
os recursos de avaliação. Ao criar um anexo, adicione o valor maxPoints
em
O mesmo objeto AddOnAttachment
que contém a studentWorkReviewUri
,
teacherViewUri
e outros campos de anexo.
A pontuação máxima padrão de uma nova atividade é 100. Sugerimos
definindo maxPoints
como um valor diferente de 100, para que você possa verificar se o
as notas estão sendo definidas corretamente. Defina maxPoints
como 50 como demonstração:
Python
Adicione o campo maxPoints
ao criar o objeto attachment
, apenas
antes de emitir uma solicitação CREATE
para o
endpoint courses.courseWork.addOnAttachments
. Você pode encontrar isso na
webapp/attachment_routes.py
, se estiver seguindo nosso exemplo fornecido.
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}",
}
Para esta demonstração, você também armazena o valor maxPoints
em
seu banco de dados de anexos local; Isso economiza a necessidade de fazer uma chamada de API adicional
mais tarde ao avaliar os envios dos alunos. Observe, no entanto, que é possível
os professores alteram as configurações de notas das atividades independentemente do complemento. Enviar
uma solicitação GET
ao endpoint courses.courseWork
para consultar
valor maxPoints
no nível da atribuição. Ao fazer isso, transmita o itemId
na
CourseWork.id
.
Agora atualize seu modelo de banco de dados para também armazenar o valor maxPoints
do anexo.
Recomendamos usar o valor maxPoints
da resposta CREATE
:
Python
Primeiro, adicione um campo max_points
à tabela Attachment
. Você encontra este
no arquivo webapp/models.py
, se estiver seguindo o exemplo fornecido.
# 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)
Retorne à solicitação CREATE
courses.courseWork.addOnAttachments
. Armazenamento
o valor maxPoints
retornado na resposta.
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()
Agora o anexo tem uma nota máxima. Você poderá testar esse comportamento agora; adicionar um anexo a uma nova tarefa e observar que o cartão do anexo mostra "Sincronização de notas" e os "Pontos" da atividade as mudanças de valor.
Definir a nota de envio dos estudantes no Google Sala de Aula
Esta seção descreve a configuração do numerador de uma nota de anexo. isto é,
a pontuação de um estudante específico para o anexo. Para fazer isso, defina um nome de
valor pointsEarned
do envio.
Agora você tem uma decisão importante a tomar: como seu complemento deve enviar
para definir pointsEarned
?
O problema é que a configuração de pointsEarned
exige o escopo teacher
do OAuth.
Não conceda o escopo teacher
a usuários estudantes. isso pode resultar em
comportamento inesperado na interação dos estudantes com seu complemento, como carregar
Iframe da visualização do professor em vez do iframe da visualização dos alunos. Portanto, você tem duas
opções sobre como definir pointsEarned
:
- Usando as credenciais do professor que fez login.
- Usar credenciais de professor armazenadas (off-line).
As seções a seguir discutem as vantagens e desvantagens de cada abordagem antes para demonstrar cada implementação. Nossos exemplos mostram as duas abordagens para passar notas no Google Sala de Aula; consulte em algumas linguagens específicas para saber como selecionar uma abordagem ao executando os exemplos fornecidos:
Python
Encontre a declaração SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS
na parte de cima.
de webapp/attachment_routes.py
. Defina esse valor como True
para fazer a transmissão
com as credenciais do professor que fez login. Defina esse valor como False
passar as notas usando as credenciais armazenadas quando o aluno enviar o
atividades.
Definir notas usando as credenciais do professor que fez login
Use as credenciais do usuário que fez login para emitir a solicitação e definir pointsEarned
.
Isso deve parecer bastante intuitivo, já que reflete o restante da implementação
até agora e requer pouco esforço para ser alcançado.
No entanto, considere que o professor interage apenas com o sistema no iframe "Revisão dos trabalhos dos alunos". Isso tem algumas implicações:
- Nenhuma nota é preenchida no Google Sala de Aula até que o professor tire na interface do Google Sala de Aula.
- Um professor pode ter que abrir todos os envios de estudantes para preencher todas as notas dos alunos.
- Há um pequeno atraso entre o recebimento das notas pelo Google Sala de Aula e a aparência dele na interface do Google Sala de Aula. O atraso é geralmente de 5 a 10 segundos, mas pode durar até 30 segundos.
A combinação desses fatores significa que os professores talvez precisem um trabalho manual considerável e demorado para preencher totalmente as notas de uma turma.
Para implementar essa abordagem, adicione mais uma chamada de API à sua conta de Trajeto da revisão do trabalho.
Após buscar o envio do estudante e os registros de anexos, avalie a
o envio do aluno e armazenar a nota resultante. Defina a nota no
Campo pointsEarned
de um objeto AddOnAttachmentStudentSubmission
. Por fim,
emitir uma solicitação PATCH
para o
endpoint courses.courseWork.addOnAttachments.studentSubmissions
com o
AddOnAttachmentStudentSubmission
no corpo da solicitação. Nós
também é necessário especificar pointsEarned
no updateMask
da solicitação 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()
Definir notas usando credenciais de professor off-line
A segunda abordagem para definir notas exige o uso de credenciais armazenadas
para o professor que criou o anexo. Essa implementação requer que
você cria credenciais usando atualizações de um professor autorizado
tokens de acesso e use essas credenciais para definir pointsEarned
.
Uma vantagem essencial dessa abordagem é que as notas são preenchidas sem ação dos professores na interface do Google Sala de Aula, evitando os problemas mencionado acima. Os usuários finais percebem a experiência de avaliação de forma simples e eficiente. Além disso, essa abordagem permite escolher momento em que você passa as notas, como quando os estudantes concluem as ou de forma assíncrona.
Conclua as tarefas a seguir para implementar essa abordagem:
- Modifique os registros do banco de dados do usuário para armazenar um token de acesso.
- Modifique os registros do banco de dados de anexos para armazenar um ID de professor.
- Recupere as credenciais do professor e, se quiser, crie um novo Instância de serviço do Sala de Aula.
- Definir a nota de uma atividade.
Para esta demonstração, defina a nota quando o aluno terminar a atividade; ou seja, quando o estudante envia o formulário na visualização do estudante trajeto.
Modifique os registros do banco de dados do usuário para armazenar o token de acesso
São necessários dois tokens exclusivos para fazer chamadas de API: o token de atualização e o
token de acesso. Se você tem seguido a série de tutoriais até agora, seu
O esquema de tabela User
já deve armazenar um token de atualização. Como armazenar a atualização
é suficiente quando você só fizer chamadas de API com o usuário conectado, conforme
você recebe um token de acesso como parte do fluxo de autenticação.
No entanto, agora você precisa fazer chamadas como outra pessoa que não seja o usuário que fez login.
ou seja, o fluxo de autenticação não está disponível. Portanto, você precisa armazenar
com o token de atualização. Atualize o esquema da tabela User
para
incluem um token de acesso:
Python
No exemplo fornecido, isso está no arquivo 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())
Em seguida, atualize qualquer código que crie ou atualize um registro User
para também armazenar o
token de acesso:
Python
No exemplo fornecido, isso está no arquivo 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()
Modificar registros do banco de dados de anexos para armazenar um ID de professor
Para dar a nota a uma atividade, chame pointsEarned
como
professor do curso. Há várias maneiras de fazer isso:
- Armazenar um mapeamento local das credenciais de professores para os IDs dos cursos. Observe, no entanto, mesmo professor nem sempre está associado a um curso específico.
- Emitir solicitações
GET
para o endpointcourses
da API Classroom para encontrar os professores atuais. Em seguida, consulte os registros locais dos usuários com as credenciais do professor. - Ao criar um anexo de complemento, armazene um ID de professor no
no banco de dados de anexos. Em seguida, recupere as credenciais de professor na
attachmentId
transmitido para o iframe do Student View.
Este exemplo demonstra a última opção, já que você está definindo notas quando o o aluno conclui um anexo de atividade.
Adicione um campo de ID de professor à tabela Attachment
do banco de dados:
Python
No exemplo fornecido, isso está no arquivo 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))
Em seguida, atualize qualquer código que crie ou atualize um registro Attachment
para também
armazenar o ID do criador:
Python
No exemplo fornecido, isso está no método create_attachments
na
arquivo 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()
Recuperar as credenciais do professor
Encontre o trajeto que veicula o iframe do Student View. Imediatamente após o armazenamento a resposta do aluno em seu banco de dados local, recupere a resposta credenciais do seu armazenamento local. Isso deve ser simples, considerando que de preparação nas duas etapas anteriores. Você também pode usá-los para criar um novo instância do serviço do Google Sala de Aula para o usuário professor:
Python
No exemplo fornecido, isso está no método load_activity_attachment
da
no arquivo 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)
Definir a nota de um envio
O procedimento aqui é idêntico ao de usar a conta do professor que fez login credenciais. Mas você deve conversar com o professor credenciais recuperadas na etapa anterior:
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()
Testar o complemento
Semelhante ao tutorial anterior, crie uma atividade com um tipo de atividade anexo como professor, enviar uma resposta como aluno e abrir no iframe "Revisão dos trabalhos dos alunos". Aparecerá nota aparecem em momentos diferentes, dependendo da abordagem de implementação:
- Se você optar por transmitir uma nota quando o aluno concluir a atividade, você já deve ver a nota temporária na interface antes de abrir o iframe "Revisão dos trabalhos dos alunos". Ele também aparece na lista de alunos quando abrindo a tarefa e, na coluna "Nota", caixa ao lado de "Trabalhos dos alunos" Revisar iframe.
- Se você escolheu transmitir uma nota quando o professor abrir os trabalhos dos alunos Revisar iframe. A nota vai aparecer em "Nota". logo após o o iframe é carregado. Conforme mencionado acima, isso pode levar até 30 segundos. Depois disso, a nota do aluno específico também deve aparecer na outras visualizações do boletim de notas do Google Sala de Aula.
Confirme se aparece a pontuação correta para o estudante.
Parabéns! Você está pronto para prosseguir para a próxima etapa: como criar anexos fora do Google Sala de Aula.