Este é o quarto tutorial da série sobre os complementos do Google Sala de Aula.
Neste tutorial, você vai interagir com a API Google Classroom para criar anexos. Você fornece rotas para os usuários visualizarem o conteúdo do anexo. As visualizações variam de acordo com a função do usuário na turma. Este tutorial aborda anexos de tipo de conteúdo, que não exigem um envio do estudante.
Neste tutorial, você vai fazer o seguinte:
- Recupere e use os seguintes parâmetros de consulta de complemento:
addOnToken
: um token de autorização transmitido para a visualização de descoberta de anexos.itemId
: um identificador exclusivo do CourseWork, CourseWorkMaterial ou Announcement que recebe o anexo do complemento.itemType
: "courseWork", "courseWorkMaterials" ou "announcement".courseId
: um identificador exclusivo do curso do Google Sala de Aula em que a atividade está sendo criada.attachmentId
: um identificador exclusivo atribuído pelo Google Sala de Aula a um anexo de complemento após a criação.
- Implemente o armazenamento permanente para anexos de tipo de conteúdo.
- Fornece rotas para criar anexos e veicular os iframes da visualização do professor e do estudante.
- Emita as seguintes solicitações para a API de complementos do Google Classroom:
- Crie um novo anexo.
- Recebe o contexto do complemento, que identifica se o usuário conectado é um estudante ou professor.
Depois de terminar, você pode criar anexos de tipo de conteúdo nas atividades usando a interface do Google Sala de Aula quando estiver conectado como professor. Os professores e estudantes da turma também podem acessar o conteúdo.
Ativar a API Classroom
Faça chamadas para a API Classroom começando por esta etapa. A API precisa ser ativada no projeto do Google Cloud antes que você possa fazer chamadas para ela. Acesse a entrada da biblioteca da API Google Classroom e escolha Ativar.
Processar os parâmetros de consulta da visualização de descoberta de anexos
Como já discutimos, o Google Sala de Aula transmite parâmetros de consulta ao carregar a visualização de descoberta de anexos no iframe:
courseId
: o ID do curso atual do Google Sala de Aula.itemId
: um identificador exclusivo do CourseWork, CourseWorkMaterial ou Announcement que recebe o anexo do complemento.itemType
: "courseWork", "courseWorkMaterials" ou "announcement".addOnToken
: um token usado para autorizar determinadas ações de complementos do Google Sala de Aula.login_hint
: o ID do Google do usuário atual.
Este tutorial aborda courseId
, itemId
, itemType
e addOnToken
.
Mantenha e transmita esses valores ao fazer chamadas para a API Classroom.
Como na etapa anterior do tutorial, armazene os valores de parâmetro de consulta transmitidos na sessão. É importante fazer isso quando a visualização de descoberta de anexos é aberta pela primeira vez, já que essa é a única oportunidade de o Google Sala de Aula transmitir esses parâmetros de consulta.
Python
Navegue até o arquivo do servidor Flask que fornece rotas para a visualização de descoberta de anexos (attachment-discovery-routes.py
se você estiver seguindo o exemplo fornecido). Na parte de cima da rota de destino do complemento
(/classroom-addon
no exemplo fornecido), recupere e armazene os
parâmetros de consulta courseId
, itemId
, itemType
e addOnToken
.
# Retrieve the itemId, courseId, and addOnToken query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("addOnToken"):
flask.session["addOnToken"] = flask.request.args.get("addOnToken")
Grave esses valores na sessão apenas se eles estiverem presentes. Eles não serão transmitidos novamente se o usuário voltar à visualização de descoberta de anexos mais tarde sem fechar o iframe.
Adicionar armazenamento permanente para anexos de tipo de conteúdo
Você precisa de um registro local de todos os anexos criados. Isso permite pesquisar o conteúdo selecionado pelo professor usando identificadores fornecidos pela Sala de Aula.
Configurar um esquema de banco de dados para um Attachment
. O exemplo fornecido apresenta anexos que mostram uma imagem e uma legenda. Um Attachment
contém os seguintes atributos:
attachment_id
: um identificador exclusivo de um anexo. Atribuído pelo Classroom e retornado na resposta ao criar um anexo.image_filename
: o nome do arquivo local da imagem a ser exibida.image_caption
: a legenda que será mostrada com a imagem.
Python
Estenda a implementação do SQLite e do flask_sqlalchemy
das etapas anteriores.
Navegue até o arquivo em que você definiu a tabela de usuários (models.py
se você estiver seguindo nosso exemplo). Adicione o seguinte na parte de baixo do arquivo, abaixo da classe User
.
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))
Importe a nova classe "Attachment" para o arquivo do servidor com suas rotas de processamento de anexos.
Configurar novas rotas
Comece esta etapa do tutorial configurando algumas páginas novas no aplicativo. Eles permitem que um usuário crie e veja conteúdo usando nosso complemento.
Adicionar rotas de criação de anexos
Você precisa de páginas para que o professor selecione conteúdo e emita solicitações de criação de anexos. Implemente a rota /attachment-options
para mostrar as opções de conteúdo
que o professor pode selecionar. Você também precisa de modelos para as páginas de seleção de conteúdo e
confirmação de criação. Os exemplos fornecidos contêm modelos para isso e também podem mostrar as solicitações e respostas da API Classroom.
Como alternativa, você pode modificar a página de destino da visualização de descoberta de anexos
para mostrar as opções de conteúdo em vez de criar a nova página
/attachment-options
. Recomendamos criar uma página para este exercício e preservar o comportamento de SSO implementado na segunda etapa do tutorial, como a revogação das permissões do app. Eles serão úteis ao criar e testar seu complemento.
Um professor pode selecionar um pequeno conjunto de imagens legendadas no exemplo fornecido. Fornecemos quatro imagens de pontos turísticos famosos cujas legendas são derivadas dos nomes dos arquivos.
Python
No exemplo fornecido, isso está no arquivo webapp/attachment_routes.py
.
@app.route("/attachment-options", methods=["GET", "POST"])
def attachment_options():
"""
Render the attachment options page from the "attachment-options.html"
template.
This page displays a grid of images that the user can select using
checkboxes.
"""
# A list of the filenames in the static/images directory.
image_filenames = os.listdir(os.path.join(app.static_folder, "images"))
# The image_list_form_builder method creates a form that displays a grid
# of images, checkboxes, and captions with a Submit button. All images
# passed in image_filenames will be shown, and the captions will be the
# title-cased filenames.
# The form must be built dynamically due to limitations in WTForms. The
# image_list_form_builder method therefore also returns a list of
# attribute names in the form, which will be used by the HTML template
# to properly render the form.
form, var_names = image_list_form_builder(image_filenames)
# If the form was submitted, validate the input and create the attachments.
if form.validate_on_submit():
# Build a dictionary that maps image filenames to captions.
# There will be one dictionary entry per selected item in the form.
filename_caption_pairs = construct_filename_caption_dictionary_list(
form)
# Check that the user selected at least one image, then proceed to
# make requests to the Classroom API.
if len(filename_caption_pairs) > 0:
return create_attachments(filename_caption_pairs)
else:
return flask.render_template(
"create-attachment.html",
message="You didn't select any images.",
form=form,
var_names=var_names)
return flask.render_template(
"attachment-options.html",
message=("You've reached the attachment options page. "
"Select one or more images and click 'Create Attachment'."),
form=form,
var_names=var_names,
)
Isso produz uma página "Criar anexos" semelhante a esta:
O professor pode selecionar várias imagens. Crie um anexo para cada imagem
que o professor selecionou no método create_attachments
.
Emitir solicitações de criação de anexos de problemas
Agora que você sabe quais conteúdos o professor quer anexar, envie solicitações à API Classroom para criar anexos na atividade. Armazene os detalhes do anexo no banco de dados depois de receber uma resposta da API Classroom.
Comece criando uma instância do serviço Google Sala de Aula:
Python
No exemplo fornecido, isso está no arquivo webapp/attachment_routes.py
.
def create_attachments(filename_caption_pairs):
"""
Create attachments and show an acknowledgement page.
Args:
filename_caption_pairs: A dictionary that maps image filenames to
captions.
"""
# Get the Google Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom",
version="v1",
credentials=credentials)
Envie uma solicitação CREATE
para o endpoint courses.courseWork.addOnAttachments
. Para cada imagem selecionada pelo professor, primeiro crie um
objeto AddOnAttachment
:
Python
No exemplo fornecido, essa é uma continuação do método create_attachments
.
# Create a new attachment for each image that was selected.
attachment_count = 0
for key, value in filename_caption_pairs.items():
attachment_count += 1
# Create a dictionary with values for the AddOnAttachment object fields.
attachment = {
# Specifies the route for a teacher user.
"teacherViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True),
},
# Specifies the route for a student user.
"studentViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True)
},
# The title of the attachment.
"title": f"Attachment {attachment_count}",
}
Pelo menos os campos teacherViewUri
, studentViewUri
e title
precisam ser fornecidos para cada anexo. Os teacherViewUri
e studentViewUri
representam os URLs carregados quando o anexo é aberto pelo
tipo de usuário respectivo.
Envie o objeto AddOnAttachment
no corpo de uma solicitação para o endpoint addOnAttachments
apropriado. Forneça os identificadores courseId
, itemId
, itemType
e addOnToken
com cada solicitação.
Python
No exemplo fornecido, essa é uma continuação do método create_attachments
.
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
# Issue a request to create the attachment.
resp = parent.addOnAttachments().create(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"],
addOnToken=flask.session["addOnToken"],
body=attachment).execute()
Crie uma entrada para esse anexo no seu banco de dados local para que você possa carregar o conteúdo correto mais tarde. O Google Sala de Aula retorna um valor id
exclusivo
na resposta à solicitação de criação. Use esse valor como a chave primária no nosso
banco de dados. O Google Sala de Aula também transmite o parâmetro de consulta attachmentId
ao abrir as visualizações do professor e do estudante:
Python
No exemplo fornecido, essa é uma continuação do método create_attachments
.
# Store the value 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)
db.session.add(new_attachment)
db.session.commit()
Nesse ponto, considere encaminhar o usuário para uma página de confirmação, reconhecendo que ele criou anexos.
Permitir anexos do seu complemento
Agora é um bom momento para adicionar os endereços adequados ao campo "Prefixos de URI de anexo permitidos" na página Configuração do app do SDK do Google Workspace Marketplace. Seu complemento só pode criar anexos de um dos prefixos de URI listados nesta página. Essa é uma medida de segurança para reduzir a possibilidade de ataques "man-in-the-middle".
A abordagem mais simples é fornecer seu domínio de nível superior neste campo, por exemplo, https://example.com
. https://localhost:<your port number>/
funciona se você estiver usando sua máquina local como servidor da Web.
Adicionar rotas para as visualizações do professor e do estudante
Há quatro iframes em que um complemento do Google Sala de Aula pode ser carregado. Até agora, você só criou rotas que atendem ao iframe da visualização de descoberta de anexos. Em seguida, adicione rotas para veicular os iframes das visualizações do professor e do estudante também.
O iframe da Visualização do professor é necessário para mostrar uma prévia da experiência do estudante, mas pode incluir informações adicionais ou recursos de edição.
A Visualização do estudante é a página apresentada a cada estudante quando ele abre um anexo de complemento.
Para fins deste exercício, crie uma única rota /load-content-attachment
que atenda às visualizações de professor e estudante. Use os métodos da API Classroom para determinar se o usuário é um professor ou estudante quando a página é carregada.
Python
No exemplo fornecido, isso está no arquivo webapp/attachment_routes.py
.
@app.route("/load-content-attachment")
def load_content_attachment():
"""
Load the attachment for the user's role."""
# Since this is a landing page for the Teacher and Student View iframes, we
# need to preserve the incoming query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("attachmentId"):
flask.session["attachmentId"] = flask.request.args.get("attachmentId")
Não se esqueça de autenticar o usuário neste momento. Você
também precisa processar o parâmetro de consulta login_hint
aqui e encaminhar o usuário para
seu fluxo de autorização, se necessário. Consulte os detalhes das orientações de login discutidos
em tutoriais anteriores para mais informações sobre esse fluxo.
Em seguida, envie uma solicitação para o endpoint getAddOnContext
que corresponda ao tipo de item.
Python
No exemplo fornecido, essa é uma continuação do método
load_content_attachment
.
# Create an instance of the Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom"
version="v1",
credentials=credentials)
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
addon_context_response = parent.getAddOnContext(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"]).execute()
Esse método retorna informações sobre a função do usuário atual na turma.
Alterar a visualização apresentada ao usuário dependendo da função dele. Exatamente um dos campos studentContext
ou teacherContext
é preenchido no objeto de resposta. Analise esses dados para determinar como se dirigir ao usuário.
Em qualquer caso, use o valor do parâmetro de consulta attachmentId
para saber qual
anexo recuperar do nosso banco de dados. Esse parâmetro de consulta é fornecido ao abrir os URIs da visualização do professor ou do estudante.
Python
No exemplo fornecido, essa é uma continuação do método
load_content_attachment
.
# Determine which view we are in by testing the returned context type.
user_context = "student" if addon_context_response.get(
"studentContext") else "teacher"
# Look up the attachment in the database.
attachment = Attachment.query.get(flask.session["attachmentId"])
# Set the text for the next page depending on the user's role.
message_str = f"I see that you're a {user_context}! "
message_str += (
f"I've loaded the attachment with ID {attachment.attachment_id}. "
if user_context == "teacher" else
"Please enjoy this image of a famous landmark!")
# Show the content with the customized message text.
return flask.render_template(
"show-content-attachment.html",
message=message_str,
image_filename=attachment.image_filename,
image_caption=attachment.image_caption,
responses=response_strings)
Testar o complemento
Siga estas etapas para testar a criação de anexos:
- Faça login no [Google Sala de Aula] como um dos seus usuários de teste Professor.
- Acesse a guia Atividades e crie uma nova Atividade.
- Clique no botão Complementos abaixo da área de texto e selecione o complemento. O iframe é aberto, e o complemento carrega o URI de configuração de anexo especificado na página Configuração do app do SDK do Google Workspace Marketplace.
- Escolha um conteúdo para anexar à atividade.
- Feche o iframe depois que o fluxo de criação de anexos for concluído.
Um card de anexo vai aparecer na interface de criação de atividades do Google Sala de Aula. Clique no card para abrir o iframe da visualização do professor e confirme se o anexo correto aparece. Clique no botão Atribuir.
Siga estas etapas para testar a experiência dos estudantes:
- Em seguida, faça login no Google Sala de Aula como um usuário de teste estudante na mesma turma do usuário de teste professor.
- Encontre a atividade de teste na guia "Atividades".
- Abra a atividade e clique no card de anexo para abrir o iframe da visualização do estudante.
Confirme se o anexo correto aparece para o estudante.
Parabéns! Agora você pode passar para a próxima etapa: criar anexos de tipo de atividade.