Este tutorial adapta pedagógicamente el Día 37. Las secciones 📝 Ejercicio original del curso provienen de los archivos del curso. Los retos 🟢🟡🟠🔴, el proyecto extra y el proyecto integrador son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.
Nota sobre las verificaciones: con Python 3.14 y
requests2.34 se consultó, el 26 de septiembre de 2026, la documentación oficial de Pixela y se probaron peticiones POST, PUT y DELETE con encabezados en los servicios de prueba httpbin.org y postman-echo.com. No se creó ninguna cuenta en Pixela.
🔐 Nota importante para el aula (menores de edad): para registrarse en Pixela hay que enviar
"notMinor": "yes". Según la documentación oficial, eso significa "no soy menor de edad, o soy menor y tengo el consentimiento de mis padres para usar el servicio" (verificado). La mayoría de estudiantes de décimo son menores de edad. Por eso:
- No respondas "yes" sin ese consentimiento: sería declarar algo falso.
- Todo el grupo puede aprender los conceptos del día con httpbin.org (sección 1), que no pide cuenta.
- Quien quiera usar Pixela, debe hacerlo con autorización de su acudiente, o el docente hace la demostración con su cuenta.
Tabla de contenidos
- Antes de empezar: el rastreador de hábitos
- Los métodos HTTP: GET, POST, PUT y DELETE
- Paso 1: crear un usuario con POST
- Paso 2: autenticación con un encabezado y crear el gráfico
- Paso 3: agregar un píxel
- La fecha automática con
strftime - Actualizar y borrar con PUT y DELETE
- 🚀 Proyecto del día: el Rastreador de Hábitos
- 🌟 Proyecto extra: el Rastreador de Hábitos con ventana
- 🏆 Proyecto integrador de los Días 35 a 37: el Reto de Hábitos Saludables del Curso
- 🧩 Comprueba lo aprendido
- 🤖 Programar con apoyo de IA
- 🛠️ Correcciones técnicas al material original
- 📋 Matriz de trazabilidad
- 📦 Evidencias de aprendizaje del Día 37
0. Antes de empezar: el rastreador de hábitos
(Fuente: 282 - Day 37 Goals- what you will make by the end of the day.md)
La profesora cuenta que admira a Simone Giertz, una inventora que presentó un calendario de hábitos: cada día que cumples tu hábito, marcas la fecha. Como las marcas forman una línea continua, dan ganas de no romper la racha.
Hoy construiremos una versión digital con la API Pixela, creada por un desarrollador japonés. Pixela no solo marca qué días cumpliste tu hábito: también muestra la intensidad con colores más oscuros o más claros (por ejemplo, cuántas páginas leíste o cuántos kilómetros recorriste en bicicleta, que es el ejemplo de la profesora).
Hasta ahora solo pedíamos datos a las APIs (GET). Hoy aprenderemos a enviar, actualizar y borrar datos, y una forma más segura de autenticarse: el encabezado (header).
Prerequisito general:
requestsy JSON (Días 33 y 34); claves y variables de entorno (Día 35); cadenas f;datetime(Día 32);input()y conversión de tipos.
1. Los métodos HTTP: GET, POST, PUT y DELETE
(Fuente: 283 - HTTP Post Requests.md, primera parte)
🎯 Objetivo de aprendizaje
Distinguir los cuatro métodos HTTP más comunes y usarlos con requests.
📖 Explicación conceptual
| Método | Qué hace | En requests |
Ejemplo del curso |
|---|---|---|---|
| GET | Pedir datos a un sistema externo | requests.get() |
La posición de la ISS |
| POST | Enviar datos nuevos; solo nos interesa saber si funcionó | requests.post() |
Guardar una fila en Google Sheets, publicar un trino |
| PUT | Actualizar un dato que ya existe | requests.put() |
Cambiar un valor de una hoja de cálculo |
| DELETE | Borrar un dato | requests.delete() |
Borrar una publicación |
El código se escribe casi igual para los cuatro: solo cambia el método.
🏫 Practicar sin cuenta: httpbin.org (adaptación)
httpbin.org es un servicio de prueba que te devuelve lo que le enviaste. Es ideal para ver qué viaja en una petición:
import requests
response = requests.post(
"https://httpbin.org/post",
json={"date": "20260926", "quantity": "9.74"},
headers={"X-USER-TOKEN": "secreto123"},
)
data = response.json()
print(data["json"]) # {'date': '20260926', 'quantity': '9.74'}
print(data["headers"]["X-User-Token"]) # secreto123(Verificado. También funcionan https://httpbin.org/put con requests.put y https://httpbin.org/delete con requests.delete. Un servicio similar es https://postman-echo.com/post.)
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: envía a httpbin un POST con tu nombre y tu hábito favorito, e imprime lo que te devuelve.
- 🟡 Reto 2: haz un PUT y un DELETE a httpbin e imprime el código de estado de cada uno.
- 🟠 Reto 3: envía el mismo diccionario con
json=y condata=. Compara, en la respuesta de httpbin, dónde aparece cada uno. ¿Qué diferencia hay? - 🔴 Reto 4: para cada una de estas acciones, di qué método HTTP usarías y por qué: consultar tu horario, inscribirte en un club, cambiar tu correo en la plataforma del colegio, cancelar tu inscripción.
2. Paso 1: crear un usuario con POST
(Fuente: 283 - HTTP Post Requests.md)
🎯 Objetivo de aprendizaje
Enviar datos con requests.post(url, json=...) siguiendo la documentación.
📝 Contenido original del curso
La página de Pixela tiene una guía de inicio en seis pasos; el curso la sigue junto con la documentación. Paso 1: crear un usuario con un POST a https://pixe.la/v1/users.
Parámetros obligatorios del cuerpo:
| Parámetro | Valor | Nota |
|---|---|---|
token |
Una cadena secreta inventada por ti | Entre 8 y 128 caracteres. Funciona como una contraseña: guárdala |
username |
Tu nombre de usuario | Único en todo Pixela |
agreeTermsOfService |
"yes" o "no" |
Aceptar las condiciones de uso |
notMinor |
"yes" o "no" |
Ver la nota para el aula al inicio |
import requests
pixela_endpoint = "https://pixe.la/v1/users"
user_params = {
"token": TOKEN,
"username": USERNAME,
"agreeTermsOfService": "yes",
"notMinor": "yes", # solo si es verdad (ver nota inicial)
}
response = requests.post(url=pixela_endpoint, json=user_params)
print(response.text)json=envía el diccionario como JSON en el cuerpo de la petición.response.textmuestra la respuesta como texto. Con un POST casi solo nos interesa saber si funcionó y, si no, por qué.- La profesora obtuvo un mensaje de éxito. Al ejecutar otra vez el mismo código, la respuesta dijo que el usuario ya existe. Por eso, después de crear el usuario, comenta esa línea.
💡 El curso copia los nombres de los parámetros de la documentación para no equivocarse:
agreeTermsOfServicetiene varias mayúsculas.
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
params=user_params en vez de json= |
Los datos viajan en la URL, no en el cuerpo, y el servidor no los encuentra | Usa json= |
| Un token de menos de 8 caracteres | Error de validación | Mínimo 8 caracteres |
| Ejecutar dos veces la creación | "This user already exists" | Comenta ese paso después de la primera vez |
| Escribir el token en el código y compartirlo | Cualquiera puede modificar tus datos | Variable de entorno (Día 35) |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: envía
user_paramsahttps://httpbin.org/post(sin crear cuenta real) y comprueba que el cuerpo llega completo. - 🟡 Reto 2: lee el
TOKENdesde una variable de entorno y valida que tenga entre 8 y 128 caracteres antes de enviarlo. - 🟠 Reto 3: genera un token aleatorio seguro con el módulo
secrets(investigasecrets.token_urlsafe). - 🔴 Reto 4: lee la lista de códigos de error de la documentación de Pixela (400, 404, 409, 503…) y escribe una función que traduzca cada código a un mensaje en español.
3. Paso 2: autenticación con un encabezado y crear el gráfico
(Fuente: 284 - Advanced Authentication using an HTTP Header.md)
🎯 Objetivo de aprendizaje
- Enviar la autenticación en un encabezado HTTP.
- Explicar por qué es más seguro que enviarla como parámetro.
📝 Contenido original del curso
Paso 2 de Pixela: crear un gráfico con un POST a /v1/users/<username>/graphs:
graph_endpoint = f"{pixela_endpoint}/{USERNAME}/graphs"
graph_config = {
"id": "graph1",
"name": "Cycling Graph",
"unit": "Km",
"type": "float",
"color": "ajisai",
}| Clave | Regla |
|---|---|
id |
Empieza con una letra (a-z), luego letras o números; de 1 a 16 caracteres. "1graph" no es válido |
name |
Nombre del gráfico |
unit |
Unidad de medida (km, páginas, minutos) |
type |
"int" (enteros, como páginas) o "float" (decimales, como kilómetros) |
color |
Nombres en japonés: por ejemplo shibafu (pasto, verde), momiji (hojas de otoño, rojo), sora (cielo, azul), ajisai (hortensia, morado). Cópialos de la documentación |
El error: al enviar requests.post(url=graph_endpoint, json=graph_config), la respuesta dice que el usuario no existe o el token es incorrecto. ¿Por qué? ¡En ningún lado enviamos el token!
📖 Los encabezados (headers)
La analogía del curso: en una carta, el encabezado tiene la información fija del remitente (logo, teléfono, dirección) y el cuerpo es el mensaje que cambia de una carta a otra. Una petición HTTP también tiene encabezados (información sobre la petición, como la autenticación) y cuerpo (los datos).
Pixela pide el token en un encabezado llamado X-USER-TOKEN:
headers = {
"X-USER-TOKEN": TOKEN
}
response = requests.post(url=graph_endpoint, json=graph_config, headers=headers)
print(response.text)headers=es un argumento con nombre: el editor no siempre lo sugiere, así que escríbelo bien (con s final).- Para ver el gráfico:
https://pixe.la/v1/users/<tu_usuario>/graphs/<id_del_gráfico>.html.
📖 ¿Por qué un encabezado es más seguro?
En el Día 36, la clave de News API iba como parámetro, es decir, dentro de la URL:
https://newsapi.org/v2/everything?apiKey=TU_CLAVE&qInTitle=TeslaLas URL quedan guardadas en muchos lugares: el historial del navegador, los registros (logs) de los servidores, extensiones que ven las peticiones. Con HTTPS la petición viaja cifrada, pero aun así la clave puede filtrarse por esos otros caminos. Por eso la documentación de News API recomienda usar un encabezado (X-Api-Key) en lugar del parámetro. Al verificar el Día 36 sin clave, su mensaje de error lo decía: "Append this to the URL with the apiKey param, or use the x-api-key HTTP header".
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
No enviar headers |
"User does not exist or the token is wrong" | headers={"X-USER-TOKEN": TOKEN} |
header= (sin s) |
TypeError: ... unexpected keyword argument 'header' |
headers= |
Color mal escrito ("sakura") |
Error de validación | Copia el nombre de la documentación |
id que empieza con número |
Error de validación | Empieza con una letra |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: envía a httpbin un POST con y sin
headersy compara qué aparece endata["headers"]. - 🟡 Reto 2: en httpbin, envía una clave como parámetro con
params=y como encabezado conheaders=. Miradata["url"]: ¿en cuál de los dos casos aparece la clave? - 🟠 Reto 3: diseña (en papel) el
graph_configde un hábito tuyo: nombre, unidad, tipo y color. Justifica si esintofloat. - 🔴 Reto 4: investiga el encabezado
User-Agent: envíalo a httpbin sin cambiar nada y luego con un valor propio. ¿Para qué crees que lo usan los servidores?
4. Paso 3: agregar un píxel
(Fuente: 285 - Challenge- Add a Pixel to the Habit Tracker using a Post Request.md)
📝 Ejercicio original del curso
Enunciado: leer en la documentación cómo publicar un píxel (descripción, encabezado y cuerpo) y hacerlo.
Pistas del curso:
- El endpoint es otro; todo lo que va entre
<y>se reemplaza por tus valores reales. - Es un POST con autenticación en el encabezado.
date: formatoyyyyMMdd(cuatro dígitos del año, dos del mes y dos del día), como texto.quantity: la cantidad del día, también como texto.
🔍 Antes de programar
- ¿Cómo queda el endpoint con tu usuario y tu
idde gráfico? - Si tu gráfico es de tipo
int, ¿puedes enviar"9.74"?
🔎 La solución del curso
GRAPH_ID = "graph1"
pixel_creation_endpoint = f"{pixela_endpoint}/{USERNAME}/graphs/{GRAPH_ID}"
pixel_data = {
"date": "20200724",
"quantity": "9.74",
}
response = requests.post(url=pixel_creation_endpoint, json=pixel_data, headers=headers)
print(response.text)Al recargar la página del gráfico aparece el píxel con su color, y Pixela calcula estadísticas: total, máximo, mínimo, promedio y número de píxeles.
📖 El rechazo del 25 % (análisis de esta adaptación)
La documentación de Pixela (verificada) indica que, para usuarios que no son supporters (donantes), algunas peticiones se rechazan a propósito el 25 % de las veces. En ese caso la respuesta es 503 y el cuerpo contiene "isRejected": true. La documentación dice que basta con reintentar.
Esto explica por qué, a veces, el mismo código funciona en una ejecución y falla en la siguiente. Un programa robusto lo reintenta automáticamente:
def post_con_reintentos(url, datos, headers, intentos=5):
for intento in range(intentos):
response = requests.post(url=url, json=datos, headers=headers)
if response.status_code != 503:
return response
print(f"Rechazado, reintentando ({intento + 1})...")
return response⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
"date": 20200724 (número) |
Error: debe ser texto | "20200724" |
"date": "2020-07-24" |
Formato inválido | yyyyMMdd, sin guiones |
"quantity": 9.74 (número) |
Error: debe ser texto | "9.74" |
Decimales en un gráfico int |
Error de validación | Envía enteros, o crea el gráfico como float |
Dejar <username> literal en la URL |
404 | Reemplaza los marcadores |
💡 La documentación define reglas de validación para
quantity: en gráficosfloat, un número con punto decimal (por ejemplo"10.0"). Si envías"10"a un gráficofloaty falla, esa es la razón probable. Compruébalo y anota el resultado.
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: con httpbin, envía el
pixel_datay verifica que ambos valores llegan como texto. - 🟡 Reto 2: agrega a tu programa la función
post_con_reintentosy haz que imprima cuántos intentos necesitó. - 🟠 Reto 3: convierte la cantidad que escribe el usuario (
input) en un texto válido: acepta"10,5"(con coma, como se escribe en Colombia) y conviértelo a"10.5". - 🔴 Reto 4: Pixela permite un campo opcional
optionalData(un texto JSON). Úsalo para guardar una nota del día, por ejemplo el nombre del libro que leíste.
5. La fecha automática con strftime
(Fuente: 286 - Autofilling today's date using strftime.md)
🎯 Objetivo de aprendizaje
Convertir un datetime en el texto exacto que pide una API.
📖 Explicación conceptual
Cada API pide la fecha en un formato distinto (el curso comenta que en Asia se suele escribir primero el año y en otros países al final). El método strftime ("string format time") convierte un datetime en texto usando códigos:
| Código | Significado | Ejemplo (26/09/2026) |
|---|---|---|
%Y |
Año con 4 dígitos | 2026 |
%m |
Mes con 2 dígitos | 09 |
%d |
Día con 2 dígitos | 26 |
%a |
Día de la semana, abreviado | Sat |
%A |
Día de la semana, completo | Saturday |
%b |
Mes, abreviado | Sep |
%B |
Mes, completo | September |
(Resultados verificados.)
from datetime import datetime
today = datetime.now()
print(today.strftime("%Y%m%d")) # 20260926
print(today.strftime("%Y-%m-%d")) # 2026-09-26: puedes agregar guiones o espaciosPara otro día: crea la fecha tú mismo, por ejemplo ayer: datetime(year=2026, month=9, day=25), y dale formato igual.
🛠️ Nota técnica: el video dice que
%bda "el nombre completo del mes". En realidad da el nombre abreviado (Sep); el completo es%B(September), verificado. Además, los nombres salen en inglés por defecto.
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: imprime la fecha de hoy como
26/09/2026. - 🟡 Reto 2: imprime "Hoy es sábado 26 de septiembre de 2026" en español (usa listas con los nombres en español y los atributos del
datetime). - 🟠 Reto 3: calcula la fecha de ayer automáticamente con
timedelta(days=1)y dale el formatoyyyyMMdd. - 🔴 Reto 4: el proceso inverso existe:
datetime.strptime("20260926", "%Y%m%d"). Úsalo para validar que una fecha escrita por el usuario es real (por ejemplo, rechazar"20260231").
6. Actualizar y borrar con PUT y DELETE
(Fuente: 287 - How to use HTTP Put and Delete Requests.md)
📝 Ejercicio original del curso
Desafío 1 (PUT): la profesora "mintió": ayer no recorrió 15 km sino 4,5. Actualiza el píxel.
Desafío 2 (DELETE): borra un píxel.
🔍 Antes de programar
- ¿Qué diferencia hay entre el endpoint para crear un píxel y el endpoint para actualizar un píxel concreto?
- ¿DELETE necesita cuerpo? ¿Necesita autenticación?
🔎 La solución del curso
update_endpoint = f"{pixela_endpoint}/{USERNAME}/graphs/{GRAPH_ID}/{today.strftime('%Y%m%d')}"
new_pixel_data = {
"quantity": "4.5"
}
response = requests.put(url=update_endpoint, json=new_pixel_data, headers=headers)
print(response.text)
delete_endpoint = f"{pixela_endpoint}/{USERNAME}/graphs/{GRAPH_ID}/{today.strftime('%Y%m%d')}"
response = requests.delete(url=delete_endpoint, headers=headers)
print(response.text)- El endpoint termina en la fecha del píxel que quieres cambiar o borrar.
- Comillas: dentro de la cadena f (con comillas dobles) el formato de
strftimeva entre comillas simples. - DELETE no lleva datos en el cuerpo, pero sí el encabezado de autenticación.
Versión final del curso: se comentan las demás peticiones, se deja la de crear el píxel con la fecha de hoy y la cantidad se pide al usuario:
pixel_data = {
"date": today.strftime("%Y%m%d"),
"quantity": input("How many kilometers did you cycle today? "),
}⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
| Usar el endpoint de creación con PUT | Error: falta la fecha en la URL | Agrega /{fecha} al final |
Olvidar headers en DELETE |
Error de autenticación | DELETE también lleva el token |
| Comillas dobles dentro de la cadena f (antes de Python 3.12) | SyntaxError |
Comillas simples adentro |
| Borrar sin confirmar | Pierdes el dato sin querer | Pide confirmación antes |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: con httpbin, haz un PUT y un DELETE con encabezado y muestra lo que devuelve cada uno.
- 🟡 Reto 2: antes de borrar, pregunta: "¿Seguro que quieres borrar el registro del 25/09/2026? (s/n)".
- 🟠 Reto 3: un menú de consola con las opciones: 1. Registrar hoy, 2. Corregir un día, 3. Borrar un día, 4. Salir.
- 🔴 Reto 4: crea una clase
ClienteHabitoscon los métodosregistrar(fecha, cantidad),corregir(fecha, cantidad)yborrar(fecha), que guarde internamente el endpoint y los encabezados. ¿Qué código repetido desaparece?
7. 🚀 Proyecto del día: el Rastreador de Hábitos
(Fuentes: 282 a 287)
Problema
Registrar cada día un hábito que quieras desarrollar y verlo como un gráfico que motive a no romper la racha.
Contexto
Elige un hábito tuyo: minutos de lectura del Plan Lector, minutos de práctica de un instrumento, vasos de agua, kilómetros en bicicleta, minutos programando…
Requisitos funcionales
- Registrar la cantidad de hoy (fecha automática con
strftime). - Corregir la cantidad de un día anterior (PUT).
- Borrar el registro de un día (DELETE), con confirmación.
- Mostrar al final el enlace del gráfico.
Restricciones técnicas
- Modo aula: todas las peticiones a httpbin.org, que simula el servidor. Modo real: Pixela, solo con cuenta autorizada (ver nota inicial). Una constante
MODO_REAL = Falsedecide a dónde van las peticiones. - Token en variable de entorno.
- Reintentos ante el 503 de Pixela.
Planificación
| Entradas | Proceso | Salidas |
|---|---|---|
| Opción del menú, cantidad, fecha | Construir endpoint, cuerpo y encabezado; enviar con el método correcto | Mensaje de éxito o de error; gráfico actualizado |
Pruebas
| Caso | Esperado |
|---|---|
Registrar hoy con "30" en un gráfico int |
Éxito |
Registrar "treinta" |
El programa rechaza el valor antes de enviar |
Registrar con "10,5" |
Se convierte a "10.5" (si el gráfico es float) |
Corregir una fecha inexistente (20260231) |
El programa la rechaza antes de enviar |
| Petición rechazada por Pixela (503) | ⚠️ (ver 🧪) |
| Token sin configurar | ⚠️ (ver 🧪) |
🧪 Prueba y depuración
- El 503 aleatorio. Sin reintentos, el programa falla una de cada cuatro veces en promedio. Implementa los reintentos con un límite (sección 4) y registra cuántos intentos fueron necesarios en 10 ejecuciones.
- El token ausente. Sin la variable de entorno, el encabezado se envía con
Noney el error de la API no lo explica. Detéctalo al iniciar el programa. - Registrar dos veces el mismo día. El curso comenta que ya no pudo publicar otro dato para hoy. Consulta la documentación de Pixela: ¿qué pasa al publicar un píxel en una fecha que ya tiene uno? Ofrece al usuario "corregir" en lugar de "registrar" si ya existe un dato de hoy.
- La fecha de hoy según la hora. Si el programa se ejecuta en un servidor en UTC (Día 32) a las 9:00 p. m. de Colombia, ¿qué fecha usa
datetime.now()? ¿Cómo lo resolverías?
⭐ Extensión del proyecto
- Guardar localmente, en un CSV, cada acción realizada (fecha, método, cantidad, resultado).
- Calcular la racha actual de días seguidos con registro.
- Programar un recordatorio por correo a las 8:00 p. m. si hoy no hay registro.
8. 🌟 Proyecto extra: el Rastreador de Hábitos con ventana
Situación
Tu familia quiere usar tu rastreador, pero no saben usar la consola. Construye una interfaz gráfica con Tkinter.
Requisitos
- Ventana con: una entrada para la cantidad, una entrada para la fecha (con la fecha de hoy ya escrita en formato
dd/mm/aaaa) y tres botones: Registrar, Corregir y Borrar. - Una clase
ClienteHabitos(Reto 4 de la sección 6) que haga las peticiones; la interfaz no usarequestsdirectamente. - Validación: fecha real (
strptime), cantidad numérica (acepta coma o punto). - Mensajes con
messagebox: éxito, error de validación, error del servidor. - Borrar pide confirmación con
messagebox.askyesno. - Un botón Ver gráfico que abra el enlace en el navegador (investiga el módulo
webbrowser). - Modo aula (httpbin) y modo real (Pixela) con una constante.
- Los botones se desactivan mientras se hace una petición (recuerda el doble clic del Día 34).
Decisiones que debes tomar
- Si el servidor tarda, la ventana se congela. ¿Es aceptable para este uso? (Investiga, sin implementarlo, qué son los hilos o threads.)
- ¿Guardas el usuario y el
iddel gráfico en el código, en un JSON o los pides en la ventana?
Casos de prueba
| Caso | Esperado |
|---|---|
Fecha 31/02/2026 |
Mensaje "Fecha no válida"; no se envía nada |
Cantidad abc |
Mensaje "Cantidad no válida" |
| Borrar y responder "No" | No se envía nada |
| Modo aula | httpbin devuelve los datos; la ventana muestra "Éxito (simulado)" |
9. 🏆 Proyecto integrador de los Días 35 a 37: el Reto de Hábitos Saludables del Curso
Proyecto integrador creado para esta adaptación. Reúne autenticación, variables de entorno y alertas (Día 35), combinación de varias APIs y caché (Día 36), y los métodos POST, PUT y DELETE con encabezados (Día 37).
Situación problema
El director de grupo propone un reto de 30 días de hábitos saludables (agua, actividad física, lectura, sueño). Cada participante registra su avance y el grupo recibe un resumen semanal. Para no depender de cuentas personales, el grupo construirá su propio servidor simulado.
Pregunta orientadora
¿Cómo diseñar un programa que hable con un servicio web usando los cuatro métodos HTTP, que funcione igual con un servicio real o simulado, y que proteja los datos de los participantes?
Objetivo
Construir el Reto de Hábitos Saludables: un cliente de consola (o Tkinter) que registra, corrige, borra y consulta hábitos, con dos "servidores" intercambiables.
Requerimientos
- Una clase abstracta o base
ServidorHabitoscon los métodoscrear(usuario, fecha, cantidad),consultar(usuario, fecha_inicio, fecha_fin),actualizar(...)yborrar(...). - Dos implementaciones:
ServidorLocal: guarda todo en unhabitos.json(sin internet). Simula el 503 de Pixela con una probabilidad configurable, para practicar los reintentos.ServidorEco: envía cada operación a httpbin.org con el método HTTP correcto (GET, POST, PUT, DELETE) y un encabezado de autenticación, y muestra qué se envió.
participantes.csvcon nombres ficticios y el hábito de cada uno (unidad y tipoint/float).- Validaciones: fecha real, dentro de los 30 días del reto; cantidad del tipo correcto; no registrar dos veces el mismo día (ofrecer corregir).
- Reintentos con límite cuando el servidor responde "rechazado".
- Resumen semanal por correo (servidor local del Día 32): participantes con racha completa, promedio por hábito y quién no ha registrado en 3 días.
- Un "token" del grupo en variable de entorno; el programa no arranca si falta.
- Un archivo
config.jsonque decide qué servidor usar.
Restricciones
- Datos ficticios; nada de cuentas personales de estudiantes en servicios externos.
- El código que usa el servidor no sabe cuál de los dos está usando.
Etapas de desarrollo
| Etapa | Entregable parcial |
|---|---|
| 1. Diseño | Diagrama de clases y tabla "operación → método HTTP" |
2. ServidorLocal |
Las cuatro operaciones probadas con habitos.json |
| 3. Reintentos | Simulación del rechazo con 30 % de probabilidad |
4. ServidorEco |
Las cuatro operaciones enviadas a httpbin con encabezado |
| 5. Resumen | Correo semanal al servidor local |
| 6. Presentación | Demostración cambiando de servidor solo con config.json |
Entregables
- Código en varios archivos,
config.json,participantes.csv. - Diagrama de clases.
- 12 casos de prueba con resultados.
- Registro de errores con al menos 5 errores reales.
- Un párrafo sobre por qué no se usaron cuentas personales de menores de edad en servicios externos.
Casos de prueba mínimos
| Caso | Esperado |
|---|---|
| Registrar dos veces el mismo día | Se ofrece corregir |
| Rechazo simulado 3 veces seguidas y luego éxito | Éxito tras 4 intentos |
| Rechazo simulado más veces que el límite | Mensaje claro de fallo |
Cambiar config.json de local a eco |
El resto del programa funciona sin cambios |
| Falta la variable de entorno del token | El programa no arranca y dice por qué |
Criterios técnicos de funcionamiento
- Ninguna función fuera de las clases de servidor usa
requestsojsondirectamente. - Las validaciones están en funciones que se pueden probar sin servidor.
- El resumen semanal se calcula correctamente con datos simulados de 30 días.
Posibles extensiones
Interfaz Tkinter para registrar; gráfico de barras del avance (lo aprenderás en días posteriores del curso); un tercer servidor que use Pixela real, solo para quien tenga autorización.
10. 🧩 Comprueba lo aprendido
Conceptuales
- ¿Qué método HTTP usarías para: pedir el clima, publicar una foto, cambiar tu foto de perfil, borrar un comentario?
- ¿Qué diferencia hay entre enviar datos con
params=y conjson=? - ¿Qué es un encabezado HTTP? Explícalo con la analogía de la carta.
- ¿Por qué enviar una clave en un encabezado es más seguro que enviarla en la URL, aunque la conexión sea HTTPS?
- ¿Qué significa
"notMinor": "yes"según la documentación de Pixela?
Análisis de código
response = requests.post(url=graph_endpoint, json=graph_config, headers=headers)Identifica qué parte de la petición es la URL, cuál el cuerpo y cuál el encabezado. ¿Cuál de ellas contiene el token?
Predicción
- ¿Qué imprime
datetime(2026, 1, 5).strftime("%Y%m%d")? ¿Y con"%d/%m/%Y"? - ¿Qué imprime
datetime(2026, 9, 26).strftime("%b %B")? - Si una petición se rechaza con probabilidad 25 %, ¿cuál es la probabilidad de que fallen dos intentos seguidos? ¿Y tres?
Depuración
pixel_data = {
"date": datetime.now().strftime("%Y-%m-%d"),
"quantity": 12,
}
response = requests.post(url=f"{pixela_endpoint}/<username>/graphs/{GRAPH_ID}", json=pixel_data)Encuentra cuatro errores.
Modificación
Cambia la versión final del curso para que, si el usuario escribe 0, se borre el píxel de hoy en lugar de registrar cero.
Creación
Escribe a_formato_pixela(texto_fecha: str) -> str que reciba una fecha como "26/09/2026" y devuelva "20260926", o lance ValueError si la fecha no es real. Pruébala con 4 casos.
11. 🤖 Programar con apoyo de IA
Actividad 1: leer los términos antes de aceptar. El curso responde "yes" a agreeTermsOfService y notMinor en segundos. Pide a una IA que te resuma los términos de servicio de Pixela y luego:
- Abre los términos originales y verifica dos afirmaciones del resumen.
- Busca qué dicen sobre los menores de edad y compáralo con la documentación del parámetro
notMinor. - Reflexiona: ¿cuántas veces has aceptado términos sin leerlos? ¿Qué podría salir mal?
Actividad 2: depurar con la IA sin compartir el token. Si una petición falla, copia la respuesta del servidor (no tu token) y tu código con el token reemplazado por "TOKEN". Pide a la IA una explicación. Comprueba la explicación con la documentación de códigos de error de Pixela.
Actividad 3: ¿conoce la IA el rechazo del 25 %? Pregunta: "¿Por qué mi petición a Pixela a veces devuelve 503 y a veces funciona?". Compara su respuesta con la documentación (sección 4). Si inventa otra causa, anota cómo lo descubriste.
12. 🛠️ Correcciones técnicas al material original
| Archivo | Afirmación o comportamiento del curso | Observación | Tratamiento |
|---|---|---|---|
283 |
Responder "notMinor": "yes" sin más explicación |
Significa "no soy menor, o soy menor con consentimiento de mis padres" (verificado en la documentación); relevante para estudiantes de décimo | Nota inicial; alternativa con httpbin |
284 |
Color "sara es cielo" | El nombre en la API es sora (cielo); copiarlo de la documentación |
Sección 3 |
285 |
Las peticiones funcionan siempre que el código sea correcto | Pixela rechaza el 25 % de algunas peticiones de usuarios no supporters con 503 e "isRejected": true (verificado en la documentación) |
Sección 4; 🧪 del proyecto |
286 |
%b da el nombre completo del mes |
%b es abreviado (Sep); el completo es %B (verificado) |
Nota técnica de la sección 5 |
287 |
input() para la cantidad, enviado sin validar |
Textos no numéricos, comas decimales o enteros en gráficos float pueden ser rechazados |
Pruebas y Reto 3 de la sección 4 |
13. 📋 Matriz de trazabilidad
| Contenido original | Archivo de origen | Concepto desarrollado | Ejercicio original | Reto adicional | Proyecto relacionado |
|---|---|---|---|---|---|
| Objetivos, calendario de hábitos, Pixela, intensidad | 282 |
Sección 0 | — | — | Sección 7 |
GET, POST, PUT, DELETE; crear usuario; json=; response.text |
283 |
Secciones 1 y 2 | Paso 1: crear usuario | Retos 1–4 de las secciones 1 y 2; httpbin | Secciones 7, 8 y 9 |
Crear gráfico, reglas de id, colores, encabezado X-USER-TOKEN, seguridad de encabezados |
284 |
Sección 3 | Paso 2: crear gráfico | Retos 1–4 de la sección 3 | Secciones 7, 8 y 9 |
Publicar un píxel, date y quantity como texto |
285 |
Sección 4 | Desafío: agregar un píxel | Retos 1–4 de la sección 4; reintentos | Secciones 7, 8 y 9 |
strftime, códigos de formato, fechas pasadas |
286 |
Sección 5 | Fecha automática | Retos 1–4 de la sección 5 | Secciones 7 y 8 |
PUT y DELETE, endpoint con fecha, input() |
287 |
Sección 6 | Desafíos PUT y DELETE | Retos 1–4 de la sección 6 | Secciones 7, 8 y 9 |
| (No existe en el curso original) | — | — | — | Rastreador de Hábitos con ventana | Sección 8 |
| (No existe en el curso original) | — | — | — | Reto de Hábitos Saludables (Días 35–37) | Sección 9 |
14. 📦 Evidencias de aprendizaje del Día 37
- Las respuestas de httpbin a un GET, POST, PUT y DELETE, con tu explicación de dónde aparecen el cuerpo y el encabezado.
- El experimento del Reto 2 de la sección 3: la clave en la URL frente a la clave en el encabezado.
- Tu
graph_configjustificado (nombre, unidad, tipo, color). - El Rastreador de Hábitos funcionando en modo aula (y en modo real, si tienes autorización), con reintentos.
- La tabla de códigos de
strftimecon ejemplos que tú ejecutaste. - El Rastreador con ventana (proyecto extra) o el Reto de Hábitos Saludables (proyecto integrador).
- Cuaderno de errores:
paramsen lugar dejson, usuario ya existente, falta de encabezado,headersin s, color mal escrito, fecha con guiones, cantidad como número, marcadores<username>sin reemplazar, 503 aleatorio,%bfrente a%By comillas dentro de la cadena f.