Radar de Precios · comparador de farmacias · Python + web estática

Arquitectura del Radar de Precios

Una corrida diaria graba las respuestas públicas de cuatro catálogos, las reprocesa sin red para cruzar productos equivalentes y deja un único data.json. Publicarlo es un acto manual con comparación previa. El visitante recibe una página estática. Cuatro vistas: contenedores, flujos, despliegue y decisiones. Haz clic en cualquier caja; reproduce un flujo para ver el recorrido.

este repo máquina o persona fuera del repo servicio externo tráfico
Detalle

Haz clic en un contenedor

Cada caja es una pieza que corre por separado: la tarea programada, el orquestador, un catálogo externo, una carpeta de archivos o el navegador de quien visita. Las líneas son llamadas reales: protocolo y qué viaja.

Por qué grabar primero

Cada respuesta HTTP se guarda comprimida antes de que nadie la interprete. El procesado lee ese archivo, no el sitio: se puede depurar un cruce equivocado sin volver a pedir nada a las cadenas, y la misma corrida produce los mismos bytes en otra máquina.

Por qué dos pasos para publicar

La corrida solo escribe en una carpeta de staging. Lo que ve el público cambia cuando alguien ejecuta el publicador, lee el resumen y escribe la palabra de confirmación. Si desaparece más de una quinta parte de los productos, el publicador se niega.

Por qué una web sin servidor

Los precios son un archivo JSON y el navegador hace los cálculos. No hay API que mantener ni costo variable. La política de contenido solo permite recursos propios, así que la página no puede llamar a terceros.

Secuencias · una por flujo · los mismos pasos que reproduce el C4
Despliegue · lo que hay y lo que cambiaría

Hoy una máquina de escritorio produce los datos y un hosting estático los sirve. Mañana, una máquina siempre encendida.

La parte pública es solo archivos, así que se puede mover sin tocar código. La parte frágil es la tarea diaria: depende de que el PC esté encendido y con la sesión abierta.

Hoy · PC Windows + hosting estático

tarea diaria a las 02:00 con la sesión iniciadaschtasks
captura, procesado y exportación, en torno a una horapy -m pipeline.run
copia del crudo y del Parquet a la nuberclone copy
publicación manual de data.json con respaldoFTPS o SFTP
web/ sin build: HTML, CSS y JS tal cualhosting estático
política de contenido: solo recursos propiosmeta CSP

Si el PC está apagado o la sesión cerrada a esa hora, ese día no hay corrida. No hay integración continua en el repo.

→ mismos archivos →

Al escalar · propuesta, no es código

la parte pública no cambia: sigue siendo un directoriosin cambios
corrida en una máquina siempre encendidapropuesta
pruebas del matcher y verificación sin red en CIpropuesta
alerta cuando una corrida falla o no se hizopropuesta

Qué gana: corridas que no dependen de una sesión abierta. Qué no cambia: la publicación seguiría siendo manual, por diseño.

Decisiones · con la alternativa descartada

Cada decisión dice qué se eligió, qué se dejó y por qué

DecisiónPor quéDescartado
Grabar el crudo y procesar siempre desde élDepurar contra el archivo y no contra el sitio; la misma corrida da los mismos bytes en otra máquina. La prueba de reproceso lo verifica sin redParsear en vivo mientras se descarga: cualquier cambio del matcher obligaría a volver a pedir todo
Staging separado y publicación manualLa corrida nunca toca lo que ve el público. Alguien lee el resumen y confirma por escritoPublicar al terminar cada corrida: una corrida rota llegaría al público sin que nadie la viera
Tope de desaparecidos del 20 %Si de golpe falta una quinta parte del catálogo, casi seguro falló la captura, no el mercadoPublicar siempre; sin tope, un fallo de una cadena vacía la página
Validar antes de exportarUn 401 o 403, o un snapshot sin filas de la cadena base, aborta sin pisar el histórico; si no, mañana todo saldría como nuevo y se perderían las flechas de cambioExportar lo que haya y marcarlo: el histórico quedaría contaminado
rclone copy, nunca syncLa copia en la nube es un archivo histórico: no debe borrar nada allí. Si falla, la copia de mañana sube lo que falteSincronizar en espejo: un error local se propagaría al archivo
Un falso positivo pesa más que un guionMostrar dos productos distintos como iguales es peor que no cruzarlos. Reglas duras, marca exigida en la zona gris y reparto uno a uno por fuerza de evidenciaBajar el umbral de texto para cruzar más: más cobertura con cruces dudosos
Foto como tercera señalUna huella de imagen confirma coincidencias dudosas y puede vetar un texto casi idéntico con foto claramente distintaDecidir solo por texto: variantes de una misma línea se confunden
Web estática, cálculo en el cliente y CSP estrictaCero costo variable y nada que mantener encendido. El script del tutorial pasó a un archivo propio para permitir la política sin excepcionesUna API propia o scripts en línea: una pieza más que vigilar y una CSP más débil
Solo datos públicos de catálogoPausas por dominio, reintentos con espera ante 429 y 503, sin sesión, sin carrito. La captura debe ser cortés con los sitiosCapturar más rápido o con sesión: más datos a cambio de riesgo para los sitios y para el proyecto