# Tema 5 · 9 · Ejercicio adicional C: meteorología (Open-Meteo) a DynamoDB Presentación del Tema 5: «Ejercicio C · ETL meteorológico a DynamoDB» (ejercicios adicionales; solo si tu docente te lo pide). Duración: a tu ritmo (el entorno se prepara en 15 min). Coste: prácticamente nulo (Lambda y DynamoDB en modo bajo demanda con este volumen). Esta actividad **no necesita la infraestructura base** (carpeta 05.01). > **Etiquetas de lo que creas.** Todo recurso que crees a mano (máquina, volumen, bucket, base de datos, tabla, función...) ponle estas etiquetas, al crearlo (apartado *Tags* / *Etiquetas*) o después desde su pestaña de etiquetas: `curso` = `G214`, `universidad` = `CUNEF` y `actividad` = `05.09-adicionalcclima`. Si algún día hay que borrar recursos a mano, se localizan todos de golpe con `aws resourcegroupstaggingapi get-resources --region eu-north-1 --tag-filters Key=actividad,Values=05.09-adicionalcclima`. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): [README principal](../../README.md), sección «Etiquetas». ## Qué vas a hacer Un ETL completo sobre una API pública real: una Lambda propia pide a **Open-Meteo** la temperatura y la precipitación horarias de varias ciudades y las guarda en una tabla de DynamoDB (`Clima`, clave `ciudad` + `ts`). Esta actividad **no trae script ni plantilla**: creas tú la tabla y la función con la receta de abajo. Este README prepara el entorno, te da el esqueleto corregido del código (`adicional-c-clima.py`, el mismo) y cómo comprobarlo; el diseño completo, la comparación query/scan y las reglas son el entregable y los haces tú. **Qué se crea en AWS:** una tabla DynamoDB `Clima` (On-demand), una función Lambda propia y un rol de IAM propio. Entregable: regla de EventBridge y comparación `query` / `scan`. ## Qué contiene esta carpeta | Fichero | Para qué sirve | En qué paso se usa | |---|---|---| | `adicional-c-clima.py` | Esqueleto corregido de la Lambda: pide a Open-Meteo la predicción horaria de cada ciudad de la variable `CIUDADES` y escribe un ítem por ciudad y hora en la tabla `Clima` (valores como `Decimal`; avisa en logs si la temperatura supera 25 °C). **No es la solución completa**: faltan la regla de EventBridge y la comparación query / scan. No lo despliega ningún script. | Paso 4 (es el código de tu función) | | `README.md` | Este documento. | Siempre | ## Antes de empezar - **No depende de la carpeta 05.01** (tiene su propia tabla). Solo necesitas una cuenta de AWS con permisos para crear recursos. - Región **eu-north-1 (Estocolmo)**. - Usa nombres de función y rol que no choquen con los del laboratorio (no reutilices `crypto-price-tracker*`). Aquí se usan `etl-adicional-c` y `rol-etl-adicional-c`. ## Cómo llevar los ficheros a CloudShell 1. En tu PC, comprime **esta carpeta entera** (`05.09-adicionalcclima`): clic derecho → *Comprimir en archivo ZIP* (Windows 11), *Enviar a > Carpeta comprimida en zip* (Windows 10) o *Comprimir* (Mac). 2. En CloudShell (región eu-north-1): **Actions → Upload file** y elige `05.09-adicionalcclima.zip`. 3. Ejecuta: ```bash cd ~ unzip -o 05.09-adicionalcclima.zip && cd 05.09-adicionalcclima && chmod -R u+rwX . ls ``` > Si ya subiste `g214-materiales.zip` (README principal), no necesitas este zip: entra con `cd ~/g214-materiales/tema5/05.09-adicionalcclima` y, si algún fichero da `Permission denied`, ejecuta allí `chmod -R u+rwX . && chmod +x *.sh`. **Qué verás:** `adicional-c-clima.py` y `README.md`. (No hay `.sh`, no hace falta `chmod`.) ## Paso a paso **1. Prueba la API desde CloudShell** (URL entre comillas y en una sola línea): ```bash curl -s "https://api.open-meteo.com/v1/forecast?latitude=40.4168&longitude=-3.7038&hourly=temperature_2m,precipitation" | head -c 800 ``` **Qué verás:** un JSON con `hourly` (`time`, `temperature_2m`, `precipitation`): 168 valores horarios (7 días) por ciudad. La API da una **predicción**: la tabla solo tendrá de hoy a +7 días. Para tener también el pasado (y poder consultar "las últimas 24 horas") añade `&past_days=1` a la URL del código. **2. Crea la tabla antes de ejecutar** (clave de partición `ciudad`, de ordenación `ts`, modo On-demand): ```bash aws dynamodb create-table --table-name Clima \ --attribute-definitions AttributeName=ciudad,AttributeType=S AttributeName=ts,AttributeType=S \ --key-schema AttributeName=ciudad,KeyType=HASH AttributeName=ts,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST --region eu-north-1 aws dynamodb describe-table --table-name Clima --region eu-north-1 --query Table.TableStatus ``` **Qué verás:** el JSON de la tabla y, en el segundo comando, `"ACTIVE"` (si dice `CREATING`, espera unos segundos y repite). **3. Crea un rol propio con acceso a tu tabla** (una sola vez; solo `Clima`: escritura por lotes, `Query` y `Scan`; la cuenta se lee con `sts`): ```bash ROLE=rol-etl-adicional-c cat > confianza.json <<'EOF' {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]} EOF ROLE_ARN=$(aws iam create-role --role-name $ROLE \ --assume-role-policy-document file://confianza.json --query Role.Arn --output text) aws iam attach-role-policy --role-name $ROLE \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole echo "$ROLE_ARN" sleep 10 ACC=$(aws sts get-caller-identity --query Account --output text) cat > politica.json <:role/rol-etl-adicional-c`). Los demás comandos no imprimen nada. El rol base solo escribe logs; la política añade únicamente acceso a la tabla `Clima`. **4. Empaqueta y crea la función** (sin librerías extra; `boto3` ya viene en Lambda). Sube el timeout a 60 s: con cinco ciudades hay cinco peticiones HTTP y 840 escrituras por lotes por ejecución. Medido: el código tarda ~1 s desde una EC2 y 2,6-3 s en Lambda con 128 MB; con el timeout por defecto de 3 s da `Task timed out` o pasa por los pelos, así que no dependas de él. ```bash cp adicional-c-clima.py lambda_function.py zip -q funcion.zip lambda_function.py aws lambda create-function --function-name etl-adicional-c --runtime python3.12 \ --handler lambda_function.lambda_handler --role "$ROLE_ARN" \ --zip-file fileb://funcion.zip --timeout 60 --memory-size 256 --region eu-north-1 ``` **Qué verás:** un JSON con la configuración de la función (`"FunctionName": "etl-adicional-c"`, `"Timeout": 60`). Si dice que el rol no se puede asumir, espera unos segundos y repite. **5. Define la variable `CIUDADES` en la consola** (el JSON lleva llaves, comillas y comas: es más cómodo ahí). Ve a **Lambda → `etl-adicional-c` → Configuration → Environment variables → Edit → Add environment variable**, clave `CIUDADES` y valor (cinco capitales o más): ``` {"Madrid": [40.4168, -3.7038], "Barcelona": [41.3851, 2.1734], "Sevilla": [37.3891, -5.9845], "Valencia": [39.4699, -0.3763], "Bilbao": [43.263, -2.935]} ``` **Qué verás:** la variable aparece en la lista de variables de entorno. (Las coordenadas de ejemplo son orientativas; esta lista está probada: 5 ciudades, `{"escritos": 840}`; usa las de las ciudades que quieras.) Sin esta variable el código falla al arrancar con `KeyError: 'CIUDADES'`. **6. Invócala y mira los logs:** ```bash aws lambda invoke --function-name etl-adicional-c --cli-binary-format raw-in-base64-out \ --payload '{}' --region eu-north-1 salida.json cat salida.json aws logs tail /aws/lambda/etl-adicional-c --since 10m --region eu-north-1 ``` **Qué verás:** `{"escritos": 840}` con cinco ciudades (5 x 168 horas); repetir la ejecución no duplica (misma clave `ciudad` + `ts`). En los logs, líneas `AVISO : C` cuando se supera el umbral de 25 °C del esqueleto (salta en varias capitales; ajústalo si quieres ver otro resultado). Si la función lanza una excepción, `invoke` muestra `"FunctionError": "Unhandled"` y el detalle está en `salida.json` y en los logs. **7. Comprueba la tabla:** ```bash aws dynamodb scan --table-name Clima --select COUNT --region eu-north-1 --query Count --output text aws dynamodb query --table-name Clima --key-condition-expression "ciudad = :c" \ --expression-attribute-values '{":c":{"S":"Madrid"}}' --select COUNT \ --return-consumed-capacity TOTAL --region eu-north-1 ``` **Qué verás:** el primero, `840`; el segundo, `Count` 168 para Madrid (168 horas por ciudad). Valores de referencia para la comparación query / scan del entregable: un `query` de una ciudad y un día (`Count` 24) consume unas 0,5 unidades de capacidad; el `scan` equivalente con filtro, unas 6 (`--return-consumed-capacity TOTAL`). **8. Entregable (lo haces tú):** la regla de EventBridge (ver la carpeta [05.06](../05.06-ProgramarEventBridge.md) para los comandos de una regla, con tu función `etl-adicional-c`) y la comparación `query` / `scan`. ## Errores frecuentes - **`Task timed out after 3.00 seconds`.** La función se creó con el tiempo máximo por defecto (3 s) y no le dio tiempo. Súbelo a 60 s: `aws lambda update-function-configuration --function-name etl-adicional-c --timeout 60 --region eu-north-1` (o *Configuration → General configuration*). Con el comando del paso 4 ya lo llevas. - **`HTTP Error 502/504` o `429`.** La API pública está saturada. En este código la función falla (no hay `try/except`): invócala de nuevo pasado un minuto. - **`Float types are not supported`.** DynamoDB no admite `float` en boto3: guarda los números como `Decimal(str(valor))` (como hace el esqueleto) o como cadena. - **`KeyError: 'CIUDADES'` o `JSONDecodeError`.** Falta la variable de entorno del paso 5, o el JSON está mal escrito (comillas dobles, corchetes y comas). - **`AccessDeniedException` al escribir en DynamoDB.** Tu rol no tiene la política del paso 3, o la tabla no se llama `Clima` (el código y la política usan ese nombre). - **`ResourceNotFoundException`.** La tabla `Clima` no existe o está en otra región: repite el paso 2 en eu-north-1. - **Pocos o ningún dato de ayer.** La API da una predicción de 7 días: añade `&past_days=1` a la URL del código. ## Limpieza Hazla cuando termines, **en este orden**, y solo borra lo que hayas creado tú: 1. **Regla de EventBridge** (si creaste una): `aws events remove-targets --rule NOMBRE --ids 1 --region eu-north-1` y `aws events delete-rule --name NOMBRE --region eu-north-1` (o **EventBridge → Rules → Delete**). 2. **La función y su rol:** ```bash aws lambda delete-function --function-name etl-adicional-c --region eu-north-1 aws iam detach-role-policy --role-name rol-etl-adicional-c \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole aws iam delete-role-policy --role-name rol-etl-adicional-c --policy-name permisos-mios aws iam delete-role --role-name rol-etl-adicional-c ``` Un error `NoSuchEntity` significa que ese elemento ya no existía. 3. **La tabla DynamoDB** (y sus datos): `aws dynamodb delete-table --table-name Clima --region eu-north-1`. 4. **Opcional:** el grupo de logs (`aws logs delete-log-group --log-group-name /aws/lambda/etl-adicional-c --region eu-north-1`) y los ficheros de CloudShell (`cd ~ && rm -rf 05.09-adicionalcclima 05.09-adicionalcclima.zip`). Esta actividad no usa la infraestructura base, así que **no depende de que la borres**. Si la tienes creada por otras actividades, su borrado (carpeta [05.01](../05.01-infraestructurabase/README.md), sección Limpieza) debe ser el último paso de todo el Tema 5. ## Cómo sabes que has terminado - [ ] La tabla `Clima` está `ACTIVE`. - [ ] La invocación devolvió `{"escritos": 840}` con cinco ciudades (5 x 168 horas) y repetirla no cambia el recuento. - [ ] Entregaste lo que pide la diapositiva (código, regla de EventBridge y comparación query / scan). - [ ] Has hecho la limpieza de esta actividad (regla, función, rol y tabla `Clima`). --- Anterior: [Tema 5 · 8 · Adicional B (Euribor)](../05.08-adicionalbeuribor/README.md) · Siguiente: (ninguna, es la última actividad del Tema 5)