diff --git a/Insurance/StahováníZpráv/111 VZP/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/111 VZP/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..eed5f79 --- /dev/null +++ b/Insurance/StahováníZpráv/111 VZP/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,38 @@ +""" +Přihlásí se na VZP Point a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POZNÁMKA: 01_prihlaseni.py čeká na ruční výběr certifikátu a stisk Enter — +počkejte na výzvu, než se spustí stahování. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace navýšení kapitační platby (p1023821772).txt b/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace navýšení kapitační platby (p1023821772).txt new file mode 100644 index 0000000..95f7bc0 --- /dev/null +++ b/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace navýšení kapitační platby (p1023821772).txt @@ -0,0 +1,30 @@ +IZ: 9305000 - MUDr. Michaela Buzalkov +Obdob: 3/2026 + +IP: 9305001 - Ordinace praktickho lkae pro dospl + +Vkov skupina Poet reg. placench poj. Koeficient Poet jednicovch pojitnc + 15 - 19 let 7 1,06 7,42 + 20 - 24 let 35 0,90 31,50 + 25 - 29 let 32 0,95 30,40 + 30 - 34 let 37 1,00 37,00 + 35 - 39 let 56 1,05 58,80 + 40 - 44 let 54 1,05 56,70 + 45 - 49 let 103 1,10 113,30 + 50 - 54 let 105 1,43 150,15 + 55 - 59 let 60 1,54 92,40 + 60 - 64 let 58 1,59 92,22 + 65 - 69 let 46 1,80 82,80 + 70 - 74 let 52 2,12 110,24 + 75 - 79 let 118 2,54 299,72 + 80 - 84 let 71 3,07 217,97 + nad 85 let 65 3,60 234,00 + +Celkem registrovnch placench pojitnc: 899 +Celkem jednicovch pojitnc: 1 614,62 +Naven kapitanho paulu: 1,00 K +Naven kapitan platby: 1 614,62 K +Degresn koeficient ve vi 0,984831 nebyl uplatnn. + +Naven kapitan platby za obdob: 3/2026 1 614,62 K +Naven kapitan platby za IZ: 9305000 1 614,62 K diff --git a/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace nároku na kapitační platbu za měsíc (p1023820394).txt b/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace nároku na kapitační platbu za měsíc (p1023820394).txt new file mode 100644 index 0000000..f87d293 --- /dev/null +++ b/Insurance/StahováníZpráv/111 VZP/Staženo/2026-04-20 Vyúčtování Rekapitulace nároku na kapitační platbu za měsíc (p1023820394).txt @@ -0,0 +1,30 @@ +IZ: 9305000 - MUDr. Michaela Buzalkov +Obdob: 3/2026 + +IP: 9305001 - Ordinace praktickho lkae pro dospl + +Vkov skupina Poet reg. placench poj. Koeficient Poet jednicovch pojitnc + 15 - 19 let 7 1,06 7,42 + 20 - 24 let 35 0,90 31,50 + 25 - 29 let 32 0,95 30,40 + 30 - 34 let 37 1,00 37,00 + 35 - 39 let 56 1,05 58,80 + 40 - 44 let 54 1,05 56,70 + 45 - 49 let 103 1,10 113,30 + 50 - 54 let 105 1,43 150,15 + 55 - 59 let 60 1,54 92,40 + 60 - 64 let 58 1,59 92,22 + 65 - 69 let 46 1,80 82,80 + 70 - 74 let 52 2,12 110,24 + 75 - 79 let 118 2,54 299,72 + 80 - 84 let 71 3,07 217,97 + nad 85 let 65 3,60 234,00 + +Celkem registrovnch placench pojitnc: 899 +Celkem jednicovch pojitnc: 1 614,62 +Sazba kapitanho paulu: 76,00 K +Celkov kapitan platba: 122 711,12 K +Degresn koeficient ve vi 0,984831 nebyl uplatnn. + +Celkov kapitan platba za obdob: 3/2026 122 711,12 K +Celkov kapitan platba za IZ: 9305000 122 711,12 K diff --git a/Insurance/StahováníZpráv/205 ČPZP/02_stahuj_vse.py b/Insurance/StahováníZpráv/205 ČPZP/02_stahuj_vse.py index 871bc5c..a580632 100644 --- a/Insurance/StahováníZpráv/205 ČPZP/02_stahuj_vse.py +++ b/Insurance/StahováníZpráv/205 ČPZP/02_stahuj_vse.py @@ -13,6 +13,7 @@ POUŽITÍ: python 02_stahuj_vse.py """ +import glob import os import re import json @@ -103,11 +104,17 @@ def stahni_zpravu(session: requests.Session, msg: dict) -> bool: """Stáhne protokol/přílohu zprávy. Vrátí True pokud staženo, False pokud přeskočeno.""" nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})") - # Přeskoč pokud existuje v jakékoliv příponě - for ext in (".html", ".pdf", ".xml", ".zip"): - if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)): - print(f" [přeskočeno] {nazev_base}{ext}") + # Přeskoč pokud existuje soubor se stejným Ref. číslem (imunní vůči Unicode/mezera variantám) + if msg["ref"]: + existing = glob.glob(os.path.join(STAZENO_DIR, f"*(Ref. {msg['ref']}).*")) + if existing: + print(f" [přeskočeno] {os.path.basename(existing[0])}") return False + else: + for ext in (".html", ".pdf", ".xml", ".zip"): + if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)): + print(f" [přeskočeno] {nazev_base}{ext}") + return False # Detail zprávy → najdi download link r = session.get(f"{BASE_URL}/app/schranka/detail/{msg['id']}/", timeout=15) diff --git a/Insurance/StahováníZpráv/205 ČPZP/03_stahuj_nove.py b/Insurance/StahováníZpráv/205 ČPZP/03_stahuj_nove.py new file mode 100644 index 0000000..e50fa15 --- /dev/null +++ b/Insurance/StahováníZpráv/205 ČPZP/03_stahuj_nove.py @@ -0,0 +1,182 @@ +""" +Stahování NOVÝCH protokolů ze schránek ČPZP — zastaví se při první již stažené zprávě. + +Použij po 01_prihlaseni.py (ten uloží cpzp_cookies.json). + +Co dělá: + - Prochází Schránku klienta a Schránku PZS od nejnovějších zpráv + - Jakmile narazí na zprávu, která už je v Staženo/, okamžitě se zastaví + - Vhodné pro pravidelné spouštění — stáhne jen to nové + +POUŽITÍ: + python 03_stahuj_nove.py +""" + +import glob +import os +import re +import json +import time + +import requests +from bs4 import BeautifulSoup + +BASE_URL = "https://portal.cpzp.cz" +COOKIES_FILE = os.path.join(os.path.dirname(__file__), "cpzp_cookies.json") +STAZENO_DIR = os.path.join(os.path.dirname(__file__), "Staženo") + +SCHRANKY = [ + ("/app/schranka/", "Schránka klienta"), + ("/app/schranka-pzs/", "Schránka PZS"), +] +PAGE_SIZE = 20 + + +def make_session() -> requests.Session: + with open(COOKIES_FILE, encoding="utf-8") as f: + cookies = json.load(f) + s = requests.Session() + s.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + for c in cookies: + s.cookies.set(c["name"], c["value"], domain=c["domain"].lstrip(".")) + + r = s.get(f"{BASE_URL}/app/", timeout=15) + if "frmPrihlasCert" in r.text: + raise SystemExit("Cookies expirovala — nejdřív spusť 01_prihlaseni.py") + return s + + +def parse_datum(datum_str: str) -> str: + """'01.04.2026 22:05:42' → '2026-04-01'""" + m = re.match(r"(\d{2})\.(\d{2})\.(\d{4})", datum_str.strip()) + if m: + return f"{m.group(3)}-{m.group(2)}-{m.group(1)}" + return "0000-00-00" + + +def safe_name(text: str) -> str: + return re.sub(r'[\\/:*?"<>|]', "_", text).strip() + + +def uz_stazeno(ref: str, nazev_base: str) -> str | None: + """Vrátí název existujícího souboru, nebo None.""" + if ref: + existing = glob.glob(os.path.join(STAZENO_DIR, f"*(Ref. {ref}).*")) + if existing: + return os.path.basename(existing[0]) + else: + for ext in (".html", ".pdf", ".xml", ".zip"): + if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)): + return nazev_base + ext + return None + + +def stahni_zpravu(session: requests.Session, msg: dict) -> bool: + """Stáhne zprávu. Vrátí True pokud staženo.""" + nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})") + + r = session.get(f"{BASE_URL}/app/schranka/detail/{msg['id']}/", timeout=15) + r.raise_for_status() + soup = BeautifulSoup(r.content, "html.parser", from_encoding="utf-8") + dl_link = soup.find("a", href=re.compile(r"/app/schranka/protokol/")) + if not dl_link: + print(f" [bez přílohy] {msg['id']} — {msg['druh']}") + return False + + for pokus in range(3): + try: + r2 = session.get(BASE_URL + dl_link["href"], timeout=60) + r2.raise_for_status() + break + except requests.exceptions.Timeout: + if pokus == 2: + print(f" [chyba] timeout po 3 pokusech — {msg['druh']} Ref. {msg['ref']}") + return False + time.sleep(3) + + ct = r2.headers.get("Content-Type", "") + link_text = dl_link.get_text(strip=True) + if link_text.lower().endswith(".pdf") or "pdf" in ct: + ext = ".pdf" + elif link_text.lower().endswith(".xml") or "xml" in ct: + ext = ".xml" + elif link_text.lower().endswith(".zip") or "zip" in ct: + ext = ".zip" + else: + ext = ".html" + + cil = os.path.join(STAZENO_DIR, nazev_base + ext) + with open(cil, "wb") as f: + f.write(r2.content) + print(f" [OK] {os.path.basename(cil)}") + return True + + +def stahuj_schranku(session: requests.Session, mailbox_url: str, seen_ids: set) -> tuple[int, bool]: + """ + Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou. + Vrátí (počet stažených, zda bylo dosaženo konce = nalezena stará zpráva). + """ + stazeno = 0 + offset = 0 + + while True: + r = session.get(f"{BASE_URL}{mailbox_url}?offset={offset}", timeout=15) + r.raise_for_status() + soup = BeautifulSoup(r.content, "html.parser", from_encoding="utf-8") + rows = soup.select("tr[id^='message-']") + if not rows: + print(" Žádné další zprávy.") + return stazeno, True + + for row in rows: + msg_id = row["id"].replace("message-", "") + if msg_id in seen_ids: + continue + seen_ids.add(msg_id) + + cells = row.find_all("td") + datum_raw = cells[5].get_text(strip=True) if len(cells) > 5 else "" + druh_ref = cells[4].get_text(separator=" ", strip=True) if len(cells) > 4 else "" + druh_match = re.match(r"^(.+?)\s+Ref\.\s*č\.\s*(\d+)", druh_ref) + druh = druh_match.group(1).strip() if druh_match else druh_ref + ref_c = druh_match.group(2).strip() if druh_match else "" + + msg = { + "id": msg_id, + "datum": parse_datum(datum_raw), + "druh": druh, + "ref": ref_c, + } + + nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})") + existing = uz_stazeno(ref_c, nazev_base) + if existing: + print(f" [stop] Nalezena již stažená zpráva: {existing}") + return stazeno, True + + if stahni_zpravu(session, msg): + stazeno += 1 + time.sleep(1.0) + + offset += PAGE_SIZE + time.sleep(1.0) + + +def main(): + os.makedirs(STAZENO_DIR, exist_ok=True) + session = make_session() + + seen_ids: set = set() + celkem = 0 + + for url, name in SCHRANKY: + print(f"\n=== {name} ({url}) ===") + stazeno, _ = stahuj_schranku(session, url, seen_ids) + celkem += stazeno + + print(f"\nHotovo: {celkem} nových zpráv staženo.") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/205 ČPZP/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/205 ČPZP/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..e37038e --- /dev/null +++ b/Insurance/StahováníZpráv/205 ČPZP/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,35 @@ +""" +Přihlásí se na portál ČPZP a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/205 ČPZP/cpzp_cookies.json b/Insurance/StahováníZpráv/205 ČPZP/cpzp_cookies.json index e1d507e..19337f1 100644 --- a/Insurance/StahováníZpráv/205 ČPZP/cpzp_cookies.json +++ b/Insurance/StahováníZpráv/205 ČPZP/cpzp_cookies.json @@ -1,7 +1,7 @@ [ { "name": "PHPSESSID", - "value": "7ps03755qsp9n4gpms64rqle77", + "value": "5mbiobj1htd5joflu2fpm480a3", "domain": ".portal.cpzp.cz", "path": "/", "expires": -1, diff --git a/Insurance/StahováníZpráv/207 OZP/03_stahuj_nove.py b/Insurance/StahováníZpráv/207 OZP/03_stahuj_nove.py new file mode 100644 index 0000000..28555d1 --- /dev/null +++ b/Insurance/StahováníZpráv/207 OZP/03_stahuj_nove.py @@ -0,0 +1,238 @@ +""" +Stahování NOVÝCH zpráv ze schránek OZP — zastaví se při první již stažené zprávě. + +Použij po 01_prihlaseni.py (ten uloží ozp_cookies.json). + +Co dělá: + - Prochází každou schránku od nejnovějších zpráv + - Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku + - Vhodné pro pravidelné spouštění — stáhne jen to nové + +POUŽITÍ: + python 03_stahuj_nove.py +""" + +import json +import os +import re +import sys +import time +from datetime import datetime +from pathlib import Path + + +BASE_URL = "https://portal.ozp.cz" +INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach" +DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu" +PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol" + +COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "ozp_cookies.json")) +DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo") + +SCHRANKY = { + "7-osobni-schranka-pzs": "Osobní schránka PZS", + "9-schranka-klienta": "Schránka klienta", + "27-zuctovaci-zpravy": "Zúčtovací zprávy", + "167-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS", + "179-schranka-klientu-portalu": "Schránka klientů portálu", + "201-vypis-pojistencu-v-registraci": "Výpis pojištěnců v registraci", +} + + +def parse_date(date_str: str) -> str: + try: + return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d") + except Exception: + try: + return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d") + except Exception: + return "0000-00-00" + + +def safe_filename(name: str) -> str: + return re.sub(r'[\\/:*?"<>|]', "_", name).strip() + + +def parse_row(cells: list) -> dict: + date_raw = cells[1].strip() if len(cells) > 1 else "" + desc_raw = cells[2].strip() if len(cells) > 2 else "" + fname_raw = cells[3].strip() if len(cells) > 3 else "" + + desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()] + if len(desc_lines) >= 3: + description = desc_lines[2] + elif len(desc_lines) >= 2: + description = desc_lines[1] + else: + description = desc_lines[0] if desc_lines else "" + description = description[:80] + + fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw) + original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip() + orig_path = Path(original) + stem = orig_path.stem or "zprava" + ext = orig_path.suffix or "" + + date_iso = parse_date(date_raw) + name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}" + if len(name) > 240: + name = f"{date_iso} ({safe_filename(stem)}){ext}" + + return {"date": date_iso, "desc": description, "original": original, "filename": name} + + +def process_schránka(page, context, segment: str, name: str, already: set) -> int: + """ + Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou. + Vrátí počet stažených souborů. + """ + downloaded = 0 + page_num = 1 + seen_ids: set = set() + + while True: + url = f"{INBOX_URL}/{segment}/stranka-{page_num}" + print(f" Stranka {page_num}: {url}") + try: + page.goto(url, wait_until="domcontentloaded", timeout=30_000) + except Exception as e: + print(f" Navigace selhala: {e}") + break + + page.wait_for_load_state("networkidle", timeout=15_000) + + data = page.evaluate("""() => { + const rows = []; + for (const tr of document.querySelectorAll('table tr')) { + const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim()); + if (cells.length < 4) continue; + const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]'); + if (!dlLink) continue; + const mFile = dlLink.getAttribute('onclick').match(/\\d+/); + const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]'); + const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null; + rows.push({ + cells, + fileId: mFile ? mFile[0] : null, + protokolId: mProt ? mProt[0] : null, + }); + } + return rows; + }""") + rows = [r for r in data if r["fileId"]] + + if not rows: + print(f" Stranka {page_num} - zadne radky, koncim schranku.") + break + + current_ids = {r["fileId"] for r in rows} + if current_ids & seen_ids: + print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.") + break + seen_ids.update(current_ids) + print(f" Nalezeno {len(rows)} zprav.") + + stop = False + for row in rows: + info = parse_row(row["cells"]) + target = os.path.join(DOWNLOAD_DIR, info["filename"]) + + if info["filename"] in already or os.path.exists(target): + print(f" [stop] Nalezena již stažená zpráva: {info['filename']}") + stop = True + break + + dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}" + try: + r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if not r.ok: + print(f" HTTP {r.status} priloha (id={row['fileId']})") + else: + with open(target, "wb") as f: + f.write(r.body()) + print(f" OK: {info['filename']}") + already.add(info["filename"]) + downloaded += 1 + except Exception as e: + print(f" Chyba priloha (id={row['fileId']}): {e}") + time.sleep(1.0) + + if row.get("protokolId"): + prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html") + prot_target = os.path.join(DOWNLOAD_DIR, prot_name) + if prot_name not in already and not os.path.exists(prot_target): + prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}" + try: + r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if r2.ok: + with open(prot_target, "wb") as f: + f.write(r2.body()) + print(f" OK: {prot_name}") + already.add(prot_name) + downloaded += 1 + else: + print(f" HTTP {r2.status} protokol (id={row['protokolId']})") + except Exception as e: + print(f" Chyba protokol (id={row['protokolId']}): {e}") + time.sleep(1.0) + + if stop: + break + + page_num += 1 + + return downloaded + + +def main() -> None: + try: + from playwright.sync_api import sync_playwright + except ImportError: + print("Chybi playwright: pip install playwright && playwright install chrome") + sys.exit(1) + + os.makedirs(DOWNLOAD_DIR, exist_ok=True) + + if not os.path.exists(COOKIES_FILE): + print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py") + sys.exit(1) + + with open(COOKIES_FILE, encoding="utf-8") as f: + cookies = json.load(f) + + with sync_playwright() as p: + context = p.chromium.launch_persistent_context( + user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"), + channel="chrome", + headless=False, + slow_mo=100, + ignore_https_errors=True, + ) + try: + context.add_cookies(cookies) + page = context.new_page() + page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000) + + if "prihlaseni" in page.url or "login" in page.url.lower(): + print("Cookies expirovala - spust 01_prihlaseni.py") + return + print("Prihlaseni OK\n") + + already = set(os.listdir(DOWNLOAD_DIR)) + print(f"V archivu: {len(already)} souboru.\n") + + celkem = 0 + for segment, name in SCHRANKY.items(): + print(f"\n=== {name} ===") + dl = process_schránka(page, context, segment, name, already) + print(f" {name}: stazeno {dl}") + celkem += dl + + print(f"\nHotovo. Celkem stazeno: {celkem}") + + finally: + context.close() + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/207 OZP/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/207 OZP/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..86a0b95 --- /dev/null +++ b/Insurance/StahováníZpráv/207 OZP/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,35 @@ +""" +Přihlásí se na portál OZP a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/207 OZP/ozp_cookies.json b/Insurance/StahováníZpráv/207 OZP/ozp_cookies.json index abf36d5..2a83ef4 100644 --- a/Insurance/StahováníZpráv/207 OZP/ozp_cookies.json +++ b/Insurance/StahováníZpráv/207 OZP/ozp_cookies.json @@ -1,7 +1,7 @@ [ { "name": "SID", - "value": "acdfc00c69bd5a7002809cca6d82e354", + "value": "8c2c0ab2e344d74177d4e7866797dd28", "domain": ".portal.ozp.cz", "path": "/", "expires": -1, @@ -14,7 +14,7 @@ "value": "CERT", "domain": ".portal.ozp.cz", "path": "/", - "expires": 1808228214, + "expires": 1808281759, "secure": true, "httpOnly": false, "sameSite": "Lax" diff --git a/Insurance/StahováníZpráv/209 ZPŠ/03_stahuj_nove.py b/Insurance/StahováníZpráv/209 ZPŠ/03_stahuj_nove.py new file mode 100644 index 0000000..b043a23 --- /dev/null +++ b/Insurance/StahováníZpráv/209 ZPŠ/03_stahuj_nove.py @@ -0,0 +1,237 @@ +""" +Stahování NOVÝCH zpráv ze schránek ZPŠ — zastaví se při první již stažené zprávě. + +Použij po 01_prihlaseni.py (ten uloží zps_cookies.json). + +Co dělá: + - Prochází každou schránku od nejnovějších zpráv + - Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku + - Vhodné pro pravidelné spouštění — stáhne jen to nové + +POUŽITÍ: + python 03_stahuj_nove.py +""" + +import json +import os +import re +import sys +import time +from datetime import datetime +from pathlib import Path + + +BASE_URL = "https://portal.zpskoda.cz" +INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach" +DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu" +PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol" + +COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "zps_cookies.json")) +DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo") + +SCHRANKY = { + "17-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS", + "19-schranka-klienta": "Schránka klienta", + "145-vypis-registrovanych-pojistencu": "Výpis registrovaných pojištěnců", + "169-zpravy-od-klientu-pzp-": "Zprávy od klientů PZP", + "181-schranka-klientu-portalu": "Schránka klientů portálu", +} + + +def parse_date(date_str: str) -> str: + try: + return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d") + except Exception: + try: + return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d") + except Exception: + return "0000-00-00" + + +def safe_filename(name: str) -> str: + return re.sub(r'[\\/:*?"<>|]', "_", name).strip() + + +def parse_row(cells: list) -> dict: + date_raw = cells[1].strip() if len(cells) > 1 else "" + desc_raw = cells[2].strip() if len(cells) > 2 else "" + fname_raw = cells[3].strip() if len(cells) > 3 else "" + + desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()] + if len(desc_lines) >= 3: + description = desc_lines[2] + elif len(desc_lines) >= 2: + description = desc_lines[1] + else: + description = desc_lines[0] if desc_lines else "" + description = description[:80] + + fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw) + original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip() + orig_path = Path(original) + stem = orig_path.stem or "zprava" + ext = orig_path.suffix or "" + + date_iso = parse_date(date_raw) + name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}" + if len(name) > 240: + name = f"{date_iso} ({safe_filename(stem)}){ext}" + + return {"date": date_iso, "desc": description, "original": original, "filename": name} + + +def process_schránka(page, context, segment: str, name: str, already: set) -> int: + """ + Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou. + Vrátí počet stažených souborů. + """ + downloaded = 0 + page_num = 1 + seen_ids: set = set() + + while True: + url = f"{INBOX_URL}/{segment}/stranka-{page_num}" + print(f" Stranka {page_num}: {url}") + try: + page.goto(url, wait_until="domcontentloaded", timeout=30_000) + except Exception as e: + print(f" Navigace selhala: {e}") + break + + page.wait_for_load_state("networkidle", timeout=15_000) + + data = page.evaluate("""() => { + const rows = []; + for (const tr of document.querySelectorAll('table tr')) { + const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim()); + if (cells.length < 4) continue; + const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]'); + if (!dlLink) continue; + const mFile = dlLink.getAttribute('onclick').match(/\\d+/); + const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]'); + const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null; + rows.push({ + cells, + fileId: mFile ? mFile[0] : null, + protokolId: mProt ? mProt[0] : null, + }); + } + return rows; + }""") + rows = [r for r in data if r["fileId"]] + + if not rows: + print(f" Stranka {page_num} - zadne radky, koncim schranku.") + break + + current_ids = {r["fileId"] for r in rows} + if current_ids & seen_ids: + print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.") + break + seen_ids.update(current_ids) + print(f" Nalezeno {len(rows)} zprav.") + + stop = False + for row in rows: + info = parse_row(row["cells"]) + target = os.path.join(DOWNLOAD_DIR, info["filename"]) + + if info["filename"] in already or os.path.exists(target): + print(f" [stop] Nalezena již stažená zpráva: {info['filename']}") + stop = True + break + + dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}" + try: + r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if not r.ok: + print(f" HTTP {r.status} priloha (id={row['fileId']})") + else: + with open(target, "wb") as f: + f.write(r.body()) + print(f" OK: {info['filename']}") + already.add(info["filename"]) + downloaded += 1 + except Exception as e: + print(f" Chyba priloha (id={row['fileId']}): {e}") + time.sleep(1.0) + + if row.get("protokolId"): + prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html") + prot_target = os.path.join(DOWNLOAD_DIR, prot_name) + if prot_name not in already and not os.path.exists(prot_target): + prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}" + try: + r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if r2.ok: + with open(prot_target, "wb") as f: + f.write(r2.body()) + print(f" OK: {prot_name}") + already.add(prot_name) + downloaded += 1 + else: + print(f" HTTP {r2.status} protokol (id={row['protokolId']})") + except Exception as e: + print(f" Chyba protokol (id={row['protokolId']}): {e}") + time.sleep(1.0) + + if stop: + break + + page_num += 1 + + return downloaded + + +def main() -> None: + try: + from playwright.sync_api import sync_playwright + except ImportError: + print("Chybi playwright: pip install playwright && playwright install chrome") + sys.exit(1) + + os.makedirs(DOWNLOAD_DIR, exist_ok=True) + + if not os.path.exists(COOKIES_FILE): + print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py") + sys.exit(1) + + with open(COOKIES_FILE, encoding="utf-8") as f: + cookies = json.load(f) + + with sync_playwright() as p: + context = p.chromium.launch_persistent_context( + user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"), + channel="chrome", + headless=False, + slow_mo=100, + ignore_https_errors=True, + ) + try: + context.add_cookies(cookies) + page = context.new_page() + page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000) + + if "prihlaseni" in page.url or "login" in page.url.lower(): + print("Cookies expirovala - spust 01_prihlaseni.py") + return + print("Prihlaseni OK\n") + + already = set(os.listdir(DOWNLOAD_DIR)) + print(f"V archivu: {len(already)} souboru.\n") + + celkem = 0 + for segment, name in SCHRANKY.items(): + print(f"\n=== {name} ===") + dl = process_schránka(page, context, segment, name, already) + print(f" {name}: stazeno {dl}") + celkem += dl + + print(f"\nHotovo. Celkem stazeno: {celkem}") + + finally: + context.close() + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/209 ZPŠ/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/209 ZPŠ/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..e826403 --- /dev/null +++ b/Insurance/StahováníZpráv/209 ZPŠ/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,35 @@ +""" +Přihlásí se na portál ZPŠ a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json b/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json index 77ac302..caf73bb 100644 --- a/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json +++ b/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json @@ -1,7 +1,7 @@ [ { "name": "SID", - "value": "9ed44d48a017f8915cdfb566d2e2e952", + "value": "e85dcec9acf6345f151cd5996be23576", "domain": ".portal.zpskoda.cz", "path": "/", "expires": -1, @@ -14,7 +14,7 @@ "value": "CERT", "domain": ".portal.zpskoda.cz", "path": "/", - "expires": 1808246438, + "expires": 1808281979, "secure": true, "httpOnly": false, "sameSite": "Lax" diff --git a/Insurance/StahováníZpráv/211 ZPMVČR/02_stahuj_vse.py b/Insurance/StahováníZpráv/211 ZPMVČR/02_stahuj_vse.py index d971c5e..4e63b31 100644 --- a/Insurance/StahováníZpráv/211 ZPMVČR/02_stahuj_vse.py +++ b/Insurance/StahováníZpráv/211 ZPMVČR/02_stahuj_vse.py @@ -16,6 +16,7 @@ POUŽITÍ: python 02_stahuj_vse.py """ +import glob import json import os import re @@ -149,7 +150,11 @@ def process_sekce(page, context, cfg: dict, already: set) -> tuple: filename = f"{period} {label} {prefix}.{ext}" target = os.path.join(DOWNLOAD_DIR, filename) - if filename in already or os.path.exists(target): + # Prefix je unikátní — soubor může mít různý suffix z Content-Disposition + prefix_check = f"{period} {label} {prefix}" + if any(f.startswith(prefix_check) for f in already) or glob.glob( + os.path.join(DOWNLOAD_DIR, glob.escape(prefix_check) + "*") + ): skipped += 1 continue diff --git a/Insurance/StahováníZpráv/211 ZPMVČR/03_stahuj_nove.py b/Insurance/StahováníZpráv/211 ZPMVČR/03_stahuj_nove.py new file mode 100644 index 0000000..3831a4a --- /dev/null +++ b/Insurance/StahováníZpráv/211 ZPMVČR/03_stahuj_nove.py @@ -0,0 +1,234 @@ +""" +Stahování NOVÝCH dokumentů ze ZPMVČR — zastaví se při první již stažené zprávě v každé sekci. + +Použij po 01_prihlaseni.py (ten uloží zpmvcr_cookies.json). + +POUŽITÍ: + python 03_stahuj_nove.py +""" + +import glob +import json +import os +import re +import sys +import time +from datetime import datetime + +BASE_URL = "https://eforms.zpmvcr.cz" +ZPRAVY_URL = f"{BASE_URL}/eforms/smluvni_zdravotnicke_zarizeni/dokumenty_ke_stazeni/zuctovaci_zprava" +AVIZA_URL = f"{BASE_URL}/eforms/smluvni_zdravotnicke_zarizeni/dokumenty_ke_stazeni/aviza" + +COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "zpmvcr_cookies.json")) +DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo") + +SEKCE = [ + { + "name": "Zúčtovací zprávy", + "url": ZPRAVY_URL, + "need_history": True, + "dl_historie": "1", + "ident_col": 1, + "typ_col": 3, + "obdobi_col": 5, + "ident_prefix": "VS-", + }, + { + "name": "Avíza", + "url": AVIZA_URL, + "need_history": False, + "dl_historie": "0", + "ident_col": 1, + "typ_col": 2, + "obdobi_col": 4, + "ident_prefix": "", + }, +] + + +def parse_period(raw: str) -> str: + try: + return datetime.strptime(raw.strip()[:10], "%d.%m.%Y").strftime("%Y-%m") + except Exception: + cleaned = raw.strip()[:7].replace(".", "-") + return cleaned if cleaned else "0000" + + +def safe_filename(name: str) -> str: + return re.sub(r'[\\/:*?"<>|]', "_", name).strip() + + +def make_js_extract(ident_col: int, typ_col: int, obdobi_col: int) -> str: + return f"""() => {{ + const KNOWN_EXTS = new Set(['pdf', 'xml', 'txt', 'zip', 'doc', 'xls', 'csv']); + const docs = []; + for (const inp of document.querySelectorAll('input[name="doc_id"]')) {{ + const docId = inp.value; + if (!docId) continue; + const td = inp.closest('td'); + const tr = td ? td.closest('tr') : null; + let ident = '', typ = 'pdf', obdobi = ''; + if (tr) {{ + const tds = Array.from(tr.querySelectorAll('td')).map(t => t.innerText.trim()); + ident = tds[{ident_col}] || ''; + const t = (tds[{typ_col}] || '').toLowerCase().trim(); + typ = KNOWN_EXTS.has(t) ? t : 'pdf'; + obdobi = tds[{obdobi_col}] || ''; + }} + docs.push({{ docId, ident, typ, obdobi }}); + }} + return docs; + }}""" + + +def click_next_page(page, page_num: int) -> bool: + next_val = str(page_num + 1) + btn = page.locator(f"input[name='page'][value='{next_val}']") + if btn.count() == 0: + return False + btn.first.click() + page.wait_for_load_state("networkidle", timeout=20_000) + return True + + +def uz_stazeno(prefix_check: str, already: set) -> bool: + return any(f.startswith(prefix_check) for f in already) or bool( + glob.glob(os.path.join(DOWNLOAD_DIR, glob.escape(prefix_check) + "*")) + ) + + +def process_sekce(page, context, cfg: dict, already: set) -> int: + name = cfg["name"] + url = cfg["url"] + ident_pfx = cfg["ident_prefix"] + js_extract = make_js_extract(cfg["ident_col"], cfg["typ_col"], cfg["obdobi_col"]) + downloaded = 0 + + print(f"\n=== {name} ===") + page.goto(url, wait_until="domcontentloaded", timeout=30_000) + page.wait_for_load_state("networkidle", timeout=15_000) + + if cfg["need_history"]: + hist = page.get_by_text("Zobrazit historii dokumentů za poslední 3 roky") + if hist.count() > 0: + hist.first.click() + page.wait_for_load_state("networkidle", timeout=20_000) + + page_num = 1 + seen_ids: set = set() + + while True: + print(f" Strana {page_num}...") + docs = page.evaluate(js_extract) + docs = [d for d in docs if d["docId"] not in seen_ids] + + if not docs: + print(f" Strana {page_num} — žádné nové dokumenty, konec sekce.") + break + + seen_ids.update(d["docId"] for d in docs) + print(f" Nalezeno {len(docs)} dokumentů") + + stop = False + for doc in docs: + period = parse_period(doc["obdobi"]) + ext = doc["typ"] or "pdf" + ident = safe_filename(doc["ident"]) + label = safe_filename(name) + prefix = ident_pfx + ident if ident else f"id-{doc['docId']}" + prefix_check = f"{period} {label} {prefix}" + + if uz_stazeno(prefix_check, already): + print(f" [stop] Nalezen již stažený dokument: {prefix_check}*") + stop = True + break + + r = context.request.post(url, form={ + "bin": "1", "dl": "1", "historie": cfg["dl_historie"], + "doc_id": doc["docId"], "save": "Stáhnout", + }, timeout=60_000) + + if not r.ok: + print(f" HTTP {r.status} doc_id={doc['docId']}") + time.sleep(1.0) + continue + + filename = f"{prefix_check}.{ext}" + cd = r.headers.get("content-disposition", "") + m = re.search(r'filename=["\']?([^"\';\r\n]+)', cd) + if m: + orig = m.group(1).strip() + stem, suf = os.path.splitext(orig) + ext2 = suf.lstrip(".").lower() or ext + filename = f"{prefix_check} ({safe_filename(stem)}).{ext2}" + + target = os.path.join(DOWNLOAD_DIR, filename) + with open(target, "wb") as f: + f.write(r.body()) + print(f" OK: {filename}") + already.add(filename) + downloaded += 1 + time.sleep(1.0) + + if stop: + break + + if not click_next_page(page, page_num): + break + page_num += 1 + + return downloaded + + +def main() -> None: + try: + from playwright.sync_api import sync_playwright + except ImportError: + print("Chybi playwright: pip install playwright && playwright install chrome") + sys.exit(1) + + os.makedirs(DOWNLOAD_DIR, exist_ok=True) + + if not os.path.exists(COOKIES_FILE): + print(f"Soubor {COOKIES_FILE} nenalezen — spust 01_prihlaseni.py") + sys.exit(1) + + with open(COOKIES_FILE, encoding="utf-8") as f: + cookies = json.load(f) + + with sync_playwright() as p: + context = p.chromium.launch_persistent_context( + user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"), + channel="chrome", + headless=False, + slow_mo=100, + ignore_https_errors=True, + ) + try: + context.add_cookies(cookies) + page = context.new_page() + + page.goto(ZPRAVY_URL, wait_until="domcontentloaded", timeout=30_000) + page.wait_for_load_state("networkidle", timeout=15_000) + if page.locator("input[name='pin']").count() > 0: + print("Cookies expirovala — spust 01_prihlaseni.py") + return + print("Prihlaseni OK\n") + + already = set(os.listdir(DOWNLOAD_DIR)) + print(f"V archivu: {len(already)} souboru.") + + celkem = 0 + for cfg in SEKCE: + dl = process_sekce(page, context, cfg, already) + print(f" {cfg['name']}: stazeno {dl}") + celkem += dl + + print(f"\nHotovo. Celkem stazeno: {celkem}") + + finally: + context.close() + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/211 ZPMVČR/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/211 ZPMVČR/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..bf03548 --- /dev/null +++ b/Insurance/StahováníZpráv/211 ZPMVČR/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,35 @@ +""" +Přihlásí se na portál ZPMVČR a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/211 ZPMVČR/zpmvcr_cookies.json b/Insurance/StahováníZpráv/211 ZPMVČR/zpmvcr_cookies.json index 26c9ade..c608568 100644 --- a/Insurance/StahováníZpráv/211 ZPMVČR/zpmvcr_cookies.json +++ b/Insurance/StahováníZpráv/211 ZPMVČR/zpmvcr_cookies.json @@ -1,7 +1,7 @@ [ { "name": "JSESSIONID", - "value": "36137BBDC47B5AA6EBACEA28257BB821", + "value": "C487947972DEE36DF5C80FA2F0A328CD", "domain": ".eforms.zpmvcr.cz", "path": "/eforms", "expires": -1, diff --git a/Insurance/StahováníZpráv/213 RBP/03_stahuj_nove.py b/Insurance/StahováníZpráv/213 RBP/03_stahuj_nove.py new file mode 100644 index 0000000..1c76b56 --- /dev/null +++ b/Insurance/StahováníZpráv/213 RBP/03_stahuj_nove.py @@ -0,0 +1,242 @@ +""" +Stahování NOVÝCH zpráv ze schránek RBP — zastaví se při první již stažené zprávě. + +Použij po 01_prihlaseni.py (ten uloží rbp_cookies.json). + +Co dělá: + - Prochází každou schránku od nejnovějších zpráv + - Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku + - Vhodné pro pravidelné spouštění — stáhne jen to nové + +POUŽITÍ: + python 03_stahuj_nove.py +""" + +import json +import os +import re +import sys +import time +from datetime import datetime +from pathlib import Path + + +BASE_URL = "https://portal.rbp-zp.cz" +INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach" +DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu" +PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol" + +COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "rbp_cookies.json")) +DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo") + +SCHRANKY = { + "31-schranka-vyuctovani": "Schránka vyúčtování", + "22-schranka-pzs": "Schránka PZS", + "24-schranka-klienta": "Schránka klienta", + "135-prohlizeni-klientely-odbornosti-001": "Prohlížení klientely", + "136-prohlizeni-plateb": "Prohlížení plateb", + "138-zadanka-o-schvaleni": "Žádanka o schválení", + "170-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS", + "182-schranka-klientu-portalu": "Schránka klientů portálu", + "203-vypis-pojistencu-registrovanych-u-pzs": "Výpis pojištěnců reg. u PZS", + "241-smluvni-dokumenty-pro-pzs": "Smluvní dokumenty pro PZS", +} + + +def parse_date(date_str: str) -> str: + try: + return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d") + except Exception: + try: + return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d") + except Exception: + return "0000-00-00" + + +def safe_filename(name: str) -> str: + return re.sub(r'[\\/:*?"<>|]', "_", name).strip() + + +def parse_row(cells: list) -> dict: + date_raw = cells[1].strip() if len(cells) > 1 else "" + desc_raw = cells[2].strip() if len(cells) > 2 else "" + fname_raw = cells[3].strip() if len(cells) > 3 else "" + + desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()] + if len(desc_lines) >= 3: + description = desc_lines[2] + elif len(desc_lines) >= 2: + description = desc_lines[1] + else: + description = desc_lines[0] if desc_lines else "" + description = description[:80] + + fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw) + original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip() + orig_path = Path(original) + stem = orig_path.stem or "zprava" + ext = orig_path.suffix or "" + + date_iso = parse_date(date_raw) + name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}" + if len(name) > 240: + name = f"{date_iso} ({safe_filename(stem)}){ext}" + + return {"date": date_iso, "desc": description, "original": original, "filename": name} + + +def process_schránka(page, context, segment: str, name: str, already: set) -> int: + """ + Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou. + Vrátí počet stažených souborů. + """ + downloaded = 0 + page_num = 1 + seen_ids: set = set() + + while True: + url = f"{INBOX_URL}/{segment}/stranka-{page_num}" + print(f" Stranka {page_num}: {url}") + try: + page.goto(url, wait_until="domcontentloaded", timeout=30_000) + except Exception as e: + print(f" Navigace selhala: {e}") + break + + page.wait_for_load_state("networkidle", timeout=15_000) + + data = page.evaluate("""() => { + const rows = []; + for (const tr of document.querySelectorAll('table tr')) { + const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim()); + if (cells.length < 4) continue; + const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]'); + if (!dlLink) continue; + const mFile = dlLink.getAttribute('onclick').match(/\\d+/); + const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]'); + const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null; + rows.push({ + cells, + fileId: mFile ? mFile[0] : null, + protokolId: mProt ? mProt[0] : null, + }); + } + return rows; + }""") + rows = [r for r in data if r["fileId"]] + + if not rows: + print(f" Stranka {page_num} - zadne radky, koncim schranku.") + break + + current_ids = {r["fileId"] for r in rows} + if current_ids & seen_ids: + print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.") + break + seen_ids.update(current_ids) + print(f" Nalezeno {len(rows)} zprav.") + + stop = False + for row in rows: + info = parse_row(row["cells"]) + target = os.path.join(DOWNLOAD_DIR, info["filename"]) + + if info["filename"] in already or os.path.exists(target): + print(f" [stop] Nalezena již stažená zpráva: {info['filename']}") + stop = True + break + + dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}" + try: + r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if not r.ok: + print(f" HTTP {r.status} priloha (id={row['fileId']})") + else: + with open(target, "wb") as f: + f.write(r.body()) + print(f" OK: {info['filename']}") + already.add(info["filename"]) + downloaded += 1 + except Exception as e: + print(f" Chyba priloha (id={row['fileId']}): {e}") + time.sleep(1.0) + + if row.get("protokolId"): + prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html") + prot_target = os.path.join(DOWNLOAD_DIR, prot_name) + if prot_name not in already and not os.path.exists(prot_target): + prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}" + try: + r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000) + if r2.ok: + with open(prot_target, "wb") as f: + f.write(r2.body()) + print(f" OK: {prot_name}") + already.add(prot_name) + downloaded += 1 + else: + print(f" HTTP {r2.status} protokol (id={row['protokolId']})") + except Exception as e: + print(f" Chyba protokol (id={row['protokolId']}): {e}") + time.sleep(1.0) + + if stop: + break + + page_num += 1 + + return downloaded + + +def main() -> None: + try: + from playwright.sync_api import sync_playwright + except ImportError: + print("Chybi playwright: pip install playwright && playwright install chrome") + sys.exit(1) + + os.makedirs(DOWNLOAD_DIR, exist_ok=True) + + if not os.path.exists(COOKIES_FILE): + print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py") + sys.exit(1) + + with open(COOKIES_FILE, encoding="utf-8") as f: + cookies = json.load(f) + + with sync_playwright() as p: + context = p.chromium.launch_persistent_context( + user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"), + channel="chrome", + headless=False, + slow_mo=100, + ignore_https_errors=True, + ) + try: + context.add_cookies(cookies) + page = context.new_page() + page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000) + + if "prihlaseni" in page.url or "login" in page.url.lower(): + print("Cookies expirovala - spust 01_prihlaseni.py") + return + print("Prihlaseni OK\n") + + already = set(os.listdir(DOWNLOAD_DIR)) + print(f"V archivu: {len(already)} souboru.\n") + + celkem = 0 + for segment, name in SCHRANKY.items(): + print(f"\n=== {name} ===") + dl = process_schránka(page, context, segment, name, already) + print(f" {name}: stazeno {dl}") + celkem += dl + + print(f"\nHotovo. Celkem stazeno: {celkem}") + + finally: + context.close() + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/213 RBP/04_prihlaseni_a_stahuj_nove.py b/Insurance/StahováníZpráv/213 RBP/04_prihlaseni_a_stahuj_nove.py new file mode 100644 index 0000000..e961a5b --- /dev/null +++ b/Insurance/StahováníZpráv/213 RBP/04_prihlaseni_a_stahuj_nove.py @@ -0,0 +1,35 @@ +""" +Přihlásí se na portál RBP a stáhne nové zprávy. + +Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění. + +POUŽITÍ: + python 04_prihlaseni_a_stahuj_nove.py +""" + +import subprocess +import sys +import os + +DIR = os.path.dirname(os.path.abspath(__file__)) + + +def run(script: str) -> None: + result = subprocess.run( + [sys.executable, os.path.join(DIR, script)], + check=False, + ) + if result.returncode != 0: + raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})") + + +def main() -> None: + print("=== Přihlášení ===") + run("01_prihlaseni.py") + + print("\n=== Stahování nových zpráv ===") + run("03_stahuj_nove.py") + + +if __name__ == "__main__": + main() diff --git a/Insurance/StahováníZpráv/213 RBP/rbp_cookies.json b/Insurance/StahováníZpráv/213 RBP/rbp_cookies.json index b2a05dd..3f3adb0 100644 --- a/Insurance/StahováníZpráv/213 RBP/rbp_cookies.json +++ b/Insurance/StahováníZpráv/213 RBP/rbp_cookies.json @@ -1,7 +1,7 @@ [ { "name": "SID", - "value": "361dbfba7820026abe74bee7d934616e", + "value": "58777dbd1ed8edded39575edb310c5c8", "domain": ".portal.rbp-zp.cz", "path": "/", "expires": -1, @@ -14,7 +14,7 @@ "value": "CERT", "domain": ".portal.rbp-zp.cz", "path": "/", - "expires": 1808227293, + "expires": 1808282104, "secure": true, "httpOnly": false, "sameSite": "Lax"