Saltar al contenido
Inicio 100 Días de Código en Python — Día 33: APIs, endpoints y parámetros — el Notificador de la ISS

100 Días de Código en Python — Día 33: APIs, endpoints y parámetros — el Notificador de la ISS

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 33 (nivel intermedio+).

Este tutorial adapta pedagógicamente el Día 33. 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: las API se consultaron en vivo el 26 de septiembre de 2026 con Python 3.14 y requests 2.34. Las API públicas cambian o desaparecen con el tiempo: si alguna no responde igual, ese también es un aprendizaje (sección 3). El archivo inicial del desafío de Kanye y el del proyecto de la ISS no están en la carpeta.


Tabla de contenidos

  1. Antes de empezar
  2. ¿Qué es una API?
  3. Endpoints y la primera petición con requests
  4. Respuestas: códigos HTTP, excepciones y JSON
  5. Desafío: la máquina de citas de Kanye
  6. Parámetros de una API: la hora del amanecer y del atardecer
  7. 🚀 Proyecto del día: el Notificador de la ISS
  8. 🌟 Proyecto extra: el Reloj Solar de Colombia
  9. 🏆 Proyecto integrador de los Días 32 y 33: la Alerta del Club de Astronomía
  10. 🧩 Comprueba lo aprendido
  11. 🤖 Programar con apoyo de IA
  12. 🛠️ Correcciones técnicas al material original
  13. 📋 Matriz de trazabilidad
  14. 📦 Evidencias de aprendizaje del Día 33

0. Antes de empezar

(Fuente: 252 - Day 33 Goals- what you will make by the end of the day.md)

Hoy aprenderemos a usar APIs (Application Programming Interfaces, interfaces de programación de aplicaciones) para traer datos en vivo de internet a nuestros programas.

El proyecto: la Estación Espacial Internacional (ISS) da muchas vueltas a la Tierra cada día. Nuestro programa averiguará dónde está en este momento y, si pasa sobre nosotros y es de noche (para poder verla), nos enviará un correo que diga: "¡Mira hacia arriba!".

Prerequisito general: diccionarios anidados y JSON (Días 9 y 30); excepciones y raise (Día 30); split de cadenas; datetime y smtplib (Día 32); Tkinter y itemconfig (Días 27 a 31); funciones que devuelven valores (Día 10).

💡 Para el docente: el envío de correos puede probarse con el servidor local del Día 32 (sección 1 de ese tutorial), sin cuentas reales.


1. ¿Qué es una API?

(Fuente: 253 - What are Application Programming Interfaces (APIs)-.md)

🎯 Objetivo de aprendizaje

Explicar qué es una API y por qué los programas la necesitan para obtener datos de otros sistemas.

📖 Explicación conceptual

Muchos sitios web tienen datos valiosos: el pronóstico del clima, el precio de las criptomonedas, las estadísticas de los jugadores de baloncesto. ¿Cómo los usamos en nuestros programas?

Una API es un conjunto de comandos, funciones, protocolos y objetos que los programadores pueden usar para crear software o interactuar con un sistema externo. Funciona como una barrera con reglas: si tu petición cumple las reglas de la API, el sistema te responde con los datos; si no, la rechaza.

La analogía del restaurante (del curso):

Restaurante API
La cocina, donde están los ingredientes Los datos del sistema externo
El menú: lo que puedes pedir La API: lo que puedes solicitar
No puedes entrar a la cocina y tomar lo que quieras No puedes acceder directamente a la base de datos del sitio

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: menciona tres aplicaciones de tu celular que probablemente usan una API (por ejemplo, una aplicación del clima). ¿Qué datos pedirían?
  • 🟡 Reto 2: explica la analogía del restaurante con otra situación de tu colegio (por ejemplo, la secretaría académica y los certificados de notas).
  • 🟠 Reto 3: ¿por qué una empresa querría ofrecer una API pública y gratuita? ¿Y por qué otra no querría?
  • 🔴 Reto 4: investiga si alguna entidad colombiana ofrece datos abiertos por API (por ejemplo, el portal de datos abiertos del Gobierno). Anota qué datos ofrece y en qué formato.

2. Endpoints y la primera petición con requests

(Fuente: 254 - API Endpoints and Making API Calls.md)

🎯 Objetivo de aprendizaje

  • Identificar el endpoint de una API.
  • Hacer una petición GET con la biblioteca requests.

