Este es el cuarto instructivo de la serie de instructivos sobre complementos de Classroom.
En este instructivo, interactuarás con la API de Google Classroom para crear archivos adjuntos. Proporcionas rutas para que los usuarios vean el contenido adjunto. Las vistas difieren según el rol del usuario en la clase. En esta explicación, se abordan los archivos adjuntos de tipo de contenido, que no requieren que los estudiantes envíen una respuesta.
Durante este recorrido, completarás las siguientes tareas:
- Recupera y usa los siguientes parámetros de consulta del complemento:
addOnToken
: Es un token de autorización que se pasa a la vista de Attachment Discovery.itemId
: Es un identificador único del CursoWork, CursoWorkMaterial o Anuncio que recibe el adjunto del complemento.itemType
: Puede ser "courseWork", "courseWorkMaterials" o "announcement".courseId
: Es un identificador único del curso de Google Classroom en el que se crea la tarea.attachmentId
: Es un identificador único que Google Classroom asigna a un adjunto de complemento después de su creación.
- Implementa el almacenamiento persistente para los archivos adjuntos de tipo de contenido.
- Proporciona rutas para crear archivos adjuntos y mostrar los iframes de la vista del profesor y la vista del estudiante.
- Envía las siguientes solicitudes a la API de Complementos de Google Classroom:
- Crea un archivo adjunto nuevo.
- Obtén el contexto del complemento, que identifica si el usuario que accedió es estudiante o profesor.
Cuando termines, podrás crear archivos adjuntos de tipo de contenido en las tareas a través de la IU de Google Classroom cuando accedas como profesor. Los profesores y los estudiantes de la clase también pueden ver el contenido.
Habilita la API de Classroom
Comienza a realizar llamadas a la API de Classroom a partir de este paso. La API debe estar habilitada para tu proyecto de Google Cloud antes de que puedas hacer llamadas a ella. Navega a la entrada de la biblioteca de la API de Google Classroom y elige Habilitar.
Cómo controlar los parámetros de consulta de la vista de descubrimiento de archivos adjuntos
Como se mencionó anteriormente, Google Classroom pasa parámetros de consulta cuando carga la vista de Descubrimiento de archivos adjuntos en el iframe:
courseId
: Es el ID del curso actual de Classroom.itemId
: Es un identificador único del CursoWork, CursoWorkMaterial o Anuncio que recibe el adjunto del complemento.itemType
: Puede ser "courseWork", "courseWorkMaterials" o "announcement".addOnToken
: Es un token que se usa para autorizar ciertas acciones de complementos de Classroom.login_hint
: Es el ID de Google del usuario actual.
En este instructivo, se abordan courseId
, itemId
, itemType
y addOnToken
.
Conserva y pasa estos valores cuando emitas llamadas a la API de Classroom.
Al igual que en el paso anterior de la guía, almacena los valores de los parámetros de consulta que se pasaron en nuestra sesión. Es importante que lo hagamos cuando se abre por primera vez la vista de Descubrimiento de archivos adjuntos, ya que esta es la única oportunidad que tiene Classroom para pasar estos parámetros de consulta.
Python
Navega al archivo del servidor de Flask que proporciona rutas para la vista de descubrimiento de archivos adjuntos (attachment-discovery-routes.py
si sigues nuestro ejemplo proporcionado). En la parte superior de la ruta de destino del complemento (/classroom-addon
en el ejemplo proporcionado), recupera y almacena los parámetros de consulta courseId
, itemId
, itemType
y 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")
Escribe estos valores en la sesión solo si están presentes. No se vuelven a pasar si el usuario regresa a la vista de Descubrimiento de archivos adjuntos más adelante sin cerrar el iframe.
Agrega almacenamiento persistente para los archivos adjuntos de tipo de contenido
Necesitas un registro local de los archivos adjuntos creados. Esto te permite buscar el contenido que seleccionó el profesor con los identificadores que proporciona Classroom.
Configura un esquema de base de datos para un Attachment
. En el ejemplo que proporcionamos, se muestran archivos adjuntos con una imagen y una leyenda. Un Attachment
contiene los siguientes atributos:
attachment_id
: Es un identificador único para un adjunto. Classroom lo asigna y se devuelve en la respuesta cuando se crea un adjunto.image_filename
: Es el nombre de archivo local de la imagen que se mostrará.image_caption
: Es el subtítulo que se mostrará con la imagen.
Python
Extiende la implementación de SQLite y flask_sqlalchemy
de los pasos anteriores.
Navega al archivo en el que definiste tu tabla de usuarios (models.py
si sigues nuestro ejemplo proporcionado). Agrega lo siguiente en la parte inferior del archivo, debajo de la clase 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))
Importa la nueva clase Attachment al archivo del servidor con tus rutas de procesamiento de archivos adjuntos.
Configurar rutas nuevas
Comienza este paso de la guía configurando algunas páginas nuevas en nuestra aplicación. Estos permiten que un usuario cree y vea contenido a través de nuestro complemento.
Agrega rutas de creación de archivos adjuntos
Necesitas páginas para que el profesor seleccione contenido y envíe solicitudes de creación de archivos adjuntos. Implementa la ruta /attachment-options
para mostrar las opciones de contenido que el profesor puede seleccionar. También necesitas plantillas para las páginas de selección de contenido y confirmación de creación. Los ejemplos que proporcionamos contienen plantillas para estos casos y también pueden mostrar las solicitudes y respuestas de la API de Classroom.
Ten en cuenta que, como alternativa, puedes modificar tu página de destino existente de la vista de Descubrimiento de archivos adjuntos para que muestre las opciones de contenido en lugar de crear la nueva página /attachment-options
. Te recomendamos que crees una página nueva para este ejercicio, de modo que conserves el comportamiento del SSO implementado en el segundo paso del recorrido, como la revocación de los permisos de la app. Estos recursos te serán útiles a medida que compiles y pruebes tu complemento.
Un profesor puede seleccionar entre un pequeño conjunto de imágenes con subtítulos en el ejemplo que proporcionamos. Proporcionamos cuatro imágenes de lugares emblemáticos famosos cuyos subtítulos se derivan de los nombres de los archivos.
Python
En el ejemplo que proporcionamos, se encuentra en el archivo 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,
)
Esto produce una página "Create Attachments" similar a la siguiente:
El profesor puede seleccionar varias imágenes. Crea un adjunto para cada imagen que el profesor seleccionó en el método create_attachments
.
Emite solicitudes de creación de archivos adjuntos
Ahora que sabes qué contenido quiere adjuntar el profesor, envía solicitudes a la API de Classroom para crear archivos adjuntos en nuestra tarea. Almacena los detalles del adjunto en tu base de datos después de recibir una respuesta de la API de Classroom.
Para comenzar, obtén una instancia del servicio de Classroom:
Python
En el ejemplo que proporcionamos, se encuentra en el archivo 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)
Envía una solicitud CREATE
al extremo courses.courseWork.addOnAttachments
. Para cada imagen que seleccione el profesor, primero construye un objeto AddOnAttachment
:
Python
En el ejemplo que proporcionamos, esta es una continuación del 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}",
}
Se deben proporcionar, al menos, los campos teacherViewUri
, studentViewUri
y title
para cada adjunto. teacherViewUri
y studentViewUri
representan las URLs que se cargan cuando el tipo de usuario respectivo abre el adjunto.
Envía el objeto AddOnAttachment
en el cuerpo de una solicitud al extremo addOnAttachments
adecuado. Proporciona los identificadores courseId
, itemId
, itemType
y addOnToken
con cada solicitud.
Python
En el ejemplo que proporcionamos, esta es una continuación del 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()
Crea una entrada para este archivo adjunto en tu base de datos local para que puedas cargar el contenido correcto más adelante. Classroom devuelve un valor id
único en la respuesta a la solicitud de creación, por lo que debes usarlo como clave principal en nuestra base de datos. Ten en cuenta también que Classroom pasa el parámetro de consulta attachmentId
cuando se abren las vistas del profesor y del estudiante:
Python
En el ejemplo que proporcionamos, esta es una continuación del 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()
En este punto, considera redireccionar al usuario a una página de confirmación para indicarle que creó los archivos adjuntos correctamente.
Permite archivos adjuntos desde tu complemento
Ahora es un buen momento para agregar las direcciones adecuadas al campo Allowed Attachment URI Prefixes en la página Configuración de la app del SDK de Google Workspace Marketplace. Tu complemento solo puede crear archivos adjuntos a partir de uno de los prefijos de URI que se indican en esta página. Esta es una medida de seguridad que ayuda a reducir la posibilidad de ataques de intermediario.
El enfoque más simple es proporcionar tu dominio de nivel superior en este campo, por ejemplo, https://example.com
. https://localhost:<your port number>/
funcionaría si usas tu máquina local como servidor web.
Agrega rutas para las vistas del profesor y del estudiante
Hay cuatro iframes en los que se puede cargar un complemento de Google Classroom. Hasta ahora, solo compilaste rutas que publican el iframe de la vista de Attachment Discovery. A continuación, agrega rutas para publicar los iframes de las vistas del profesor y del estudiante también.
Se requiere el iframe de la vista del profesor para mostrar una vista previa de la experiencia del estudiante, pero, de manera opcional, se puede incluir información adicional o funciones de edición.
La vista del estudiante es la página que se presenta a cada estudiante cuando abre un archivo adjunto de complemento.
Para los fines de este ejercicio, crea una sola ruta /load-content-attachment
que muestre la vista del profesor y la vista del estudiante. Usa los métodos de la API de Classroom para determinar si el usuario es profesor o estudiante cuando se carga la página.
Python
En el ejemplo que proporcionamos, se encuentra en el archivo 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")
Ten en cuenta que también debes autenticar al usuario en este punto. También debes controlar el parámetro de consulta login_hint
aquí y dirigir al usuario a tu flujo de autorización si es necesario. Consulta los detalles de la guía de acceso que se analizaron en las explicaciones anteriores para obtener más información sobre este flujo.
Luego, envía una solicitud al extremo getAddOnContext
que coincida con el tipo de elemento.
Python
En el ejemplo que proporcionamos, esta es una continuación del 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()
Este método devuelve información sobre el rol del usuario actual en la clase.
Modificar la vista que se le presenta al usuario según su rol En el objeto de respuesta, se completa exactamente uno de los campos studentContext
o teacherContext
. Examina estos datos para determinar cómo dirigirte al usuario.
En cualquier caso, usa el valor del parámetro de consulta attachmentId
para saber qué adjunto recuperar de nuestra base de datos. Este parámetro de consulta se proporciona cuando se abren los URIs de la vista del profesor o del estudiante.
Python
En el ejemplo que proporcionamos, esta es una continuación del 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)
Prueba el complemento
Para probar la creación de archivos adjuntos, completa los siguientes pasos:
- Accede a [Google Classroom] como uno de tus usuarios de prueba de profesor.
- Navega a la pestaña Trabajo en clase y crea una nueva Tarea.
- Haz clic en el botón Complementos que se encuentra debajo del área de texto y, luego, selecciona el complemento. Se abrirá el iframe y el complemento cargará el URI de configuración de adjuntos que especificaste en la página Configuración de la app del SDK de Google Workspace Marketplace.
- Elige un contenido para adjuntarlo a la tarea.
- Cierra el iframe después de que se complete el flujo de creación de archivos adjuntos.
Deberías ver una tarjeta de archivo adjunto en la IU de creación de tareas en Google Classroom. Haz clic en la tarjeta para abrir el iframe de la vista del profesor y confirmar que aparece el adjunto correcto. Haz clic en el botón Asignar.
Para probar la experiencia del estudiante, completa los siguientes pasos:
- Luego, accede a Classroom como usuario de prueba estudiante en la misma clase que el usuario de prueba profesor.
- Busca la tarea de prueba en la pestaña Trabajo en clase.
- Expande la tarea y haz clic en la tarjeta del archivo adjunto para abrir el iframe de la vista del estudiante.
Confirma que el estudiante vea el adjunto correcto.
¡Felicitaciones! Ya puedes continuar con el siguiente paso: crear adjuntos de tipo actividad.