diff --git a/Medevio/60 ScansProcessing/corrections.json b/Medevio/60 ScansProcessing/corrections.json index 9d4a124..f3d7339 100644 --- a/Medevio/60 ScansProcessing/corrections.json +++ b/Medevio/60 ScansProcessing/corrections.json @@ -1586,5 +1586,41 @@ { "original": "510417052 2026-05-18 Hejkrlík, Jan [LZ urologie] [st.p. DVP 22.6.23 kontinent, erekce 0, CaP gs 3+4 ISUP2 pT2cN0M0 R-, PSA <0.006, E.coli 10^5 IMC].pdf", "corrected": "510417052 2026-05-18 Hejkrlík, Jan [LZ urologie] [st.p. DVP 22.6.23 kontinent, erekce 0, CaP gs 3+4 ISUP2 pT2cN0M0 R-, PSA 0.006, E.coli 10^5 IMC].pdf" + }, + { + "original": "0261070194 2021-11-09 Krähe, Yirka [LZ pediatrie] [výpis zdrav. dok.; trans. chlapec, HRT Nebido od 8/2020, st.p. panic. ataka].pdf", + "corrected": "0261070194 2021-11-09 Krähe, Yirka [LZ pediatrie] [výpis zdrav. dok.; trans. chlapec, HRT Nebido od 82020, st.p. panic. ataka].pdf" + }, + { + "original": "471130030 2026-05-20 Ouředník, František [LZ plicní] [CHOPN 3. st., Trimbo a Berodual 1x/den s efektem, stac. stav, bez exacerbace].pdf", + "corrected": "471130030 2026-05-20 Ouředník, František [LZ plicní] [CHOPN 3. st., Trimbo a Berodual 1xden s efektem, stac. stav, bez exacerbace].pdf" + }, + { + "original": "496203179 2026-05-20 Netřebská, Blanka [LZ kardiologie] [st.p. resekci levé ledviny pro tumor, TK 115/75, EKG SR 75/min PQ 240ms, holter bez záchytu aryt.].pdf", + "corrected": "496203179 2026-05-20 Netřebská, Blanka [LZ kardiologie] [st.p. resekci levé ledviny pro tumor, TK 11575, EKG SR 75min PQ 240ms, holter bez záchytu aryt.].pdf" + }, + { + "original": "505521223 2026-05-19 Vejlupková, Miluše [žádost o výpis zdravotní dokumentace] [žádost o zaslání veškerých informací o léčbě TAT].pdf", + "corrected": "505521223 2026-05-19 Vejlupková, Miluše [žádost o výpis zdravotní dokumentace] [žádost o zaslání veškerých informací o léčbě TAT, MUDr. Miloš Bareš s.r.o.].pdf" + }, + { + "original": "510214143 2026-05-07 Šotola, Radomír [LZ kardiologie] [st.p. SAVR 10/2022 SJM Epic No25, DM2, ICHS, aortální stenóza, námahová dušnost].pdf", + "corrected": "510214143 2026-05-07 Šotola, Radomír [LZ kardiologie] [plánovaná kontrola, st.p. SAVR 102022 SJM Epic No25, DM2, ICHS, aortální stenóza, námahová dušnost].pdf" + }, + { + "original": "510214143 2026-04-28 Šotola, Radomír [PZ rehabilitace] [31MAR2026–28APR2026 TEP L kyčle 28.1.2026, VAS LSpáteře, neuropatie DKK, DM2, hypertenze, ICHS].pdf", + "corrected": "510214143 2026-04-28 Šotola, Radomír [PZ lázně] [31MAR2026–28APR2026 TEP L kyčle 28.1.2026, VAS LSpáteře, neuropatie DKK, DM2, hypertenze, ICHS].pdf" + }, + { + "original": "6454232378 2026-05-14 Vaváková, Dagmar [LZ radiační onkologie] [inflam. ca prsu l.dx. ypT2 ypN2a, adj. RT hrud. stěny vpravo 42.56 Gy16fr, ukončena 14.5.2026].pdf", + "corrected": "6454232378 2026-05-12 Vaváková, Dagmar [LZ radiační onkologie] [inflam. ca prsu l.dx. ypT2 ypN2a, adj. RT hrud. stěny vpravo 42.56 Gy16fr, ukončena 14.5.2026].pdf" + }, + { + "original": "7108290002 2025-08-18 Hašek, Milan [LZ diabetologie] [DM2 PAD od 2/2025, HbA1c 78 (min.97), kompenzace zlepšena, nad cílem, důraz diety].pdf", + "corrected": "7108290002 2025-08-18 Hašek, Milan [LZ diabetologie] [DM2 PAD od 22025, HbA1c 78 (min.97), kompenzace zlepšena, nad cílem, důraz diety].pdf" + }, + { + "original": "9353050003 Dufková, Kateřina split_013.pdf", + "corrected": "9353050003 2026-05-22 Dufková, Kateřina [EKG] [normální křivka].pdf" } ] \ No newline at end of file diff --git a/mcp_firebird.py b/mcp_firebird.py index 87f1049..f755a3c 100644 --- a/mcp_firebird.py +++ b/mcp_firebird.py @@ -419,6 +419,352 @@ def safe_query(sql: str, params: Optional[list] = None) -> dict: raise +@mcp.tool() +def get_patient_prescriptions(idpac: int, months: int = 6) -> dict: + """Vrátí seznam předepsaných léků pacienta z receptů za posledních N měsíců (výchozí 6). + Výsledek: pacient (jmeno, prijmeni, rc) + seznam receptů (datum, lek, dsig). + """ + try: + cur = conn.cursor() + cur.execute("SELECT JMENO, PRIJMENI, RODCIS FROM KAR WHERE IDPAC = ?", [idpac]) + pac = cur.fetchone() + if not pac: + return {'error': f'Pacient IDPAC={idpac} nenalezen'} + + cur.execute(""" + SELECT DATUM, LEK, DSIG + FROM RECEPT + WHERE IDPAC = ? + AND DATUM >= DATEADD(-? MONTH TO CURRENT_DATE) + ORDER BY DATUM DESC, ID DESC + """, [idpac, months]) + + import datetime + recepty = [ + { + 'datum': r[0].isoformat() if isinstance(r[0], datetime.date) else r[0], + 'lek': r[1], + 'dsig': r[2] or '', + } + for r in cur.fetchall() + ] + return { + 'pacient': {'jmeno': pac[0], 'prijmeni': pac[1], 'rc': pac[2]}, + 'obdobi_mesice': months, + 'pocet': len(recepty), + 'recepty': recepty, + } + except Exception: + log(f"get_patient_prescriptions chyba: {traceback.format_exc()}") + raise + + +@mcp.tool() +def get_patient_dxa_files(idpac: int) -> dict: + """Zjistí, zda má pacient fyzicky uložený DXA/denzitometrický nález v tabulce FILES. + Hledá v FILENAME výrazy: DXA, DENZIT, OSTEOPOR (bez rozlišení velikosti písmen). + Vrátí: pacient + seznam nalezených souborů (id, filename, datum, poznamka). + """ + try: + cur = conn.cursor() + cur.execute("SELECT JMENO, PRIJMENI, RODCIS FROM KAR WHERE IDPAC = ?", [idpac]) + pac = cur.fetchone() + if not pac: + return {'error': f'Pacient IDPAC={idpac} nenalezen'} + + cur.execute(""" + SELECT ID, FILENAME, DATUM, POZNAMKA + FROM FILES + WHERE IDPAC = ? + AND UPPER(FILENAME) LIKE '%DXA%' + ORDER BY DATUM DESC, ID DESC + """, [idpac]) + + import datetime + soubory = [ + { + 'id': r[0], + 'filename': r[1], + 'datum': r[2].isoformat() if isinstance(r[2], datetime.date) else r[2], + 'poznamka': r[3] or '', + } + for r in cur.fetchall() + ] + return { + 'pacient': {'jmeno': pac[0], 'prijmeni': pac[1], 'rc': pac[2]}, + 'ma_dxa': len(soubory) > 0, + 'pocet': len(soubory), + 'soubory': soubory, + } + except Exception: + log(f"get_patient_dxa_files chyba: {traceback.format_exc()}") + raise + + +@mcp.tool() +def get_patient_vaccinations(idpac: int) -> dict: + """Vrátí všechna očkování pacienta z tabulky OCKZAZ. + Výsledek: pacient + seznam očkování (datum, latka, zkratka, nazev, davka, por, priste, poznamka). + Řazení: od nejnovějšího. Zrušené záznamy (ZRUSENO='T') jsou označeny. + """ + try: + cur = conn.cursor() + cur.execute("SELECT JMENO, PRIJMENI, RODCIS FROM KAR WHERE IDPAC = ?", [idpac]) + pac = cur.fetchone() + if not pac: + return {'error': f'Pacient IDPAC={idpac} nenalezen'} + + cur.execute(""" + SELECT DATUM, LATKA, ZKRATKA, NAZEV, DAVKA, POR, PRISTE, POZNAMKA, ZRUSENO + FROM OCKZAZ + WHERE IDPAC = ? + ORDER BY DATUM DESC, ID DESC + """, [idpac]) + + import datetime + ockovani = [ + { + 'datum': r[0].isoformat() if isinstance(r[0], datetime.date) else r[0], + 'latka': r[1] or '', + 'zkratka': r[2] or '', + 'nazev': r[3] or '', + 'davka': r[4] or '', + 'poradi': r[5], + 'priste': r[6].isoformat() if isinstance(r[6], datetime.date) else r[6], + 'poznamka': r[7] or '', + 'zruseno': r[8] == 'T', + } + for r in cur.fetchall() + ] + return { + 'pacient': {'jmeno': pac[0], 'prijmeni': pac[1], 'rc': pac[2]}, + 'pocet': len(ockovani), + 'ockovani': ockovani, + } + except Exception: + log(f"get_patient_vaccinations chyba: {traceback.format_exc()}") + raise + + +@mcp.tool() +def get_patient_psa_lab(idpac: int) -> dict: + """Vrátí laboratorní výsledky PSA pacienta: datum, hodnota, jednotka, referenční meze (dolní/horní). + Hledá všechny metody jejichž název obsahuje PSA (Total PSA, Free PSA, PSA celkový atd.). + Výsledek seřazen od nejnovějšího. + """ + try: + cur = conn.cursor() + cur.execute("SELECT JMENO, PRIJMENI, RODCIS FROM KAR WHERE IDPAC = ?", [idpac]) + pac = cur.fetchone() + if not pac: + return {'error': f'Pacient IDPAC={idpac} nenalezen'} + + cur.execute(""" + SELECT + h.DATUM, + m.NAZEV, + d.VYSL, + j.JEDN, + s.NORMDOL, + s.NORMHOR + FROM LABVH h + JOIN LABVD d ON d.IDVH = h.IDVH + JOIN LABMETOD m ON m.IDMETOD = d.IDMETOD + LEFT JOIN LABJEDN j ON j.IDJEDN = d.IDJEDN + LEFT JOIN LABSKALY s ON s.IDSKALY = d.IDSKALY AND s.TYP = '4' + WHERE h.IDPACIENT = ? + AND UPPER(m.NAZEV) LIKE '%PSA%' + ORDER BY h.DATUM DESC, h.IDVH DESC + """, [idpac]) + + import datetime + vysledky = [ + { + 'datum': r[0].isoformat() if isinstance(r[0], datetime.date) else r[0], + 'metoda': r[1], + 'hodnota': r[2], + 'jednotka': r[3] or '', + 'ref_dolni': r[4], + 'ref_horni': r[5], + } + for r in cur.fetchall() + ] + return { + 'pacient': {'jmeno': pac[0], 'prijmeni': pac[1], 'rc': pac[2]}, + 'pocet': len(vysledky), + 'vysledky': vysledky, + } + except Exception: + log(f"get_patient_psa_lab chyba: {traceback.format_exc()}") + raise + + +@mcp.tool() +def get_patient_sick_leaves(idpac: int) -> dict: + """Vrátí přehled pracovních neschopností pacienta z tabulky NES. + Výsledek: pacient + seznam neschopností (od, do, diagnóza, stav, zaměstnavatel, počet dní, ECN číslo). + Stornované záznamy jsou označeny. Řazení od nejnovějšího. + """ + try: + cur = conn.cursor() + cur.execute("SELECT JMENO, PRIJMENI, RODCIS FROM KAR WHERE IDPAC = ?", [idpac]) + pac = cur.fetchone() + if not pac: + return {'error': f'Pacient IDPAC={idpac} nenalezen'} + + cur.execute(""" + SELECT ZACNES, KONNES, DIAGNO, PRACNE, PRICINA, PODNIK, PROFES, + STORNO, ECN, DUVOD_UKONCENI + FROM NES + WHERE IDPAC = ? + ORDER BY ZACNES DESC, ID DESC + """, [idpac]) + + import datetime + def fmt_date(v): + return v.isoformat() if isinstance(v, datetime.date) else v + + def pocet_dni(zacnes, konnes, pracne): + if not zacnes: + return None + konec = konnes if konnes else datetime.date.today() + if isinstance(zacnes, datetime.date) and isinstance(konec, datetime.date): + return (konec - zacnes).days + 1 + return None + + neschopenky = [ + { + 'od': fmt_date(r[0]), + 'do': fmt_date(r[1]), + 'diagno': (r[2] or '').strip(), + 'stav': 'aktivní' if r[3] == 'A' else 'ukončená', + 'pricina': r[4] or '', + 'zamestnavatel': r[5] or '', + 'profes': r[6] or '', + 'storno': r[7] == 'T', + 'ecn': r[8] or '', + 'duvod_ukonceni': r[9] or '', + 'pocet_dni': pocet_dni(r[0], r[1], r[3]), + } + for r in cur.fetchall() + ] + aktivni = sum(1 for n in neschopenky if n['stav'] == 'aktivní' and not n['storno']) + return { + 'pacient': {'jmeno': pac[0], 'prijmeni': pac[1], 'rc': pac[2]}, + 'pocet_celkem': len(neschopenky), + 'pocet_aktivnich': aktivni, + 'neschopenky': neschopenky, + } + except Exception: + log(f"get_patient_sick_leaves chyba: {traceback.format_exc()}") + raise + + +PSA_KODY = ('01131', '01132', '01133', '81227', '81530', '81718', '81800', '93225') + + +@mcp.tool() +def get_psa_records(idpac: Optional[int] = None, rok: Optional[int] = None) -> dict: + """Vrátí přehled vykázaných PSA výkonů z DOKLADD. + Sledované kódy: 01131–01133, 81227, 81530, 81718, 81800, 93225. + idpac: filtr konkrétního pacienta (None = všichni). + rok: filtr roku (None = vše). + Výsledek: počet + seznam (datum, kód, název, příjmení, jméno, rc). + """ + try: + cur = conn.cursor() + kody_placeholder = ','.join(['?' for _ in PSA_KODY]) + params = list(PSA_KODY) + sql = f""" + SELECT d.DATOSE, d.KOD, v.NAZ, k.PRIJMENI, k.JMENO, d.RODCIS + FROM DOKLADD d + JOIN KAR k ON k.RODCIS = d.RODCIS + JOIN VYKONY v ON v.KOD = d.KOD + AND d.DATOSE BETWEEN v.PLATIOD AND COALESCE(v.PLATIDO, CURRENT_DATE) + WHERE d.KOD IN ({kody_placeholder}) + """ + if idpac: + sql += " AND k.IDPAC = ?" + params.append(idpac) + if rok: + sql += " AND EXTRACT(YEAR FROM d.DATOSE) = ?" + params.append(rok) + sql += " ORDER BY d.DATOSE DESC, k.PRIJMENI" + + cur.execute(sql, params) + import datetime + zaznamy = [ + { + 'datum': r[0].isoformat() if isinstance(r[0], datetime.date) else r[0], + 'kod': r[1], + 'naz': r[2], + 'prijmeni': r[3], + 'jmeno': r[4], + 'rc': r[5].strip() if r[5] else '', + } + for r in cur.fetchall() + ] + return { + 'idpac': idpac, + 'rok': rok, + 'pocet': len(zaznamy), + 'zaznamy': zaznamy, + } + except Exception: + log(f"get_psa_records chyba: {traceback.format_exc()}") + raise + + +DXA_KODY = ('10035', '11320', '11321', '11322', '11323', '11324', '11325', '11326') + + +@mcp.tool() +def get_dxa_records(rok: Optional[int] = None) -> dict: + """Vrátí přehled vykázaných DXA výkonů (osteoporóza, denzitometrie). + Sledované kódy: 11320, 11321, 11322–11326, 10035. + rok: filtr roku (např. 2025), None = vše. + Výsledek: celkový počet + seznam záznamů (datum, kód, název kódu, příjmení, jméno, rc). + """ + try: + cur = conn.cursor() + kody_placeholder = ','.join(['?' for _ in DXA_KODY]) + params = list(DXA_KODY) + sql = f""" + SELECT d.DATOSE, d.KOD, v.NAZ, k.PRIJMENI, k.JMENO, d.RODCIS + FROM DOKLADD d + JOIN KAR k ON k.RODCIS = d.RODCIS + JOIN VYKONY v ON v.KOD = d.KOD + AND d.DATOSE BETWEEN v.PLATIOD AND COALESCE(v.PLATIDO, CURRENT_DATE) + WHERE d.KOD IN ({kody_placeholder}) + """ + if rok: + sql += " AND EXTRACT(YEAR FROM d.DATOSE) = ?" + params.append(rok) + sql += " ORDER BY d.DATOSE DESC, k.PRIJMENI" + + cur.execute(sql, params) + import datetime + zaznamy = [ + { + 'datum': r[0].isoformat() if isinstance(r[0], datetime.date) else r[0], + 'kod': r[1], + 'naz': r[2], + 'prijmeni': r[3], + 'jmeno': r[4], + 'rc': r[5].strip() if r[5] else '', + } + for r in cur.fetchall() + ] + return { + 'rok': rok, + 'pocet': len(zaznamy), + 'zaznamy': zaznamy, + } + except Exception: + log(f"get_dxa_records chyba: {traceback.format_exc()}") + raise + + if __name__ == '__main__': log("MCP Firebird server spuštěn (FastMCP)") mcp.run()