📖 Explicación conceptual

Endpoint (punto final): es la dirección donde están los datos, normalmente una URL. Si quieres sacar dinero de un banco, necesitas saber dónde queda.

Petición (request): es como ir al banco y hablar con el cajero, que actúa como la API: no te deja entrar a la bóveda, pero atiende tus solicitudes. Algunas requieren identificarte (retirar dinero); otras no (preguntar el horario). Hoy usaremos APIs del segundo tipo: no piden identificación.

La API de la ubicación de la ISS:

Texto
http://api.open-notify.org/iss-now.json

Si pegas esa dirección en el navegador, verás algo así (respuesta real del 26/09/2026):

JSON
{"message": "success", "iss_position": {"latitude": "29.1542", "longitude": "40.0602"}, "timestamp": 1790444272}

Si recargas la página, los números cambian: la ISS se mueve muy rápido.

💡 El curso recomienda una extensión del navegador para ver el JSON en forma de árbol (JSON Viewer Awesome). Algunos navegadores, como Firefox, ya lo muestran así sin instalar nada.

¿Por qué JSON? La analogía del curso: un mueble de IKEA viene en un paquete plano para transportarlo fácil, y en casa lo armas. JSON es ese paquete plano: texto compacto que viaja rápido por internet y que en Python "armamos" de nuevo como diccionario.

🔎 Ejemplo explicado

Python
import requests

response = requests.get(url="http://api.open-notify.org/iss-now.json")
print(response)          # <Response [200]>
  • requests no viene con Python: hay que instalarlo (en PyCharm, con el bombillo rojo; o con pip install requests).
  • requests.get hace una petición de tipo GET (obtener datos) al endpoint.
  • Al imprimir la respuesta no vemos el JSON, sino <Response [200]>: un código de estado. Lo estudiamos en la siguiente sección.

⚠️ Errores frecuentes

Error Qué ocurre Solución
import request (sin la s) ModuleNotFoundError La biblioteca se llama requests
ModuleNotFoundError: No module named 'requests' No está instalada en el intérprete del proyecto Instálala
Sin conexión a internet ConnectionError Revisa la conexión; maneja la excepción (sección 3)
Copiar mal el endpoint Código 404 Copia la URL exacta de la documentación

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: abre el endpoint en el navegador 3 veces con un minuto de diferencia y anota la latitud y longitud. ¿Hacia dónde se mueve la ISS?
  • 🟡 Reto 2: haz la misma petición desde Python e imprime response.text. ¿Qué tipo de dato es?
  • 🟠 Reto 3: mide cuánto tarda la respuesta (investiga response.elapsed).
  • 🔴 Reto 4: el endpoint http://api.open-notify.org/astros.json dice quiénes están en el espacio. Consúltalo y compara la lista con una fuente de noticias reciente. ¿Coinciden? (Ver sección 3, "Datos que no son confiables".)

3. Respuestas: códigos HTTP, excepciones y JSON

(Fuente: 255 - Working with Responses- HTTP Codes, Exceptions & JSON Data.md)

🎯 Objetivo de aprendizaje

  • Interpretar los códigos de estado HTTP.
  • Lanzar una excepción cuando la petición falla.
  • Extraer datos del JSON de la respuesta.

📖 Los códigos de estado

El primer dígito resume lo que pasó:

Código Significado Ejemplo
1xx Espera, algo está en proceso (informativo) —
2xx ¡Éxito! Aquí están tus datos 200 OK
3xx Redirección: el recurso está en otra parte 301 Moved Permanently
4xx Tú cometiste un error 400 Bad Request, 401 Unauthorized, 404 Not Found
5xx El servidor cometió un error 500 Internal Server Error

🛠️ Nota técnica: en el video, los 3xx se describen como "no tienes permiso, vete". En realidad significan redirección: el recurso se movió a otra dirección. La falta de permiso se indica con códigos 4xx, como 401 (no autenticado) o 403 (prohibido). Consulta la referencia de códigos de estado de MDN (se abre en otra pestaña).

📖 ¿Qué hacer si la petición falla?

El curso muestra el razonamiento paso a paso:

  1. Tentación: if response.status_code != 200: print("error"). Imprimir no sirve: el programa sigue como si nada.
  2. Mejor: lanzar una excepción, raise Exception("Bad response from ISS API"). Pero es demasiado general.
  3. Más específico: un if/elif para 404, 401, … Pero hay decenas de códigos: imposible cubrirlos todos.
  4. Solución: que requests lance la excepción adecuada:
