# ============================================================ # export_from_seaweed_v1.1.py # Verze: 1.1 # Datum: 2026-06-15 # Popis: Sestaví na disk strom dokumentů studie ze SeaweedFS # (zdroj pravdy = Mongo VTMF.documents + objekty ve Fileru). # Pojmenování podle původních Dropbox pravidel (pipeline v1.5): # # \\\ # "YYYY-MM-DD Description [VTMF-xxx] [v1.0]." # # Základní podadresář pod OUTPUT_ROOT podle úrovně: # study -> STUDY # country -> COUNTRY # site -> <číslo centra> (dokument se zapíše do složky # KAŽDÉHO centra, do kterého je referencovaný) # # RYCHLÉ NASTAVENÍ (nahoře): STUDY + OUTPUT_ROOT + přepínače EXPORT # (True/False u study/country/site). # # Stahuje jen deleted=False, downloaded=True, placeholder!=True, # se seaweed_path. Idempotentní — existující soubory přeskakuje # (resume), takže lze pouštět opakovaně pro „aktuální verzi". # # v1.1: přepínače úrovní (True/False) + základní podadresář dle úrovně # (STUDY / COUNTRY / číslo centra); site-level se kopíruje do # složky každého centra. (v1.0 v TRASH/ — jen study, pevně.) # # Spuštění: # & "...\.venv\Scripts\python.exe" "...\export_from_seaweed_v1.1.py" # ============================================================ import re import sys import urllib.request from pathlib import Path from pymongo import MongoClient, ASCENDING # ==================================================================== # RYCHLÉ NASTAVENÍ # ==================================================================== STUDY = "77242113UCO3001" # která studie OUTPUT_ROOT = Path(r"U:\77242113UCO3001") # kam (pod ní vzniknou # podadresáře dle úrovně) # Co stáhnout — přepni True/False: EXPORT = { "study": True, # -> OUTPUT_ROOT\STUDY\\\... "country": False, # -> OUTPUT_ROOT\COUNTRY\\\... "site": False, # -> OUTPUT_ROOT\<číslo centra>\\\... } OVERWRITE = False # True = přepsat i existující soubory # ==================================================================== MONGO_URI = "mongodb://192.168.1.76:27017" MONGO_DB = "VTMF" MONGO_COLL = "documents" BAD_CHARS_RE = re.compile(r"[<>:\"/\\|?*\x00-\x1f�]") def log(msg): print(msg, flush=True) def clean(s): s = BAD_CHARS_RE.sub("_", str(s or "")) s = re.sub(r"\s+", " ", s) s = re.sub(r"_{2,}", "_", s) return s.strip(" ._") def filename_for(doc): """'YYYY-MM-DD Description [VTMF-xxx] [v1.0].'.""" ext = Path(doc.get("seaweed_path", "")).suffix date = doc.get("date") or "" date_prefix = (date + " ") if date else "" version = f" [{doc['version']}]" if doc.get("version") else "" desc = clean(doc.get("desc")) or clean(doc.get("vtmf")) return f"{date_prefix}{desc} [{doc['vtmf']}]{version}{ext}" def base_dirs_for(doc, level): """Seznam základních podadresářů (pod OUTPUT_ROOT) pro daný dokument. study -> [STUDY], country -> [COUNTRY], site -> [].""" if level == "study": return [OUTPUT_ROOT / "STUDY"] if level == "country": return [OUTPUT_ROOT / "COUNTRY"] # site: jedna kopie do složky každého centra sites = [clean(s) for s in doc.get("sites", []) if clean(s)] if not sites: return [OUTPUT_ROOT / "SITE_nezname"] return [OUTPUT_ROOT / s for s in sites] def target_paths(doc, level): fname = filename_for(doc) typ = clean(doc.get("type")) or "_" sub = clean(doc.get("subtype")) or "_" return [base / typ / sub / fname for base in base_dirs_for(doc, level)] def export_level(coll, level): q = {"level": level, "studies": STUDY, "deleted": False, "downloaded": True, "placeholder": {"$ne": True}, "seaweed_path": {"$ne": None}} docs = list(coll.find(q).sort([("vtmf", ASCENDING), ("version", ASCENDING)])) log(f"\n=== {level.upper()} === dokumentů: {len(docs)}") if not docs: if level != "study": log(f"[i] (žádné {level}-level dokumenty v Mongo — pipeline pro " f"tuto úroveň zatím možná neproběhla)") return 0, 0, 0, 0 written = skipped = failed = 0 total_bytes = 0 for n, doc in enumerate(docs, 1): dests = target_paths(doc, level) need = dests if OVERWRITE else [d for d in dests if not d.exists()] if not need: skipped += len(dests) continue try: with urllib.request.urlopen(doc["seaweed_url"], timeout=120) as r: data = r.read() for dest in need: dest.parent.mkdir(parents=True, exist_ok=True) dest.write_bytes(data) written += 1 total_bytes += len(data) skipped += len(dests) - len(need) kb = len(data) / 1024 size = f"{kb:.0f} KB" if kb < 1024 else f"{kb / 1024:.1f} MB" extra = f" (+{len(need)} kopií)" if len(need) > 1 else "" log(f"[{n}/{len(docs)}] {need[0].relative_to(OUTPUT_ROOT)} ({size}){extra}") except Exception as e: failed += 1 log(f"[!] {doc['_id']}: {e}") return written, skipped, failed, total_bytes def main(): coll = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)[MONGO_DB][MONGO_COLL] levels = [lvl for lvl in ("study", "country", "site") if EXPORT.get(lvl)] log(f"[i] Studie: {STUDY}") log(f"[i] Cíl: {OUTPUT_ROOT}") log(f"[i] Úrovně: {', '.join(levels) if levels else '(žádná — zapni něco v EXPORT)'}") if not levels: sys.exit(0) OUTPUT_ROOT.mkdir(parents=True, exist_ok=True) tw = ts = tf = tb = 0 for level in levels: w, s, f, b = export_level(coll, level) tw += w; ts += s; tf += f; tb += b mb = tb / 1024 / 1024 log(f"\n[ok] Hotovo: {tw} souborů zapsáno ({mb:.1f} MB), " f"{ts} přeskočeno (už existuje), {tf} chyb.") sys.exit(1 if tf else 0) if __name__ == "__main__": main()