168 lines
6.2 KiB
Python
168 lines
6.2 KiB
Python
# ============================================================
|
|
# 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):
|
|
#
|
|
# <základní podadresář>\<Type>\<Subtype>\
|
|
# "YYYY-MM-DD Description [VTMF-xxx] [v1.0].<přípona>"
|
|
#
|
|
# 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\<Type>\<Subtype>\...
|
|
"country": False, # -> OUTPUT_ROOT\COUNTRY\<Type>\<Subtype>\...
|
|
"site": False, # -> OUTPUT_ROOT\<číslo centra>\<Type>\<Subtype>\...
|
|
}
|
|
|
|
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].<přípona>'."""
|
|
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 -> [<každé centrum>]."""
|
|
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()
|