Python
response = requests.get(url="http://api.open-notify.org/iss-now.json")
response.raise_for_status()

Si el código no es de éxito, raise_for_status() lanza una HTTPError con un mensaje claro. Con un endpoint mal escrito (verificado):

Texto
HTTPError: 404 Client Error: Not Found for url: http://api.open-notify.org/iss-now.jsonx

📖 Extraer los datos

Python
data = response.json()
longitude = data["iss_position"]["longitude"]
latitude = data["iss_position"]["latitude"]
iss_position = (longitude, latitude)
print(iss_position)
  • response.json() convierte el JSON en un diccionario de Python.
  • iss_position es un diccionario anidado: se accede con dos pares de corchetes.
  • ⚠️ La latitud y la longitud llegan como texto ("29.1542"), no como números. Para compararlas hay que usar float(...) (sección 6).

Para ver en un mapa dónde está la ISS, el curso usa latlong.net (se abre en otra pestaña) (Lat Long to Address).

📖 Datos que no son confiables (análisis de esta adaptación)

raise_for_status() solo detecta los errores que el servidor reporta con su código. Dos casos reales, verificados el 26/09/2026:

  • http://api.open-notify.org/astros.json responde 200, pero la lista de astronautas corresponde a una tripulación de 2024: los datos están desactualizados.
  • https://restcountries.com/v3.1/..., una API de países muy usada en tutoriales, responde 200 pero su contenido dice "success": false y que esa versión fue retirada.

Conclusión: un código 200 significa "el servidor respondió", no "los datos son correctos". Revisa también el contenido y compáralo con otras fuentes.

⚠️ Errores frecuentes

Error Qué ocurre Solución
response.json sin paréntesis Obtienes el método, no los datos response.json()
data["longitude"] KeyError: la longitud está dentro de iss_position data["iss_position"]["longitude"]
Comparar "29.15" > 5 TypeError entre str e int Convierte con float()
Olvidar raise_for_status() El programa sigue con datos vacíos o de error Llámalo justo después de la petición

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: provoca a propósito un 404 y captúralo con try/except requests.exceptions.HTTPError, mostrando un mensaje amable.
  • 🟡 Reto 2: desconecta el internet y ejecuta la petición. ¿Qué excepción aparece? Captúrala también.
  • 🟠 Reto 3: convierte el timestamp de la respuesta en una fecha legible (investiga datetime.fromtimestamp).
  • 🔴 Reto 4: escribe obtener_posicion_iss() que devuelva una tupla de floats (lat, lng), o None si hubo cualquier error (HTTP, conexión o tiempo de espera con timeout=10).

4. Desafío: la máquina de citas de Kanye

(Fuente: 256 - Challenge - Build a Kanye Quotes App using the Kanye Rest API.md)

📝 Ejercicio original del curso

Enunciado: el curso entrega una aplicación Tkinter ya construida (con background.png y kanye.png) que muestra una cita dentro de un lienzo. Al pulsar el botón con la cara de Kanye West se llama get_quote(). Tu trabajo: completar esa función para que traiga una cita aleatoria de la API https://api.kanye.rest y la muestre en quote_text.

La API devuelve un JSON con una sola clave (verificado):

JSON
{"quote": "People always tell you 'Be humble. Be humble.' When was the last time someone told you to be amazing? ..."}

🔍 Antes de programar

  • ¿Qué clave del JSON contiene la cita?
  • ¿Cómo se cambiaba el texto de un elemento del lienzo? (Día 31)

🔎 La solución del curso

Python
def get_quote():
    response = requests.get("https://api.kanye.rest")
    response.raise_for_status()
    data = response.json()
    quote = data["quote"]
    canvas.itemconfig(quote_text, text=quote)

🏫 Nota para el aula (adaptación): las citas llegan de una API externa y no podemos controlar su contenido. Si el docente prefiere otra fuente, la API https://api.adviceslip.com/advice (consejos cortos en inglés, sin clave) funciona igual; su JSON es anidado: data["slip"]["advice"] (verificado).

