"""MP1 · paso 40: extrae del ZIP ATOM original de la PLACSP (sin red) la última versión de la entrada de cada licitación de la muestra del cotejo (pre-registro §11). Imagen etl:v2 (solo biblioteca estándar). Uso: python3 40_atom_extraer.py /w/interno/cotejo/objetivos.json objetivos.json: [{"clave": "...", "zip": "licitaciones/…_2026.zip", "id": "licitacionesPerfilContratante/20768067"}, …] Escribe /w/interno/cotejo/xml/.xml (la entrada con el mayor; también las de los ZIP de años posteriores si se pasan varios) y /w/interno/cotejo/extraccion.json (ficheros recorridos, versiones encontradas por objetivo). """ import sys, json, re, zipfile, time from pathlib import Path from collections import defaultdict T0 = time.time() obj = json.load(open(sys.argv[1])) BASE = Path("/zips") OUT = Path("/w/interno/cotejo/xml"); OUT.mkdir(parents=True, exist_ok=True) por_zip = defaultdict(list) for o in obj: for zname in (o["zip"] if isinstance(o["zip"], list) else [o["zip"]]): por_zip[zname].append(o) mejor = {} # clave -> (updated, xml, fichero) primera_mod = {} # clave -> primera versión (updated) que trae alguna ContractModification vistos = defaultdict(int) inf = {"zips": {}} RE_ENTRY = re.compile(r".*?", re.S) RE_UPD = re.compile(r"([^<]+)") for zname, lista in por_zip.items(): ids = {f"https://contrataciondelestado.es/sindicacion/{o['id']}": o["clave"] for o in lista} z = zipfile.ZipFile(BASE / zname) nf = 0 for name in z.namelist(): if not name.endswith(".atom"): continue nf += 1 txt = z.open(name).read().decode("utf-8", "replace") if not any(k in txt for k in ids): continue for m in RE_ENTRY.finditer(txt): e = m.group(0) for k, clave in ids.items(): if k in e: u = RE_UPD.search(e).group(1) vistos[clave] += 1 if "" in e and (clave not in primera_mod or u < primera_mod[clave]): primera_mod[clave] = u if clave not in mejor or u > mejor[clave][0]: mejor[clave] = (u, e, f"{zname}:{name}") inf["zips"][zname] = {"ficheros_atom": nf, "objetivos": len(lista)} print(zname, nf, "ficheros", round(time.time() - T0), "s", flush=True) for clave, (u, e, f) in mejor.items(): (OUT / f"{clave}.xml").write_text(e, encoding="utf-8") inf["encontrados"] = {o["clave"]: {"versiones": vistos.get(o["clave"], 0), "ultima": mejor.get(o["clave"], (None, None, None))[0], "fichero": mejor.get(o["clave"], (None, None, None))[2], "primera_version_con_modificacion": primera_mod.get(o["clave"])} for o in obj} inf["segundos"] = round(time.time() - T0, 1) json.dump(inf, open("/w/interno/cotejo/extraccion.json", "w"), indent=1, ensure_ascii=False) print(json.dumps({k: v["versiones"] for k, v in inf["encontrados"].items()}), flush=True)