Quitar el reparto por paquetes, que sobra al ir los países en el repositorio

Los zips por país y el descargador se hicieron para no meter 40 MB de visores
en git. Como al final entran, esa vía no aporta nada y sí puede estorbar:
descargar.py descomprimía encima de out/<país>/ y habría pisado en silencio un
visor versionado con la copia de una release vieja.

Fuera src/empaquetar.py, src/descargar.py y sus objetivos del Makefile. Se
queda 'make selector', que sí hace falta: cada visor congela la lista de países
al construirse y al calcular uno nuevo hay que rehacer el de los demás.
This commit is contained in:
hacklab 2026-08-11 18:44:41 +02:00
parent d817c66dec
commit 9f7e9dd173
6 changed files with 16 additions and 189 deletions

3
.gitignore vendored
View file

@ -31,9 +31,6 @@ out/*/*
!out/*/candidatos_estrellas.csv !out/*/candidatos_estrellas.csv
!out/*/stats.json !out/*/stats.json
# Paquetes por país listos para subir a una release
dist/
# Restos de trabajo # Restos de trabajo
*.log *.log
.DS_Store .DS_Store

View file

@ -2,7 +2,7 @@
# así que se puede reejecutar solo la parte que interese. # así que se puede reejecutar solo la parte que interese.
PY := ./.venv/bin/python PY := ./.venv/bin/python
.PHONY: all datos rasters modelo salidas cobertura revisar empaquetar descargar selector limpio .PHONY: all datos rasters modelo salidas cobertura revisar selector limpio
all: datos rasters modelo salidas revisar all: datos rasters modelo salidas revisar
@ -52,16 +52,9 @@ cobertura:
revisar: revisar:
$(PY) src/revisar.py $(PY) src/revisar.py
## --- países: paquetes para publicar y descarga de los publicados --- ## --- desplegable de país ---
# El repositorio lleva España. Los demás se calculan (REGION=xx make all) o se # Cada visor congela la lista de países al construirse, así que al calcular uno
# bajan ya hechos, que son unos pocos MB en vez de un par de horas. # nuevo hay que rehacer el selector de los demás.
empaquetar:
$(PY) src/empaquetar.py
descargar:
$(PY) src/descargar.py $(or $(PAIS),todos)
# Vuelve a poner al día el desplegable de país de todos los visores en disco.
selector: selector:
$(PY) src/paises.py $(PY) src/paises.py

View file

@ -34,7 +34,7 @@ No hace falta servidor ni conexión. Ojo: `visor.html` necesita la carpeta
| `out/ortho/` | Ortofoto PNOA de cada localización, 640 m de lado | | `out/ortho/` | Ortofoto PNOA de cada localización, 640 m de lado |
| `out/stats.json` | Cifras del modelo, que leen el informe y el visor | | `out/stats.json` | Cifras del modelo, que leen el informe y el visor |
| `src/` | El pipeline entero, reproducible | | `src/` | El pipeline entero, reproducible |
| `out/<país>/` | Lo mismo para cada país calculado. No van al repositorio: se publican como paquetes (`make empaquetar`) y se bajan con `make descargar` | | `out/<país>/` | El visor y los datos de cada uno de los otros 22 países |
## Cómo se calcula ## Cómo se calcula
@ -133,18 +133,10 @@ clima, así que ese 8 % del modelo deja de distinguir y les baja la banda de
estrellas a todos por igual. Finlandia no sale con cero cinco-estrellas porque estrellas a todos por igual. Finlandia no sale con cero cinco-estrellas porque
sus sitios sean peores. sus sitios sean peores.
## Bajarse un país, o calcular otro ## Calcular un país
El repositorio lleva España. Los demás se bajan ya hechos, que son unos pocos MB Los 27 del catálogo ya están calculados y vienen en el repositorio. Para añadir
y un momento, en vez de un par de horas y 25 GB: uno nuevo:
```bash
make descargar PAIS=is # solo Islandia
make descargar # todos los publicados
```
Aparecen en el desplegable de país del visor automáticamente. Y para calcular
uno que no esté:
```bash ```bash
./.venv/bin/python src/regions.py # ver el catálogo ./.venv/bin/python src/regions.py # ver el catálogo

View file

@ -1,38 +1,16 @@
# Publicar # Publicar
## Los países, fuera del repositorio ## Los países van dentro del repositorio
En el repositorio va **España y nada más**. Cada visor lleva el relieve y un De cada país entra el visor —que es autónomo, lleva el relieve y el terreno
recorte de terreno por candidato incrustados en base64, así que pesa entre 1 y incrustados— y los datos en CSV: unos 40 MB los 23. Fuera se quedan los WebP,
4 MB y cualquier recálculo reescribe el fichero entero: doce países en el que van ya dentro del visor, los PNG de los mapas, que solo usa el informe, y
historial de git son cientos de MB en unos pocos recálculos, y el clon deja de el GeoTIFF del score.
ser algo que la gente pueda bajarse sin pensárselo.
Los demás se publican como adjuntos de una release: Al clonar están todos, y se salta de uno a otro desde el desplegable del visor.
```bash Ojo al regenerar: el visor es binario a efectos de git, así que cada recálculo
make empaquetar # deja dist/rave-scout-<país>.zip, 13 MB los once mete una copia entera en el historial y esa ya no sale.
```
Se suben a una release con la etiqueta `paises`. A partir de ahí, cualquiera:
```bash
make descargar PAIS=is
make descargar # todos los publicados
```
El paquete lleva el visor y los CSV, que es lo que hace falta para mirar el
país. Fuera se quedan los PNG de los mapas (solo los usa el informe, que
únicamente existe para España), los WebP (ya van dentro del visor) y el GeoTIFF
del score. Al descomprimir se actualiza solo el desplegable de país de todos los
visores que haya en disco, que si no el recién bajado no aparecería en los otros.
Si lo publicas en otro sitio, `RAVE_SCOUT_PAISES` manda sobre la dirección por
defecto:
```bash
RAVE_SCOUT_PAISES=https://mi-servidor/rave-scout make descargar PAIS=no
```
## Este repositorio está en Gitea ## Este repositorio está en Gitea

View file

@ -1,82 +0,0 @@
"""Baja el visor de un país ya calculado, sin recalcular nada.
Calcular un país son un par de horas, varios GB de descarga y unos 25 GB de
disco. Quien solo quiera mirarlo no tiene por qué pagar eso: el visor es un
HTML autónomo de unos pocos MB.
python src/descargar.py is ie hr # países sueltos
python src/descargar.py todos # los que haya publicados
La dirección sale de RAVE_SCOUT_PAISES si está puesta, y si no de las releases
del repositorio. Si el paquete no está publicado todavía, lo dice y sigue con
el siguiente en vez de dejarlo a medias.
"""
import io
import os
import sys
import urllib.error
import urllib.request
import zipfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import paises
import regions as R
BASE = os.environ.get(
"RAVE_SCOUT_PAISES",
"https://gitea.laenre.net/hacklab/EXPLORER/releases/download/paises")
def bajar(code):
if code not in R.REGIONS:
print(f" {code}: no está en el catálogo (src/regions.py)")
return False
if code == R.DEFAULT:
print(f" {code}: es el país por defecto, ya viene en el repositorio")
return False
url = f"{BASE}/rave-scout-{code}.zip"
print(f" {R.REGIONS[code]['name']:14s} {url}")
try:
with urllib.request.urlopen(url, timeout=120) as r:
datos = r.read()
except urllib.error.HTTPError as e:
print(f" no está publicado todavía (HTTP {e.code})")
return False
except Exception as e:
print(f" no se ha podido bajar: {type(e).__name__}")
return False
with zipfile.ZipFile(io.BytesIO(datos)) as zf:
# Las rutas del zip son out/<país>/…, relativas a la raíz. Se comprueba
# que ninguna se salga de ahí antes de escribir nada: un zip es un
# fichero de fuera y '../' dentro de un nombre escribe donde quiera.
for nombre in zf.namelist():
destino = (paises.ROOT / nombre).resolve()
if not str(destino).startswith(str((paises.ROOT / "out").resolve())):
raise SystemExit(f" ruta sospechosa en el zip: {nombre}")
zf.extractall(paises.ROOT)
n = len(list(zf.namelist()))
print(f" {n} ficheros en out/{code}/")
return True
def main(args):
if not args:
raise SystemExit(__doc__)
if args == ["todos"]:
args = [c for c in R.REGIONS if c != R.DEFAULT]
ok = sum(bajar(c) for c in args)
if ok:
# Los visores llevan la lista de países congelada de cuando se
# construyeron: sin esto, el país recién bajado no sale en el
# desplegable de los demás.
print("\nactualizando el selector de país de los visores…")
paises.refrescar()
print(f"\n{ok} países añadidos.")
if __name__ == "__main__":
main(sys.argv[1:])

View file

@ -1,51 +0,0 @@
"""Empaqueta cada país calculado en un zip para subirlo a una release.
El repositorio se queda con España. Los demás países pesan entre 1 y 4 MB de
visor cada uno, y como el visor lleva el relieve incrustado, cualquier recálculo
reescribe el fichero entero: en el historial de git eso son cientos de MB en
unos pocos recálculos, y el clon deja de ser portable.
Al paquete va solo lo que hace falta para mirar el país, que es el visor es
autónomo, sin servidor ni conexión y los datos en CSV. Fuera se quedan los
PNG de los mapas (solo los usa el informe, que únicamente existe para España),
los WebP (van ya incrustados dentro del visor) y el GeoTIFF del score, que pesa
lo que pesa y se regenera.
"""
import sys
import zipfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import paises
import regions as R
DENTRO = ("visor.html", "candidatos_estrellas.csv", "stats.json")
DIST = paises.ROOT / "dist"
def main():
DIST.mkdir(exist_ok=True)
hechos = [(c, d, n) for c, d, n in paises.calculados() if c != R.DEFAULT]
if not hechos:
raise SystemExit("no hay ningún país calculado aparte del de por defecto")
total = 0
for code, d, n in sorted(hechos):
z = DIST / f"rave-scout-{code}.zip"
with zipfile.ZipFile(z, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
for nombre in DENTRO:
f = d / nombre
if f.exists():
# Dentro del zip cuelgan de out/<país>/, para que se
# descomprima en su sitio desde la raíz del proyecto.
zf.write(f, f"out/{code}/{nombre}")
total += z.stat().st_size
print(f" {R.REGIONS[code]['name']:14s} {n:>4} localizaciones "
f"{z.stat().st_size/1e6:5.1f} MB {z.name}")
print(f"\n{len(hechos)} paquetes, {total/1e6:.1f} MB en {DIST}")
print("Súbelos como adjuntos de una release con la etiqueta 'paises'.")
if __name__ == "__main__":
main()