Si no tienes el archivo inicial: construye una ventana con un Canvas que tenga un texto (quote_text) y un botón que llame a get_quote. Es la estructura de Flashy (Día 31), más sencilla.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: muestra la primera cita al abrir la aplicación, sin esperar al botón.
  • 🟡 Reto 2: si la API falla (sin internet), muestra en el lienzo "No se pudo obtener la cita" en lugar de cerrar el programa.
  • 🟠 Reto 3: agrega un botón Guardar que añada la cita actual a un archivo favoritas.txt, sin repetirlas.
  • 🔴 Reto 4: las citas largas se salen del lienzo. Investiga el parámetro width de create_text y reduce el tamaño de la letra si la cita supera cierto número de caracteres.

5. Parámetros de una API: la hora del amanecer y del atardecer

(Fuente: 257 - Understand API Parameters- Match Sunset Times with the Current Time.md)

🎯 Objetivo de aprendizaje

  • Enviar parámetros en una petición.
  • Leer la documentación de una API para saber qué parámetros acepta.
  • Extraer la hora de una fecha en formato ISO con split.

📖 Explicación conceptual

Los parámetros son entradas que cambian la respuesta, igual que los argumentos de una función. En el banco: en vez de "¿cuál es su horario?", preguntas "¿a qué hora cierran el lunes?".

  • Algunas APIs no tienen parámetros (la de la ISS).
  • Otras tienen parámetros obligatorios y opcionales. Los opcionales tienen un valor por defecto, como los parámetros de las funciones.

La API de amanecer y atardecer: https://api.sunrise-sunset.org/json

Parámetro Tipo ¿Obligatorio? Para qué
lat decimal Sí Latitud
lng decimal Sí Longitud
date fecha No (por defecto, hoy) Otro día
formatted 0 o 1 No (por defecto, 1) 1: reloj de 12 horas con AM/PM; 0: formato ISO 8601
tzid zona horaria No (por defecto, UTC) Devolver las horas en otra zona horaria

🔎 La solución del curso, paso a paso

1. Sin parámetros: error 400 (Bad Request): ¿cómo va a saber la hora del amanecer si no le dices dónde estás? (Verificado: {"results":"","status":"INVALID_REQUEST"}.)

2. Con los parámetros en un diccionario: las claves deben ser exactamente las de la documentación. Tu latitud y longitud se buscan en latlong.net; incluye el signo negativo si lo tiene (en Colombia, la longitud es negativa):

Python
MY_LAT = 4.711       # ejemplo: Bogotá (busca las de tu municipio)
MY_LONG = -74.0721

parameters = {
    "lat": MY_LAT,
    "lng": MY_LONG,
    "formatted": 0,
}

response = requests.get("https://api.sunrise-sunset.org/json", params=parameters)
response.raise_for_status()
data = response.json()
sunrise = data["results"]["sunrise"]
sunset = data["results"]["sunset"]

3. Los parámetros en la URL: requests los agrega así: https://api.sunrise-sunset.org/json?lat=4.711&lng=-74.0721&formatted=0. El ? inicia los parámetros y el & los separa. Puedes pegar esa URL en el navegador para ver la respuesta.

4. Extraer la hora con split: con formatted=0, la respuesta es "2026-09-26T10:43:35+00:00" (verificado):

Python
sunrise_hour = int(sunrise.split("T")[1].split(":")[0])
Paso Resultado
sunrise.split("T") ["2026-09-26", "10:43:35+00:00"]
[1] "10:43:35+00:00"
.split(":") ["10", "43", "35+00", "00"]
[0] y int(...) 10

5. Comparar con la hora actual: datetime.now().hour, que ya está en formato de 24 horas.

🛠️ Nota técnica (muy importante en Colombia): las horas de esta API están en UTC (fíjate en el +00:00). El curso se grabó en Londres, donde la hora local coincide con UTC buena parte del año, así que no se nota el problema. En Colombia (UTC−5), para Bogotá el 26/09/2026 la API dice amanecer a las 10 y atardecer a las 22 (verificado), pero en la hora local son las 5:43 y las 17:51. Si comparas esas horas con datetime.now().hour, que es la hora local, el resultado es incorrecto. Solución: agrega el parámetro "tzid": "America/Bogota" y la API devuelve "2026-09-26T05:43:35-05:00" (verificado).

⚠️ Errores frecuentes

