diff --git a/07VytvorPozadavekChripka.py b/07VytvorPozadavekChripka.py new file mode 100644 index 0000000..08c66c2 --- /dev/null +++ b/07VytvorPozadavekChripka.py @@ -0,0 +1,125 @@ +#fcb2414b-067b-4ca2-91b2-6c36a86d4cbb = Vladimir Buzalka +#0210db7b-8fb0-4b47-b1d8-ec7a10849a63 = Vladko - testovací aplikace + +#tento kód otevře pacienta podle jeho UUID a založí mu požadavek chřipka a finito + +from pathlib import Path +from datetime import datetime +from playwright.sync_api import sync_playwright, TimeoutError as PWTimeout +import time + +STATE_FILE = Path("medevio_storage.json") +PATIENT_UUID = "0210db7b-8fb0-4b47-b1d8-ec7a10849a63" +PATIENT_URL = f"https://my.medevio.cz/mudr-buzalkova/klinika/pacienti?pacient={PATIENT_UUID}" +MESSAGE_TEXT = "Dobrý den, vakcína proti chřipce je k dispozici, zítra (úterý 23.9) budeme očkovat od 13-17 hodin, prosím potvrďte jestli můžete přijít a jaký čas se Vám hodí." + +def savepage(name: str, page): + """ + Save the current HTML of a Playwright Page to + U:\Dropbox\!!!Days\Downloads Z230\Pages\.html + """ + folder = Path(r"U:\Dropbox\!!!Days\Downloads Z230\Pages") + folder.mkdir(parents=True, exist_ok=True) # ensure the folder exists + + # create sortable timestamp like 2025-09-19_14-05-33 + ts = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + + filepath = folder / f"{ts}_{name}.html" + with filepath.open("w", encoding="utf-8") as f: + f.write(page.content()) + print(f"Saved page snapshot to {filepath}") + +def main(): + with sync_playwright() as p: + browser = p.chromium.launch(headless=False, slow_mo=200) + context = browser.new_context(storage_state=str(STATE_FILE)) + + # ---- keep a stable reference to the patient card page ---- + ptcard = context.new_page() + ptcard.goto(PATIENT_URL, wait_until="networkidle") + #saving ptcard1 + # savepage("ptcard1",ptcard) + + ptcard.get_by_text("Historie požadavků").wait_for(timeout=15_000) + + # 1) Create new request on the patient card + ptcard.get_by_role("button", name="Nový požadavek").click() + ptcard.wait_for_timeout(300) # small settle + + # cursor is already in the "Začněte psát…" field + ptcard.keyboard.type("očkování - chřipka") + ptcard.locator("[role='option']", has_text="Očkování - Chřipka").first.click() + ptcard.get_by_role("button", name="Vytvořit požadavek").click() + + #saving ptcard1 + # savepage("ptcard2",ptcard) + + # 2) Ensure we are back on the patient card again + # (some UIs rerender; either way we want a fresh list) + try: + ptcard.get_by_text("Historie požadavků").wait_for(timeout=7_000) + except PWTimeout: + # If for any reason we are not on the card, navigate back explicitly + ptcard.goto(PATIENT_URL, wait_until="networkidle") + ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + + # Optional: hard refresh to get the just-created request at the top + ptcard.reload(wait_until="networkidle") + ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + time.sleep(5) + + # 3) Open the “Očkování – Chřipka …” request card by its H4 text + # (click the whole card container, not just the heading) + try: + # wait until at least one request card is rendered + ptcard.locator("div[data-testid='patient-request-item']").first.wait_for(timeout=10_000) + + # locate the specific card that contains the H4 with "Očkování - Chřipka" + chripka_card = ptcard.locator("div[data-testid='patient-request-item']").filter( + has=ptcard.locator("h4:has-text('Očkování - Chřipka')") + ).first + + # ensure it's attached/visible then click it + chripka_card.wait_for(timeout=10_000) + chripka_card.click(timeout=5_000) + + except Exception as e: + # Fallback: click the very first card on the list (newest) + try: + first_card = ptcard.locator("div[data-testid='patient-request-item']").first + first_card.click(timeout=5_000) + except Exception: + # if even that fails, save snapshot for inspection and raise + savepage("ptcard_click_fail", ptcard) + raise + + # 4) Wait for request detail and send the message + # We’re now on the detail page + try: + ptcard.wait_for_url("**/pozadavky?pozadavek=*", timeout=10_000) + except PWTimeout: + pass # URL may be SPA; rely on textarea presence + + ptcard.get_by_placeholder("Napište odpověď").wait_for(timeout=10_000) + ptcard.get_by_placeholder("Napište odpověď").fill(MESSAGE_TEXT) + + sent = False + for sel in ["button:has-text('Odeslat')", + "button:has-text('Odeslat zprávu')", + "button:has-text('Odeslat SMS')", + "button:has-text('Odeslat do aplikace')"]: + try: + ptcard.click(sel, timeout=4000) + sent = True + break + except Exception: + continue + + if not sent: + raise RuntimeError("Nepodařilo se najít/kliknout tlačítko Odeslat.") + + ptcard.wait_for_timeout(2000) + print("✅ Požadavek vytvořen, otevřen a zpráva odeslána.") + +if __name__ == "__main__": + main() diff --git a/07VytvorPozadavekChripka01.py b/07VytvorPozadavekChripka01.py new file mode 100644 index 0000000..b717bfd --- /dev/null +++ b/07VytvorPozadavekChripka01.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from pathlib import Path +from datetime import datetime +import re +import time +import pymysql +from pymysql.cursors import DictCursor +from playwright.sync_api import sync_playwright, TimeoutError as PWTimeout + +# ===================================================== +STATE_FILE = Path("medevio_storage.json") # saved login state from your login script + +MYSQL_CFG = dict( + host="192.168.1.76", + port=3307, + user="root", + password="Vlado9674+", + database="medevio", + cursorclass=DictCursor, + autocommit=False, +) + +UUID_COLUMN = "rid" # column with Medevio UUID +FLAG_COLUMN = "pozchripkavytvoren" # bool flag we update after success +FLAG_TS_COL = "pozchripka_vytv_at" # optional timestamp when request created + +MESSAGE_TEXT = ( + "Dobrý den, vakcína proti chřipce je k dispozici, " + "zítra (úterý 23.9) budeme očkovat od 13-17 hodin, " + "prosím potvrďte jestli můžete přijít a jaký čas se Vám hodí." +) +PATIENT_URL_TMPL = "https://my.medevio.cz/mudr-buzalkova/klinika/pacienti?pacient={uuid}" +RC_DIGITS = re.compile(r"\D+") +# ===================================================== + +def normalize_rc(rc: str) -> str: + """Return digits-only RC (removes slash/spaces).""" + return RC_DIGITS.sub("", rc or "") + +def ensure_flag_columns(conn): + """Create required columns if missing (works for all MySQL/MariaDB).""" + needed = { + FLAG_COLUMN: "TINYINT(1) NULL", + FLAG_TS_COL: "DATETIME NULL", + } + with conn.cursor() as cur: + for col, coldef in needed.items(): + cur.execute(""" + SELECT COUNT(*) AS cnt + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'patients_extracted' + AND COLUMN_NAME = %s + """, (col,)) + if cur.fetchone()["cnt"] == 0: + ddl = f"ALTER TABLE `patients_extracted` ADD COLUMN `{col}` {coldef}" + print("Adding column:", ddl) + cur.execute(ddl) + conn.commit() + +def fetch_uuid_by_rc(conn, rc_digits: str) -> dict | None: + """ + Return row with rid (primary key), medevio UUID, jmeno, prijmeni for the given RC. + Prints the query and parameter for debugging. + """ + sql = ( + f"SELECT rid, `{UUID_COLUMN}` AS uuid, jmeno, prijmeni, rc " + "FROM patients_extracted " + "WHERE REPLACE(REPLACE(rc,'/',''),' ','') = %s " + "LIMIT 1" + ) + print("DEBUG SQL:", sql, "| param:", rc_digits) + + with conn.cursor() as cur: + cur.execute(sql, (rc_digits,)) + row = cur.fetchone() + + print("DEBUG result:", row) + return row + +def mark_flag_success(conn, rid: str): + """Update the flag once the Medevio request is created.""" + with conn.cursor() as cur: + cur.execute( + f"UPDATE patients_extracted " + f"SET {FLAG_COLUMN}=1, {FLAG_TS_COL}=NOW() " + f"WHERE rid=%s", + (rid,) + ) + conn.commit() + +def create_flu_request_for_uuid(uuid: str) -> bool: + """Automates Medevio UI to create 'Očkování - Chřipka' request and send MESSAGE_TEXT.""" + with sync_playwright() as p: + browser = p.chromium.launch(headless=False, slow_mo=200) + context = browser.new_context(storage_state=str(STATE_FILE)) + ptcard = context.new_page() + + url = PATIENT_URL_TMPL.format(uuid=uuid) + ptcard.goto(url, wait_until="networkidle") + + # ensure patient card loaded + ptcard.get_by_text("Historie požadavků").wait_for(timeout=15_000) + + # create new request + ptcard.get_by_role("button", name="Nový požadavek").click() + ptcard.wait_for_timeout(300) + ptcard.keyboard.type("očkování - chřipka") + ptcard.locator("[role='option']", has_text="Očkování - Chřipka").first.click() + ptcard.get_by_role("button", name="Vytvořit požadavek").click() + time.sleep(5) + # wait until back on card + try: + ptcard.get_by_text("Historie požadavků").wait_for(timeout=7_000) + except PWTimeout: + ptcard.goto(url, wait_until="networkidle") + ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + + ptcard.reload(wait_until="networkidle") + ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + time.sleep(2) + + # open the new request + try: + ptcard.locator("div[data-testid='patient-request-item']").first.wait_for(timeout=10_000) + chripka_card = ptcard.locator("div[data-testid='patient-request-item']").filter( + has=ptcard.locator("h4:has-text('Očkování - Chřipka')") + ).first + chripka_card.click(timeout=5_000) + except Exception: + ptcard.locator("div[data-testid='patient-request-item']").first.click(timeout=5_000) + + # send the message + try: + ptcard.wait_for_url("**/pozadavky?pozadavek=*", timeout=10_000) + except PWTimeout: + pass + ptcard.get_by_placeholder("Napište odpověď").wait_for(timeout=10_000) + ptcard.get_by_placeholder("Napište odpověď").fill(MESSAGE_TEXT) + + for sel in [ + "button:has-text('Odeslat')", + "button:has-text('Odeslat zprávu')", + "button:has-text('Odeslat SMS')", + "button:has-text('Odeslat do aplikace')", + ]: + try: + ptcard.click(sel, timeout=4000) + browser.close() + return True + except Exception: + continue + + browser.close() + return False + +def main(): + rc_input = input("Zadejte RC (s/bez lomítka, Enter pro konec): ").strip() + # rc_input="320312460" + if not rc_input: + print("Konec.") + return + rc = normalize_rc(rc_input) + + conn = pymysql.connect(**MYSQL_CFG) + try: + ensure_flag_columns(conn) + + row = fetch_uuid_by_rc(conn, rc) + if not row or not row.get("uuid"): + print(f"✗ Pacient s RC {rc} nenalezen nebo nemá sloupec {UUID_COLUMN}.") + return + + print(f"→ Nalezen: {row.get('prijmeni','')} {row.get('jmeno','')} " + f"| RC {row.get('rc','')} | UUID {row['uuid']} | rid {row['rid']}") + + if create_flu_request_for_uuid(row["uuid"]): + mark_flag_success(conn, row["rid"]) + print("✅ Požadavek chřipka vytvořen a DB aktualizována.") + else: + print("✗ Nepodařilo se odeslat zprávu v požadavku.") + + finally: + conn.close() + +if __name__ == "__main__": + main() diff --git a/07VytvorPozadavekChripka02majiucet.py b/07VytvorPozadavekChripka02majiucet.py new file mode 100644 index 0000000..a3a6538 --- /dev/null +++ b/07VytvorPozadavekChripka02majiucet.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from pathlib import Path +import re +import time +import pymysql +from pymysql.cursors import DictCursor +from playwright.sync_api import sync_playwright, TimeoutError as PWTimeout + +# ================== CONFIG ================== +STATE_FILE = Path("medevio_storage.json") + +MYSQL_CFG = dict( + host="192.168.1.76", + port=3307, + user="root", + password="Vlado9674+", + database="medevio", + cursorclass=DictCursor, + autocommit=False, +) + +# Column that goes into the Medevio URL. +# If your Medevio patient UUID is stored in a different column, change this: +UUID_COLUMN = "rid" # Medevio UUID in your table +FLAG_COLUMN = "pozchripkavytvoren" # set to 1 on success +FLAG_TS_COL = "pozchripka_vytv_at" # timestamp when created + +MESSAGE_TEXT = ( + "Dobrý den, vakcína proti chřipce je k dispozici, " + "zítra (úterý 23.9) budeme očkovat od 13-17 hodin, " + "prosím, otevřete si tento požadavek a vyberte si termín. Můžete si samozřejmě vybrat i kterýkoliv jiný den, ale hromadně očkujeme další 4 úterky. Další 4 úterky najdete spoustu termínů." +) + +PATIENT_URL_TMPL = "https://my.medevio.cz/mudr-buzalkova/klinika/pacienti?pacient={uuid}" +BATCH_LIMIT = 2 # change if you want to limit how many to process in one run +PAUSE_BETWEEN = 1.0 # seconds between patients (UI courtesy) +# =========================================== + +RC_DIGITS = re.compile(r"\D+") + +def ensure_flag_columns(conn): + """Create required columns if missing (portable).""" + needed = { + FLAG_COLUMN: "TINYINT(1) NULL", + FLAG_TS_COL: "DATETIME NULL", + } + with conn.cursor() as cur: + for col, coldef in needed.items(): + cur.execute(""" + SELECT COUNT(*) AS cnt + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'patients_extracted' + AND COLUMN_NAME = %s + """, (col,)) + if cur.fetchone()["cnt"] == 0: + cur.execute(f"ALTER TABLE `patients_extracted` ADD COLUMN `{col}` {coldef}") + conn.commit() + +def fetch_batch(conn): + """ + Get patients where: + - flu_reply = 'ano' + - mamedevioucet is true-ish + - rc starts with '8' (after removing slash/spaces) + - pozchripkavytvoren is NULL + - uuid column is present + """ + sql = f""" + SELECT + rid, jmeno, prijmeni, rc, + `{UUID_COLUMN}` AS uuid + FROM patients_extracted + WHERE flu_reply = 'ano' + AND (mamedevioucet = 1 OR mamedevioucet = TRUE OR mamedevioucet = '1') + AND REPLACE(REPLACE(rc,'/',''),' ','') LIKE '0%%' + AND {FLAG_COLUMN} IS NULL + AND `{UUID_COLUMN}` IS NOT NULL + AND `{UUID_COLUMN}` <> '' + ORDER BY prijmeni, jmeno + LIMIT %s + """ + with conn.cursor() as cur: + cur.execute(sql, (BATCH_LIMIT,)) + return cur.fetchall() + +def mark_flag_success(conn, rid: str): + with conn.cursor() as cur: + cur.execute( + f"UPDATE patients_extracted " + f"SET {FLAG_COLUMN}=1, {FLAG_TS_COL}=NOW() " + f"WHERE rid=%s", + (rid,) + ) + conn.commit() + +def create_flu_request_for_uuid(uuid: str) -> bool: + """Automate Medevio UI for one patient: create 'Očkování - Chřipka' and send MESSAGE_TEXT.""" + with sync_playwright() as p: + browser = p.chromium.launch(headless=False, slow_mo=200) + context = browser.new_context(storage_state=str(STATE_FILE)) + ptcard = context.new_page() + + url = PATIENT_URL_TMPL.format(uuid=uuid) + ptcard.goto(url, wait_until="networkidle") + + # ensure patient card loaded + ptcard.get_by_text("Historie požadavků").wait_for(timeout=15_000) + + # create new request + ptcard.get_by_role("button", name="Nový požadavek").click() + ptcard.wait_for_timeout(300) + ptcard.keyboard.type("očkování - chřipka") + ptcard.locator("[role='option']", has_text="Očkování - Chřipka").first.click() + ptcard.get_by_role("button", name="Vytvořit požadavek").click() + time.sleep(2) + # # wait until back on card + # try: + # ptcard.get_by_text("Historie požadavků").wait_for(timeout=7_000) + # except PWTimeout: + # ptcard.goto(url, wait_until="networkidle") + # ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + + # ptcard.reload(wait_until="networkidle") + ptcard.get_by_text("Historie požadavků").wait_for(timeout=10_000) + time.sleep(2) + + # open the new request + try: + ptcard.locator("div[data-testid='patient-request-item']").first.wait_for(timeout=10_000) + chripka_card = ptcard.locator("div[data-testid='patient-request-item']").filter( + has=ptcard.locator("h4:has-text('Očkování - Chřipka')") + ).first + chripka_card.click(timeout=5_000) + except Exception: + ptcard.locator("div[data-testid='patient-request-item']").first.click(timeout=5_000) + + # send the message + # try: + # ptcard.wait_for_url("**/pozadavky?pozadavek=*", timeout=10_000) + # except PWTimeout: + # pass + ptcard.get_by_placeholder("Napište odpověď").wait_for(timeout=10_000) + ptcard.get_by_placeholder("Napište odpověď").fill(MESSAGE_TEXT) + time.sleep(2) + for sel in [ + "button:has-text('Odeslat')", + "button:has-text('Odeslat zprávu')", + "button:has-text('Odeslat SMS')", + "button:has-text('Odeslat do aplikace')", + ]: + try: + ptcard.click(sel, timeout=4000) + browser.close() + return True + except Exception: + continue + + browser.close() + return False + +def main(): + conn = pymysql.connect(**MYSQL_CFG) + try: + ensure_flag_columns(conn) + + rows = fetch_batch(conn) + if not rows: + print("Nenalezen žádný pacient pro zpracování.") + return + + print(f"Zpracujeme {len(rows)} pacientů…") + + processed = ok = fail = 0 + for r in rows: + processed += 1 + rid = r["rid"] + uuid = r["uuid"] + name = f"{r.get('prijmeni','')}, {r.get('jmeno','')}" + rc = r.get("rc","") + + print(f"[{processed:>3}] {name} | RC {rc} | UUID {uuid}") + + try: + success = create_flu_request_for_uuid(uuid) + if success: + mark_flag_success(conn, rid) + ok += 1 + print(" ✓ vytvořeno + odesláno, DB flag nastaven") + else: + fail += 1 + print(" ✗ nepodařilo se odeslat zprávu (tlačítko 'Odeslat' nenalezeno)") + except Exception as e: + fail += 1 + conn.rollback() + print(f" ✗ chyba: {type(e).__name__}: {e}") + + time.sleep(PAUSE_BETWEEN) + + print(f"Hotovo. processed={processed}, ok={ok}, fail={fail}") + finally: + conn.close() + +if __name__ == "__main__": + main()