115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Roztřídí DASTA XML soubory do adresářové struktury podle DATA ODBĚRU.
|
||
|
||
Zdroj: u:\\Dropbox\\Ordinace\\pomoc\\DASTA\\*.xml
|
||
Cíl: U:\\DASTA_SUBGROUPS\\RRRR\\MM\\DD\\<soubor>.xml (kopie, originál zůstává)
|
||
|
||
Datum odběru = první element <dat_du> v souboru (první potomek prvního <vr>).
|
||
Hodnota má formát DTS (2016-06-20T08:00:00) nebo DT (2017-05-18T07:30) –
|
||
v obou případech začíná YYYY-MM-DD, takže rok/měsíc/den čteme z prvních znaků.
|
||
|
||
Speciální případy:
|
||
_BEZ_DATUMU soubor neobsahuje žádný <dat_du>
|
||
_CHYBY soubor se nepodařilo naparsovat
|
||
|
||
Použití:
|
||
python roztrid_dle_odberu.py # zdroj = výchozí (Dropbox)
|
||
python roztrid_dle_odberu.py U:\\DASTA # jiný zdrojový adresář
|
||
python roztrid_dle_odberu.py U:\\DASTA --dry-run # jen vypíše, co by udělal
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
import shutil
|
||
import sys
|
||
from collections import Counter
|
||
from pathlib import Path
|
||
from xml.etree import ElementTree as ET
|
||
|
||
ZDROJ_VYCHOZI = Path(r"u:\dasta")
|
||
CIL = Path(r"U:\DASTA_SUBGROUPS")
|
||
|
||
# Záchytný regex pro případ, že ElementTree selže (poškozená hlavička apod.)
|
||
_RE_DAT_DU = re.compile(rb"<dat_du[^>]*>\s*(\d{4})-(\d{2})-(\d{2})")
|
||
|
||
|
||
def datum_odberu(cesta: Path) -> tuple[str, str, str] | None:
|
||
"""Vrátí (rok, měsíc, den) z prvního <dat_du>, nebo None když chybí."""
|
||
raw = cesta.read_bytes()
|
||
# Rychlá a robustní cesta: najdi první <dat_du> v bytech.
|
||
m = _RE_DAT_DU.search(raw)
|
||
if m:
|
||
return m.group(1).decode(), m.group(2).decode(), m.group(3).decode()
|
||
# Záloha přes ElementTree (kdyby byl dat_du formátovaný jinak)
|
||
try:
|
||
root = ET.fromstring(raw)
|
||
el = root.find(".//dat_du")
|
||
if el is not None and el.text:
|
||
d = el.text.strip()
|
||
return d[0:4], d[5:7], d[8:10]
|
||
except ET.ParseError:
|
||
raise
|
||
return None
|
||
|
||
|
||
def main() -> None:
|
||
dry = "--dry-run" in sys.argv
|
||
pozicni = [a for a in sys.argv[1:] if not a.startswith("--")]
|
||
zdroj = Path(pozicni[0]) if pozicni else ZDROJ_VYCHOZI
|
||
|
||
soubory = sorted(zdroj.glob("*.xml"))
|
||
print(f"Zdroj: {zdroj}")
|
||
print(f"Cíl: {CIL}")
|
||
print(f"Nalezeno souborů: {len(soubory)}"
|
||
f"{' [DRY-RUN]' if dry else ''}")
|
||
print("-" * 60)
|
||
|
||
if not dry:
|
||
CIL.mkdir(parents=True, exist_ok=True)
|
||
|
||
stat = Counter()
|
||
roky = Counter()
|
||
chyby: list[str] = []
|
||
|
||
for src in soubory:
|
||
try:
|
||
d = datum_odberu(src)
|
||
except ET.ParseError as e:
|
||
d = None
|
||
cilovy_dir = CIL / "_CHYBY"
|
||
chyby.append(f"{src.name}: parse error – {e}")
|
||
stat["chyba"] += 1
|
||
else:
|
||
if d is None:
|
||
cilovy_dir = CIL / "_BEZ_DATUMU"
|
||
stat["bez_datumu"] += 1
|
||
else:
|
||
rok, mes, den = d
|
||
cilovy_dir = CIL / rok / mes / den
|
||
stat["ok"] += 1
|
||
roky[rok] += 1
|
||
|
||
dst = cilovy_dir / src.name
|
||
if dry:
|
||
continue
|
||
cilovy_dir.mkdir(parents=True, exist_ok=True)
|
||
shutil.copy2(src, dst)
|
||
|
||
print("Hotovo:")
|
||
print(f" zařazeno dle data odběru : {stat['ok']}")
|
||
print(f" bez data (_BEZ_DATUMU) : {stat['bez_datumu']}")
|
||
print(f" chyby parsování (_CHYBY) : {stat['chyba']}")
|
||
if roky:
|
||
print("\nRozložení podle roku:")
|
||
for rok in sorted(roky):
|
||
print(f" {rok}: {roky[rok]}")
|
||
if chyby:
|
||
print("\nDetail chyb:")
|
||
for c in chyby:
|
||
print(f" {c}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|