Error Qué ocurre Solución
"lon" o "long" en lugar de "lng" Error 400 Usa las claves de la documentación
Olvidar el signo negativo de la longitud Obtienes los datos de un lugar en Asia Copia el signo
data["sunrise"] KeyError: está dentro de results data["results"]["sunrise"]
Comparar "10" < 17 TypeError Convierte con int()
No usar tzid en Colombia Horas corridas 5 horas "tzid": "America/Bogota"

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: imprime la hora del amanecer y del atardecer de tu municipio en formato 05:43.
  • 🟡 Reto 2: usa el parámetro date para averiguar la hora del atardecer del día de tu cumpleaños.
  • 🟠 Reto 3: usa day_length (duración del día en segundos) y muéstralo en horas y minutos.
  • 🔴 Reto 4: extrae la hora y los minutos para decidir si "ya se puso el sol" con precisión de minutos, no solo de horas. (Investiga datetime.fromisoformat, que convierte el texto completo en un objeto datetime.)

6. 🚀 Proyecto del día: el Notificador de la ISS

(Fuente: 258 - ISS Overhead Notifier Project - Challenge & Solution.md)

Problema

Enviarte un correo cuando la ISS esté cerca de tu posición y sea de noche.

Contexto

La ISS solo se ve a simple vista cuando el cielo está oscuro y pasa relativamente cerca. El programa combina dos APIs (posición de la ISS y amanecer/atardecer), datetime y smtplib.

Requisitos funcionales (del curso)

  1. Si la ISS está cerca de tu posición (±5 grados de latitud y de longitud)…
  2. …y es de noche…
  3. …envíate un correo: "¡Mira hacia arriba! La ISS está sobre ti".
  4. Desafío extra del curso: repetir la comprobación cada 60 segundos.

Pistas del curso:

  • Las horas del amanecer y del atardecer son texto: conviértelas con int().
  • La posición de la ISS también es texto: conviértela con float().
  • Crea una función que devuelva True si la ISS está a ±5 grados.

Planificación

🔍 Antes de programar

  • Entradas: tu latitud y longitud, la posición de la ISS, la hora del amanecer, del atardecer y la actual.
  • Proceso: dos preguntas de sí o no. ¿Cuáles? ¿Qué función responde cada una?
  • Salida: un correo.
  • Si tu latitud es 4,7, ¿entre qué valores debe estar la latitud de la ISS para que cuente como "cerca"?

Construcción paso a paso (solución del curso)

Versión 1 — ¿Está cerca la ISS?

Python
def is_iss_overhead():
    response = requests.get(url="http://api.open-notify.org/iss-now.json")
    response.raise_for_status()
    data = response.json()
    iss_latitude = float(data["iss_position"]["latitude"])
    iss_longitude = float(data["iss_position"]["longitude"])

    if MY_LAT - 5 <= iss_latitude <= MY_LAT + 5 and MY_LONG - 5 <= iss_longitude <= MY_LONG + 5:
        return True

La expresión a <= x <= b comprueba que x esté entre a y b.

Versión 2 — ¿Es de noche?

Python
def is_night():
    parameters = {"lat": MY_LAT, "lng": MY_LONG, "formatted": 0}
    response = requests.get("https://api.sunrise-sunset.org/json", params=parameters)
    response.raise_for_status()
    data = response.json()
    sunrise = int(data["results"]["sunrise"].split("T")[1].split(":")[0])
    sunset = int(data["results"]["sunset"].split("T")[1].split(":")[0])

    time_now = datetime.now().hour
    if time_now >= sunset or time_now <= sunrise:
        return True

El curso encontró un error: primero comparaba datetime.now() (un objeto datetime) con números enteros. La corrección fue usar datetime.now().hour.

Versión 3 — El correo. Si is_iss_overhead() and is_night(), se envía el correo como en el Día 32.

Versión final — Repetir cada 60 segundos:

Python
while True:
    time.sleep(60)
    if is_iss_overhead() and is_night():
        # enviar el correo

El programa ya no termina: sigue funcionando mientras el computador esté encendido.

Pruebas

Es difícil esperar a que la ISS pase de noche. Para probar, simula las condiciones:

Caso Cómo simularlo Esperado
ISS cerca y de noche Temporalmente, pon MY_LAT y MY_LONG iguales a la posición actual de la ISS, y fuerza is_night a devolver True Llega el correo
ISS lejos Posición real No llega nada
Es de día Fuerza is_night a False No llega nada
Sin internet Desconecta la red ⚠️ (ver 🧪)
Probar is_night en Colombia a las 7:00 p. m. Sin tzid ⚠️ (ver 🧪)

