"""
Stažení detailu receptů (NacistPredpis) z eRecept SÚKL.
Logika přeskakování:
- Recept je v recept_doklad se stav_terminal = 1 → přeskočit (vydaný / zrušený)
- Recept není v recept_doklad → stáhnout (nový)
- Recept je v recept_doklad se stav_terminal = 0 → stáhnout znovu (dosud nevyzvednutý)
Spuštění:
python 10_StahnoutXML.py # všechny od 2025-01-01
python 10_StahnoutXML.py --od 2026-01-01
python 10_StahnoutXML.py --limit 50 # testování
XML odpovědi se ukládají do xml_archive/YYYY-MM-DD/ERP_KOD.xml
"""
import sys
import time
import uuid
from datetime import datetime, timezone, date
from pathlib import Path
import random
import pymysql
import pymysql.cursors
from requests import Session
from requests_pkcs12 import Pkcs12Adapter
from Knihovny.najdi_dropbox import get_dropbox_root
from Knihovny.medicus_db import get_medicus_connection
from Knihovny.mysql_db import connect_mysql
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(errors="replace")
# ── Konfigurace eRecept ──────────────────────────────────────────────────────
PFX_FILE = Path(__file__).parent.parent / "AMBSUKL214235369G_31DEC2024.pfx"
PFX_PASS = "Vlado7309208104++"
API_USER = "e08c89c6-2b1a-4eba-8ed9-4e3e63618379"
API_PASS = "Buzalka@Vladimir2025"
UZIVATEL = "E08C89C6-2B1A-4EBA-8ED9-4E3E63618379"
PRACOVISTE = "00214235367"
ENDPOINT = "https://lekar-soap.erecept.sukl.cz/cuer/Lekar"
NAMESPACE = "http://www.sukl.cz/erp/201704"
PAUZA_MIN = 4 # sekund mezi voláními API (minimum)
PAUZA_MAX = 6 # sekund mezi voláními API (maximum)
# ── Adresáře ─────────────────────────────────────────────────────────────────
XML_DIR = Path(get_dropbox_root()) / "Ordinace" / "Dokumentace_ke_zpracování" / "Zúčtovací zprávy" / "NačteníPředpisuWithClaude" / "xml_archive"
# ── Parametry spuštění (uprav zde) ───────────────────────────────────────────
DATUM_OD = "2025-01-01" # recepty od tohoto data
LIMIT = None # max počet receptů ke stažení; None = bez omezení
# ─────────────────────────────────────────────────────────────────────────────
def nacti_terminal_set(mysql_conn):
"""
Vrátí set ERP kódů, které jsou již terminální (vydané / zrušené / expirované).
Jeden dotaz na začátku — pak jen O(1) lookup v Pythonu.
"""
with mysql_conn.cursor() as cur:
cur.execute("SELECT id_dokladu FROM recept_doklad WHERE stav_terminal = 1")
return {row["id_dokladu"] for row in cur.fetchall()}
def nacti_erp_kody(fb_conn, datum_od, limit=None):
"""
Načte unikátní ERP kódy z Firebirdu (recept_epodani.erp) od datum_od.
Vrací list tuplů: (datum, lek, dop, idpac, prijmeni, jmeno, erp_kod)
"""
if limit:
sql = f"SELECT FIRST {int(limit)}"
else:
sql = "SELECT"
sql += """
r.datum, r.lek, r.dop, r.idpac,
TRIM(kar.prijmeni) AS prijmeni, TRIM(kar.jmeno) AS jmeno,
ep.erp
FROM recept r
JOIN recept_epodani ep ON r.id_epodani = ep.id
JOIN kar ON r.idpac = kar.idpac
WHERE r.datum >= ? AND ep.erp IS NOT NULL AND r.STORNO = 'F'
ORDER BY r.datum DESC
"""
cur = fb_conn.cursor()
cur.execute(sql, [datum_od])
rows = cur.fetchall()
cur.close()
# deduplikace dle ERP kódu — jeden recept může mít více léků (řádků)
seen = set()
unique = []
for row in rows:
erp = row[6]
if erp not in seen:
seen.add(erp)
unique.append(row)
return unique
def volej_nacist_predpis(sess, erp_kod):
"""Zavolá NacistPredpis SOAP a vrátí (status_code, response_text)."""
id_zpravy = str(uuid.uuid4())
odeslano = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S+00:00")
soap_body = (
''
''
''
f''
f''
f''
f'{UZIVATEL}'
f'{PRACOVISTE}'
f''
f''
f'{erp_kod}'
f''
f''
f''
f'{id_zpravy}'
f'202501A'
f'{odeslano}'
f'MEDICUS_____'
f''
f''
''
''
)
headers = {
"Content-Type": 'text/xml; charset="UTF-8"',
"SOAPAction": '"NacistPredpis"',
"User-Agent": "Medicus",
}
resp = sess.post(ENDPOINT, data=soap_body.encode("utf-8"), headers=headers, timeout=15)
return resp.status_code, resp.text
def main():
datum_od = DATUM_OD
limit = LIMIT
dnes = date.today().isoformat()
out_dir = XML_DIR / dnes
out_dir.mkdir(parents=True, exist_ok=True)
# ── 1. Načti terminální sadu z MySQL ─────────────────────────────────────
print("Připojuji MySQL...")
mysql = connect_mysql(database="medicus", cursorclass=pymysql.cursors.DictCursor)
terminal = nacti_terminal_set(mysql)
mysql.close()
print(f" Terminálních receptů v DB: {len(terminal)}\n")
# ── 2. Načti ERP kódy z Firebirdu ────────────────────────────────────────
print("Připojuji Firebird...")
fb = get_medicus_connection()
rows = nacti_erp_kody(fb, datum_od, limit)
fb.close()
print(f" Unikátních ERP kódů v Medicusu (od {datum_od}): {len(rows)}\n")
if not rows:
print("Žádné recepty k zpracování.")
return
# ── 3. Filtruj — přeskoč terminální ──────────────────────────────────────
ke_stazeni = [r for r in rows if r[6] not in terminal]
preskoceno = len(rows) - len(ke_stazeni)
print(f" Přeskočeno (terminální): {preskoceno}")
print(f" Ke stažení: {len(ke_stazeni)}\n")
if not ke_stazeni:
print("Vše je již staženo a terminální. Hotovo.")
return
# ── 4. SOAP session ───────────────────────────────────────────────────────
sess = Session()
sess.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_FILE, pkcs12_password=PFX_PASS))
sess.auth = (API_USER, API_PASS)
ok = 0
chyby = 0
for i, row in enumerate(ke_stazeni, 1):
datum_rec, lek, dop, idpac, prijmeni, jmeno, erp_kod = row
lek_str = f"{lek} {dop}".strip() if dop else str(lek or "").strip()
label = f"{prijmeni} {jmeno}".strip()
print(f"[{i:4d}/{len(ke_stazeni)}] {label:30s} {erp_kod} ", end="", flush=True)
try:
status, text = volej_nacist_predpis(sess, erp_kod)
je_chyba = status != 200 or "" in text
if not je_chyba:
xml_file = out_dir / f"{erp_kod}.xml"
xml_file.write_text(text, encoding="utf-8")
print(f"OK {len(text.encode()) / 1024:5.1f} KB {lek_str[:40]}")
ok += 1
else:
chyba_short = text[:120].replace("\n", " ")
print(f"CHYBA HTTP {status} {chyba_short}")
xml_file = out_dir / f"{erp_kod}_CHYBA.xml"
xml_file.write_text(text, encoding="utf-8")
chyby += 1
except Exception as e:
print(f"EXCEPTION {e}")
chyby += 1
if i < len(ke_stazeni):
time.sleep(random.uniform(PAUZA_MIN, PAUZA_MAX))
print(f"\nHotovo: {ok} OK, {chyby} chyb, {preskoceno} přeskočeno")
print(f"XML: {out_dir}")
if __name__ == "__main__":
main()