z230
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# -*- 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()
|
||||
Reference in New Issue
Block a user