🧪 Prueba y depuración

  1. La hora UTC (análisis de esta adaptación). Con las horas UTC de Bogotá (amanecer 10, atardecer 22) y la hora local, así responde el is_night() del curso:
Hora local en Bogotá ¿Está oscuro de verdad? is_night() del curso
6:00 a. m. No (ya amaneció) True ❌
12:00 m. No False ✅
7:00 p. m. Sí False ❌
9:00 p. m. Sí False ❌
11:00 p. m. Sí True ✅

(Simulación verificada.) Corrige la función con tzid y repite la tabla.

  1. Correos repetidos. Con el bucle cada 60 s, mientras la ISS esté cerca llega un correo por minuto. ¿Cómo haces que llegue uno solo por paso de la ISS?
  2. Peticiones innecesarias. El bucle consulta la hora del amanecer cada minuto, aunque solo cambia una vez al día. Guárdala y consúltala una sola vez por día.
  3. Sin internet o API caída. Una sola falla lanza una excepción y detiene el bucle para siempre. Captura requests.exceptions.RequestException y agrega timeout=10 a cada petición para que el programa no se quede esperando indefinidamente.
  4. La línea de cambio de fecha. Si tu longitud fuera 178, la ISS en −179 está a solo 3 grados, pero la resta da 357. No afecta a Colombia, pero ¿cómo lo corregirías?

⭐ Extensión del proyecto

  • Mostrar en el correo la posición exacta y un enlace a un mapa.
  • Registrar en un CSV cada vez que la ISS pasa cerca (fecha, hora, posición).
  • Usar el "crepúsculo civil" (civil_twilight_end) en lugar del atardecer: el cielo está más oscuro.

7. 🌟 Proyecto extra: el Reloj Solar de Colombia

Situación

En clase de Ciencias Naturales se preguntan: ¿por qué en Colombia los días duran casi lo mismo todo el año, mientras que en otros países cambian mucho? Vamos a comprobarlo con datos reales.

Requisitos

  1. Un diccionario con al menos 6 ciudades: 4 de Colombia de distintas regiones (por ejemplo, Leticia, Riohacha, San Andrés y tu municipio) y 2 lejos del ecuador (por ejemplo, Ushuaia y Oslo). Busca tú sus coordenadas en latlong.net.
  2. Para cada ciudad, consultar la API de amanecer y atardecer con formatted=0 y su tzid (investiga la zona horaria de cada ciudad).
  3. Mostrar una tabla con: ciudad, amanecer, atardecer y duración del día en horas y minutos (day_length).
  4. Repetir la consulta para cuatro fechas del año (21 de marzo, 21 de junio, 23 de septiembre y 21 de diciembre) usando el parámetro date.
  5. Guardar los resultados en reloj_solar.csv con pandas.
  6. Responder con los datos: ¿cuántos minutos varía la duración del día en Bogotá entre junio y diciembre? ¿Y en Oslo?
  7. Manejar errores de red sin detener el programa: si una ciudad falla, se informa y se sigue con las demás.

Decisiones que debes tomar

  • ¿Cómo organizas el código para no repetir la petición 24 veces (6 ciudades × 4 fechas)?
  • ¿Muestras los resultados en la consola, en Tkinter o en ambos?

Casos de prueba

Caso Esperado
Ciudad colombiana en junio y diciembre Diferencia de pocos minutos
Oslo en junio y diciembre Diferencia de muchas horas
Coordenadas inválidas (latitud 200) Mensaje de error para esa ciudad; el programa continúa

8. 🏆 Proyecto integrador de los Días 32 y 33: la Alerta del Club de Astronomía

Proyecto integrador creado para esta adaptación. Combina correo y fechas (Día 32) con APIs, parámetros y manejo de respuestas (Día 33), y recupera archivos, CSV y JSON (Días 24 a 30).

Situación problema

El club de astronomía del colegio quiere observar la ISS. Necesita un sistema que avise a los miembros del club cuando la ISS pase cerca de noche, sin llenarlos de correos, y que lleve un registro de los pasos para planear observaciones.

Pregunta orientadora

¿Cómo construir un programa que tome decisiones con datos en vivo de varias APIs y que siga funcionando aunque internet o una API fallen?

Objetivo

Construir un servicio de consola que revise cada minuto la posición de la ISS y envíe una alerta por paso a los miembros del club.

Requerimientos

  1. miembros.csv con nombres y correos ficticios, y un campo activo (sí/no).
  2. config.json con la ubicación del colegio, el margen en grados, el intervalo de revisión y el servidor de correo.
  3. Hora del amanecer y del atardecer con tzid correcto, consultada una vez al día.
  4. Detección de "paso": desde que la ISS entra en el margen hasta que sale. Un solo correo por paso.
  5. Correo con EmailMessage (tildes y ñ correctas) a todos los miembros activos.
  6. pasos.csv con cada paso detectado: fecha, hora de inicio, hora de fin y si fue de noche.
  7. Manejo de HTTPError, ConnectionError y Timeout: el programa espera y vuelve a intentar, sin detenerse.
  8. Un modo simulación (en config.json) que use posiciones falsas de la ISS para poder probar en clase.

Restricciones

  • Todas las peticiones con timeout.
  • Ninguna contraseña en el código (variables de entorno, Día 32).
  • Funciones separadas para: obtener posición, decidir si está cerca, decidir si es de noche, enviar correo y registrar.

Etapas de desarrollo

Etapa Entregable parcial
1. Diseño Diagrama de flujo del bucle y estados ("fuera", "pasando")
2. Funciones de datos obtener_posicion y horas_de_sol con manejo de errores
3. Lógica Detección de paso probada en modo simulación
4. Correo y registro Correos al servidor local y pasos.csv
5. Robustez Prueba desconectando internet durante la ejecución
6. Presentación Demostración en modo simulación y explicación de una ejecución real

Entregables

  • Código organizado en funciones, config.json y datos ficticios.
  • Tabla de 10 casos de prueba con resultados.
  • Registro de errores con al menos 5 errores reales.
  • pasos.csv de al menos 24 horas de ejecución real (si es posible) o de la simulación.

Casos de prueba mínimos

Caso Esperado
Simulación: la ISS entra en el margen durante 5 minutos, de noche Un correo; un registro en pasos.csv
Simulación: paso de día Sin correo; registro con "de noche: no"
Internet desconectado 3 minutos Mensajes de aviso; el programa sigue y se recupera
Miembro con activo = no No recibe correo

Criterios técnicos de funcionamiento

  • El programa corre al menos 1 hora sin detenerse.
  • La API de amanecer y atardecer se consulta una vez al día, no cada minuto.
  • Cambiar la ubicación o el margen solo requiere editar config.json.

Posibles extensiones

Interfaz Tkinter con un mapa sencillo y la posición de la ISS actualizada con after; estadística semanal de pasos nocturnos.


9. 🧩 Comprueba lo aprendido

Conceptuales

  1. Explica con tus palabras qué es un endpoint y qué es un parámetro.
  2. ¿Qué significa un código 404? ¿Y un 500? ¿De quién es la culpa en cada caso?
  3. ¿Por qué raise_for_status() es mejor que escribir un if para cada código?
  4. ¿Por qué un código 200 no garantiza que los datos sean correctos?

Análisis de código

Python
parameters = {"lat": MY_LAT, "lng": MY_LONG, "formatted": 0}
response = requests.get("https://api.sunrise-sunset.org/json", params=parameters)

¿Qué URL completa se envía? ¿Qué pasaría si quitaras "lng"?

Predicción

  1. ¿Qué devuelve "2026-09-26T17:51:28-05:00".split("T")[1].split(":")[0]? ¿De qué tipo es?
  2. Con MY_LAT = 4.7, ¿la ISS en latitud "9.2" está "cerca" según la función del curso? ¿Y en "10.1"?

Depuración

Python
data = response.json()
iss_latitude = data["iss_position"]["latitude"]
if MY_LAT - 5 <= iss_latitude <= MY_LAT + 5:
    print("¡Cerca!")

Este código produce un error. ¿Cuál y por qué?

Python
def is_iss_overhead():
    ...
    if cerca:
        return True

Un compañero dice que la función "devuelve False si no está cerca". ¿Es cierto? ¿Qué devuelve realmente? ¿Importa en un if?

Modificación

Cambia el margen de ±5 grados por una constante y haz que el correo diga a cuántos grados de distancia está la ISS.

Creación

Escribe es_de_noche(hora_actual, hora_amanecer, hora_atardecer) que no haga ninguna petición (solo reciba números) y pruébala con 6 horas distintas. ¿Por qué es más fácil de probar que la versión del curso?


10. 🤖 Programar con apoyo de IA

Actividad 1: la documentación manda. Pregunta a una IA: "¿Qué parámetros acepta la API sunrise-sunset.org?". Compara su respuesta con la documentación oficial de la API. ¿Mencionó tzid? ¿Inventó algún parámetro que no existe? Pruébalo: un parámetro inventado, ¿produce error o se ignora?

Actividad 2: detectar el error de la zona horaria. Pega el is_night() del curso en una IA y pregunta: "¿Funciona correctamente en Bogotá, Colombia?". Si responde que sí, pídele que lo explique con un ejemplo a las 7:00 p. m. Compara con tu tabla de la sección 🧪. Anota si la IA encontró el problema sola o necesitó pistas.

Actividad 3: APIs que ya no existen. Las IA aprenden de textos antiguos y a veces recomiendan APIs retiradas (como REST Countries v3.1, sección 3). Pide a una IA "una API gratuita sin clave para obtener datos de países" y prueba cada una que te recomiende. Registra cuáles funcionan hoy.


11. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
255 Códigos 3xx = "no tienes permiso, vete" Los 3xx son redirecciones; la falta de permiso es 401/403 Nota técnica de la sección 3
255 Un 200 indica que "todo salió bien" El servidor respondió, pero los datos pueden estar desactualizados o indicar error en el contenido (verificado con astros.json y REST Countries) Sección 3
257 Comparar las horas de la API con datetime.now().hour La API devuelve UTC por defecto; en Colombia la comparación falla (verificado). Solución: tzid Nota técnica de la sección 5; 🧪 del proyecto
257 Con formatted=0, "formato Unix, que es de 12 horas" Es el formato ISO 8601, con reloj de 24 horas Sección 5
258 Bucle while True con peticiones sin timeout ni try Un error de red detiene el programa; sin timeout puede quedarse esperando 🧪 del proyecto
258 Revisión cada 60 s Envía un correo por minuto mientras la ISS está cerca 🧪 del proyecto
258 is_iss_overhead solo tiene return True Si no está cerca devuelve None (funciona en un if, pero es mejor devolver False explícitamente) Comprueba lo aprendido
254 Extensión JSON Viewer Awesome Opcional: algunos navegadores ya formatean JSON Sección 2

12. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Objetivos: APIs, rastreador de la ISS 252 Sección 0 — — Sección 6
Qué es una API, analogía del restaurante 253 Sección 1 — Retos 1–4 de la sección 1 Secciones 6, 7 y 8
Endpoint, analogía del banco, JSON como paquete plano, requests.get 254 Sección 2 Primera petición a la ISS Retos 1–4 de la sección 2 Secciones 6 y 8
Códigos HTTP, raise_for_status, response.json(), JSON anidado, latlong.net 255 Sección 3 Posición de la ISS como tupla Retos 1–4 de la sección 3 Secciones 6, 7 y 8
Máquina de citas de Kanye con Tkinter 256 Sección 4 Desafío Kanye Retos 1–4 de la sección 4 —
Parámetros, sunrise-sunset, params, formatted, split 257 Sección 5 Horas en formato de 24 horas Retos 1–4 de la sección 5 Secciones 6, 7 y 8
Notificador de la ISS, ±5 grados, is_night, bucle cada 60 s 258 Sección 6 Proyecto del curso 🧪 del proyecto Sección 8
(No existe en el curso original) — — — Reloj Solar de Colombia Sección 7
(No existe en el curso original) — — — Alerta del Club de Astronomía (Días 32–33) Sección 8

13. 📦 Evidencias de aprendizaje del Día 33

  • Captura del JSON de la ISS en el navegador y tu explicación de cada clave.
  • La tabla de códigos HTTP con un ejemplo real de 200 y de 404 provocado por ti.
  • La máquina de citas funcionando (Kanye o la alternativa de consejos), con manejo de errores.
  • Las horas del amanecer y del atardecer de tu municipio, con y sin tzid, y tu explicación de la diferencia.
  • El Notificador de la ISS con las correcciones de la sección 🧪 y la tabla de is_night corregida.
  • El Reloj Solar de Colombia (proyecto extra) o la Alerta del Club de Astronomía (proyecto integrador).
  • Cuaderno de errores: ModuleNotFoundError de requests, 404 por un endpoint mal escrito, 400 por falta de parámetros, KeyError en JSON anidado, comparación de texto con números, datetime comparado con int, horas UTC, bucle detenido por un error de red y correos repetidos.