notebookvb
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# 60 ScansProcessing
|
||||
|
||||
Agent pro zpracování naskenovaných lékařských zpráv (PDF i JPG/PNG).
|
||||
|
||||
## Skripty
|
||||
|
||||
### `extract_patient_info.py` — hlavní agent
|
||||
Spuštění: `python extract_patient_info.py` (bez argumentů = celá složka ToProcess)
|
||||
|
||||
**Workflow:**
|
||||
1. Načte soubory z `ToProcess/`
|
||||
2. Claude Vision API (sonnet-4-6) extrahuje: jméno, RČ, datum, typ dokumentu, poznámku, navržený název, rotaci
|
||||
3. Ověří pacienta v Medicus Firebird (tabulka KAR, pole RODCIS/PRIJMENI/JMENO)
|
||||
4. Fuzzy matching RČ při nenalezení: vynechání cifry + záměna podobných (0↔8, 1↔7, 5↔6, 3↔8) + checksum /11
|
||||
5. Upozorní na duplicitu v `U:\Dropbox\Ordinace\Dokumentace_zpracovaná\`
|
||||
6. Interaktivní schválení / oprava názvu
|
||||
7. JPG/PNG → skutečné PDF (správná orientace, DPI=150, quality=80)
|
||||
8. Přesun do `Processed/`, smazání z `ToProcess/`
|
||||
9. Opravy názvů se ukládají do `corrections.json` jako few-shot příklady
|
||||
|
||||
**Formát názvu souboru:**
|
||||
`{RČ} {YYYY-MM-DD} {Příjmení}, {Jméno} [{typ dokumentu}] [{poznámka}].pdf`
|
||||
|
||||
Příklady typů: `LZ chirurgie`, `LZ kardiologie`, `Laboratoř`, `CT břicha`, `kolonoskopie`, `poukaz FT`
|
||||
|
||||
### `jpg_to_pdf.py` — konverze obrázku na PDF
|
||||
```
|
||||
python jpg_to_pdf.py soubor.jpg [vystup.pdf] [rotace_ccw]
|
||||
```
|
||||
- Opravuje EXIF orientaci
|
||||
- Rotace: 0 / 90 / 180 / 270 (CCW)
|
||||
- A4, DPI=150, quality=80, bez okrajů
|
||||
- Používá se i interně z `extract_patient_info.py`
|
||||
|
||||
## Složky
|
||||
|
||||
| Složka | Účel |
|
||||
|---|---|
|
||||
| `ToProcess/` | Sem se házejí nové skeny (PDF, JPG, PNG) |
|
||||
| `Processed/` | Správně pojmenované PDF po schválení |
|
||||
| `U:\Dropbox\Ordinace\Dokumentace_zpracovaná\` | Finální archiv |
|
||||
|
||||
## Konfigurace
|
||||
- API klíč: `U:\Medevio\.env` → `ANTHROPIC_API_KEY`
|
||||
- Medicus: `localhost:c:\medicus 3\data\medicus.fdb` (Firebird, SYSDBA)
|
||||
- Few-shot korekce: `corrections.json`
|
||||
@@ -0,0 +1,577 @@
|
||||
"""
|
||||
Agent pro extrakci a pojmenování naskenovaných PDF lékařských zpráv.
|
||||
- Claude Vision API — bez OCR, správná čeština s diakritikou
|
||||
- Ověření pacienta proti Medicus (KAR), fuzzy matching RČ
|
||||
- Interaktivní schválení / oprava názvu
|
||||
- Few-shot learning z uložených korekcí
|
||||
"""
|
||||
|
||||
import base64
|
||||
import gc
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# Windows: nastav stdout/stderr na UTF-8
|
||||
if sys.platform == "win32":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
import anthropic
|
||||
from pdf2image import convert_from_path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from Knihovny.najdi_dropbox import get_dropbox_root
|
||||
from Knihovny.najdi_medicus import get_medicus_config
|
||||
|
||||
POPPLER_PATH = r"C:/Poppler/Library/bin"
|
||||
CORRECTIONS_FILE = Path(__file__).parent / "corrections.json"
|
||||
_DROPBOX = Path(get_dropbox_root())
|
||||
TO_PROCESS = _DROPBOX / r"Ordinace\Dokumentace_ke_zpracování\Ricoh Fi-8040\KeZpracování"
|
||||
PROCESSED = _DROPBOX / r"Ordinace\Dokumentace_ke_zpracování\Ricoh Fi-8040\Zpracováno"
|
||||
DOKUMENTACE = _DROPBOX / r"Ordinace\Dokumentace_zpracovaná"
|
||||
|
||||
|
||||
# ─── Konfigurace ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _load_env():
|
||||
env_path = Path(__file__).parent.parent / ".env"
|
||||
if env_path.exists():
|
||||
for line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if "=" in line and not line.startswith("#"):
|
||||
k, v = line.split("=", 1)
|
||||
os.environ[k.strip()] = v.strip()
|
||||
|
||||
_load_env()
|
||||
|
||||
|
||||
# ─── Korekce (few-shot příklady) ──────────────────────────────────────────────
|
||||
|
||||
def load_corrections() -> list[dict]:
|
||||
if CORRECTIONS_FILE.exists():
|
||||
return json.loads(CORRECTIONS_FILE.read_text(encoding="utf-8"))
|
||||
return []
|
||||
|
||||
def save_correction(original: str, corrected: str):
|
||||
corrections = load_corrections()
|
||||
for c in corrections:
|
||||
if c["original"] == original and c["corrected"] == corrected:
|
||||
return
|
||||
corrections.append({"original": original, "corrected": corrected})
|
||||
CORRECTIONS_FILE.write_text(
|
||||
json.dumps(corrections, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
print(f" ✓ Korekce uložena ({len(corrections)} celkem)")
|
||||
|
||||
def build_corrections_prompt() -> str:
|
||||
corrections = load_corrections()
|
||||
if not corrections:
|
||||
return ""
|
||||
lines = ["Příklady korekcí z minulých běhů (uč se z nich):"]
|
||||
for c in corrections[-10:]:
|
||||
lines.append(f' - špatně: "{c["original"]}"')
|
||||
lines.append(f' správně: "{c["corrected"]}"')
|
||||
return "\n".join(lines) + "\n\n"
|
||||
|
||||
|
||||
# ─── Kontrola duplicit ───────────────────────────────────────────────────────
|
||||
|
||||
def check_duplicates(rc: str, datum: str) -> list[str]:
|
||||
"""
|
||||
Hledá v Dokumentace_zpracovaná soubory se stejným RČ a datem.
|
||||
Vrátí seznam názvů nalezených souborů.
|
||||
"""
|
||||
if not DOKUMENTACE.exists():
|
||||
return []
|
||||
prefix = f"{rc} {datum}"
|
||||
return [f.name for f in DOKUMENTACE.iterdir() if f.name.startswith(prefix)]
|
||||
|
||||
|
||||
# ─── Medicus ověření ──────────────────────────────────────────────────────────
|
||||
|
||||
def _medicus_connect():
|
||||
try:
|
||||
import fdb
|
||||
cfg = get_medicus_config()
|
||||
return fdb.connect(
|
||||
dsn=cfg.dsn,
|
||||
user="SYSDBA", password="masterkey", charset="win1250"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f" [Medicus] Nepřipojeno: {e}")
|
||||
return None
|
||||
|
||||
def _lookup_by_rc(cur, rc_digits: str) -> dict | None:
|
||||
"""Přesné vyhledání podle RČ (bez lomítka)."""
|
||||
cur.execute(
|
||||
"SELECT IDPAC, PRIJMENI, JMENO, RODCIS FROM KAR "
|
||||
"WHERE REPLACE(RODCIS, '/', '') = ?",
|
||||
(rc_digits,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return {"idpac": row[0], "prijmeni": row[1].strip(), "jmeno": row[2].strip(), "rodcis": row[3].strip()}
|
||||
return None
|
||||
|
||||
def _rc_candidates(rc: str) -> list[str]:
|
||||
"""
|
||||
Generuje kandidáty RČ pro fuzzy matching:
|
||||
- vynechání každé cifry (OCR přečetlo znak navíc)
|
||||
- vložení nuly na každou pozici (OCR přehlédlo nulu v sekvenci 00)
|
||||
- záměna podobně vypadajících číslic na každé pozici
|
||||
Vrátí unikátní seznam kandidátů bez původního RČ.
|
||||
"""
|
||||
similar = {"0": "8", "8": "0", "1": "7", "7": "1", "5": "6", "6": "5", "3": "8"}
|
||||
candidates = set()
|
||||
|
||||
# Vynechání jedné cifry (OCR přečetlo znak navíc)
|
||||
for i in range(len(rc)):
|
||||
candidates.add(rc[:i] + rc[i+1:])
|
||||
|
||||
# Vložení nuly na každou pozici (nejčastější chyba: sekvence 00 přečtena jako 0)
|
||||
for i in range(len(rc) + 1):
|
||||
candidates.add(rc[:i] + "0" + rc[i:])
|
||||
|
||||
# Záměna podobné cifry na každé pozici
|
||||
for i, ch in enumerate(rc):
|
||||
if ch in similar:
|
||||
candidates.add(rc[:i] + similar[ch] + rc[i+1:])
|
||||
|
||||
candidates.discard(rc)
|
||||
candidates = {c for c in candidates if len(c) in (9, 10)}
|
||||
return sorted(candidates)
|
||||
|
||||
def _rc_checksum_ok(rc: str) -> bool:
|
||||
"""Ověří dělitelnost 11 pro 10místná RČ (platí pro narozené po 1.1.1954)."""
|
||||
digits = re.sub(r"\D", "", rc)
|
||||
if len(digits) == 10:
|
||||
return int(digits) % 11 == 0
|
||||
return True # 9místná RČ nemají checksum
|
||||
|
||||
def verify_patient(rc_raw: str) -> dict:
|
||||
"""
|
||||
Ověří pacienta v Medicus.
|
||||
Vrací:
|
||||
status: "ok" | "fuzzy" | "not_found" | "offline"
|
||||
patient: dict nebo None
|
||||
rc_corrected: opravené RČ (pokud fuzzy) nebo None
|
||||
"""
|
||||
rc = re.sub(r"\D", "", rc_raw or "")
|
||||
if not rc:
|
||||
return {"status": "not_found", "patient": None, "rc_corrected": None}
|
||||
|
||||
con = _medicus_connect()
|
||||
if con is None:
|
||||
return {"status": "offline", "patient": None, "rc_corrected": None}
|
||||
|
||||
try:
|
||||
cur = con.cursor()
|
||||
|
||||
# 1. Přesná shoda
|
||||
patient = _lookup_by_rc(cur, rc)
|
||||
if patient:
|
||||
return {"status": "ok", "patient": patient, "rc_corrected": None}
|
||||
|
||||
# 2. Fuzzy matching — zkus kandidáty, preferuj ty s platným checksumem
|
||||
candidates = _rc_candidates(rc)
|
||||
matches = []
|
||||
for cand in candidates:
|
||||
p = _lookup_by_rc(cur, cand)
|
||||
if p:
|
||||
matches.append((cand, p))
|
||||
|
||||
if not matches:
|
||||
return {"status": "not_found", "patient": None, "rc_corrected": None}
|
||||
|
||||
# Seřaď: platný checksum na prvním místě
|
||||
matches.sort(key=lambda x: (0 if _rc_checksum_ok(x[0]) else 1))
|
||||
best_rc, best_patient = matches[0]
|
||||
return {"status": "fuzzy", "patient": best_patient, "rc_corrected": best_rc, "all_matches": matches}
|
||||
|
||||
finally:
|
||||
con.close()
|
||||
|
||||
|
||||
# ─── PDF → obrázek ────────────────────────────────────────────────────────────
|
||||
|
||||
def pdf_to_images(pdf_path: str) -> list:
|
||||
return convert_from_path(pdf_path, poppler_path=POPPLER_PATH, dpi=300)
|
||||
|
||||
def image_to_base64(image) -> str:
|
||||
buf = io.BytesIO()
|
||||
image.save(buf, format="JPEG", quality=95)
|
||||
return base64.standard_b64encode(buf.getvalue()).decode("utf-8")
|
||||
|
||||
|
||||
# ─── Extrakce Claude Vision ───────────────────────────────────────────────────
|
||||
|
||||
def extract_patient_info(pdf_path: str) -> dict:
|
||||
pdf_path = Path(pdf_path)
|
||||
if not pdf_path.exists():
|
||||
raise FileNotFoundError(f"Soubor nenalezen: {pdf_path}")
|
||||
|
||||
print(f"\nNačítám: {pdf_path.name}")
|
||||
suffix = pdf_path.suffix.lower()
|
||||
if suffix in (".jpg", ".jpeg", ".png"):
|
||||
from PIL import Image
|
||||
img = Image.open(pdf_path)
|
||||
image_b64 = image_to_base64(img)
|
||||
img.close()
|
||||
else:
|
||||
images = pdf_to_images(str(pdf_path))
|
||||
image_b64 = image_to_base64(images[0])
|
||||
del images
|
||||
gc.collect()
|
||||
|
||||
prompt = (
|
||||
build_corrections_prompt() +
|
||||
"Toto je naskenovaná lékařská zpráva v češtině. "
|
||||
"Vrať JSON s těmito poli:\n"
|
||||
"- \"jmeno\": celé jméno pacienta (příjmení + jméno + případný titul)\n"
|
||||
"- \"rodne_cislo\": rodné číslo pacienta BEZ lomítka (pouze číslice)\n"
|
||||
"- \"datum_zpravy\": datum zprávy ve formátu YYYY-MM-DD\n"
|
||||
"- \"typ_dokumentu\": typ dokumentu — "
|
||||
"\"LZ {oddělení}\" = ambulantní/lékařská zpráva (např. \"LZ chirurgie\", \"LZ kardiologie\", \"LZ plicní\", \"LZ ORL\"); "
|
||||
"\"PZ {oddělení}\" = propouštěcí zpráva z hospitalizace (např. \"PZ interna\", \"PZ neurologie\"). "
|
||||
"Jiné typy: \"Laboratoř\", \"CT břicha\", \"MRI páteře\", \"kolonoskopie\", "
|
||||
"\"operační protokol oční\", \"poukaz FT\", \"diagnostická mamografie\" atd.\n"
|
||||
"- \"poznamka\": krátká klinická poznámka česky, max 80 znaků. "
|
||||
"DŮLEŽITÉ: pokud zpráva obsahuje sekci \"Závěr:\" nebo \"Závěr vyšetření:\", "
|
||||
"použij VÝHRADNĚ obsah této sekce — je nejdůležitější. "
|
||||
"Teprve pokud závěr chybí, shrň obsah z celé zprávy.\n"
|
||||
"- \"nazev_souboru\": název souboru ve formátu "
|
||||
"\"{rodne_cislo} {datum_zpravy} {Příjmení}, {Jméno} [{typ_dokumentu}] [{poznamka}].pdf\" "
|
||||
"(jméno bez titulu, RČ bez lomítka)\n"
|
||||
"- \"rotace\": o kolik stupňů CCW je třeba otočit obrázek aby byl text čitelně na výšku nebo šířku "
|
||||
"(hodnoty: 0, 90, 180, 270). Pokud je text již správně orientovaný, vrať 0.\n\n"
|
||||
"Pokud pole nenajdeš, použij null. Nepiš nic jiného než JSON."
|
||||
)
|
||||
|
||||
print(" Volám Claude Vision API...")
|
||||
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-6",
|
||||
max_tokens=400,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_b64}},
|
||||
{"type": "text", "text": prompt},
|
||||
],
|
||||
}],
|
||||
)
|
||||
|
||||
usage = response.usage
|
||||
cost_input = usage.input_tokens * 3 / 1_000_000
|
||||
cost_output = usage.output_tokens * 15 / 1_000_000
|
||||
print(f" Tokeny: {usage.input_tokens} in + {usage.output_tokens} out = ${cost_input + cost_output:.4f}")
|
||||
|
||||
raw = response.content[0].text.strip()
|
||||
if raw.startswith("```"):
|
||||
raw = raw.split("```")[1]
|
||||
if raw.startswith("json"):
|
||||
raw = raw[4:]
|
||||
try:
|
||||
return json.loads(raw.strip())
|
||||
except json.JSONDecodeError:
|
||||
print(f" VAROVÁNÍ: nelze parsovat JSON: {raw!r}")
|
||||
return {"nazev_souboru": None, "raw": raw}
|
||||
|
||||
|
||||
# ─── Interaktivní schválení ───────────────────────────────────────────────────
|
||||
|
||||
def sanitize_filename(name: str) -> str:
|
||||
return re.sub(r'[<>:"/\\|?*]', '', name)
|
||||
|
||||
|
||||
def _open_preview(root, pdf_path: Path):
|
||||
"""Otevře náhledové okno PDF/obrázku jako Toplevel. Pracuje s temp kopií — žádné zamykání originálu."""
|
||||
import tkinter as tk
|
||||
import tempfile
|
||||
import shutil as _shutil
|
||||
try:
|
||||
from PIL import Image, ImageTk
|
||||
import fitz
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
# Temp kopie — prohlížeč nikdy nesahá na originál
|
||||
tmp = Path(tempfile.mktemp(suffix=pdf_path.suffix))
|
||||
_shutil.copy2(pdf_path, tmp)
|
||||
|
||||
suffix = pdf_path.suffix.lower()
|
||||
if suffix in (".jpg", ".jpeg", ".png"):
|
||||
pil_pages = [Image.open(tmp)]
|
||||
doc = None
|
||||
else:
|
||||
try:
|
||||
doc = fitz.open(str(tmp))
|
||||
except Exception:
|
||||
tmp.unlink(missing_ok=True)
|
||||
return
|
||||
pil_pages = []
|
||||
|
||||
def render(n) -> Image.Image:
|
||||
if doc is not None:
|
||||
page = doc[n]
|
||||
zoom = min(700 / page.rect.width, (sh - 150) / page.rect.height)
|
||||
pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
|
||||
return Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
|
||||
else:
|
||||
img = pil_pages[0].copy()
|
||||
img.thumbnail((700, sh - 150), Image.LANCZOS)
|
||||
return img
|
||||
|
||||
def on_close():
|
||||
try:
|
||||
if doc:
|
||||
doc.close()
|
||||
except Exception:
|
||||
pass
|
||||
tmp.unlink(missing_ok=True)
|
||||
win.destroy()
|
||||
|
||||
page_count = len(doc) if doc else 1
|
||||
sh = root.winfo_screenheight()
|
||||
current = [0]
|
||||
photo_ref = [None]
|
||||
|
||||
win = tk.Toplevel(root)
|
||||
win.title(pdf_path.name)
|
||||
win.attributes("-topmost", True)
|
||||
win.resizable(False, False)
|
||||
win.protocol("WM_DELETE_WINDOW", on_close)
|
||||
|
||||
lbl_img = tk.Label(win)
|
||||
lbl_img.pack()
|
||||
|
||||
frame_nav = tk.Frame(win)
|
||||
frame_nav.pack(pady=4)
|
||||
|
||||
lbl_page = tk.Label(frame_nav, font=("Segoe UI", 9))
|
||||
lbl_page.pack(side="left", padx=10)
|
||||
|
||||
def show(n):
|
||||
current[0] = n
|
||||
img = render(n)
|
||||
photo_ref[0] = ImageTk.PhotoImage(img)
|
||||
lbl_img.config(image=photo_ref[0])
|
||||
lbl_page.config(text=f"Strana {n + 1} / {page_count}")
|
||||
btn_prev.config(state="normal" if n > 0 else "disabled")
|
||||
btn_next.config(state="normal" if n < page_count - 1 else "disabled")
|
||||
|
||||
btn_prev = tk.Button(frame_nav, text="◄ Předchozí",
|
||||
command=lambda: show(current[0] - 1))
|
||||
btn_prev.pack(side="left")
|
||||
btn_next = tk.Button(frame_nav, text="Další ►",
|
||||
command=lambda: show(current[0] + 1))
|
||||
btn_next.pack(side="left")
|
||||
|
||||
show(0)
|
||||
|
||||
win.update_idletasks()
|
||||
win.geometry(f"+0+0")
|
||||
|
||||
|
||||
def _rename_dialog(nazev: str, info_lines: list[str]) -> str | None:
|
||||
"""
|
||||
Spustí rename_dialog.py jako subprocess — vyhneme se Tkinter konfliktům s PyCharm.
|
||||
Vrátí finální název (s .pdf) nebo None = přeskočit.
|
||||
"""
|
||||
import tempfile
|
||||
|
||||
data = {"nazev": nazev, "info_lines": info_lines}
|
||||
tmp = Path(tempfile.mktemp(suffix=".json"))
|
||||
tmp.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
dialog_script = Path(__file__).parent / "rename_dialog.py"
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
[sys.executable, str(dialog_script), str(tmp)],
|
||||
capture_output=True, text=True, encoding="utf-8",
|
||||
)
|
||||
output = proc.stdout.strip()
|
||||
if output:
|
||||
return json.loads(output).get("value")
|
||||
return None
|
||||
finally:
|
||||
tmp.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def print_verification(verif: dict, rc_from_scan: str):
|
||||
"""Vypíše výsledek ověření proti Medicus."""
|
||||
status = verif["status"]
|
||||
patient = verif.get("patient")
|
||||
|
||||
if status == "ok":
|
||||
print(f" ✓ Medicus: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
elif status == "fuzzy":
|
||||
rc_corr = verif["rc_corrected"]
|
||||
print(f" ⚠ Medicus: RČ ze skenu '{rc_from_scan}' nenalezeno")
|
||||
print(f" → Nalezen podobný pacient: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
print(f" → Pravděpodobná oprava RČ: {rc_from_scan} → {rc_corr} (OCR chyba)")
|
||||
if len(verif.get("all_matches", [])) > 1:
|
||||
print(f" → Další shody: {[m[0] for m in verif['all_matches'][1:]]}")
|
||||
elif status == "not_found":
|
||||
print(f" ✗ Medicus: RČ '{rc_from_scan}' nenalezeno ani při fuzzy hledání")
|
||||
elif status == "offline":
|
||||
print(f" — Medicus: nedostupný (offline), ověření přeskočeno")
|
||||
|
||||
|
||||
def interactive_rename(pdf_path: Path, info: dict, verif: dict) -> bool:
|
||||
"""
|
||||
Otevře tkinter dialog pro schválení / opravu názvu.
|
||||
Schválený soubor přesune do Processed/ a smaže z ToProcess/.
|
||||
"""
|
||||
rc = re.sub(r"\D", "", verif["patient"]["rodcis"] if verif.get("patient") else info.get("rodne_cislo") or "")
|
||||
datum = info.get("datum_zpravy") or ""
|
||||
duplicity = check_duplicates(rc, datum)
|
||||
|
||||
# Oprava RČ při fuzzy matchi
|
||||
nazev = info.get("nazev_souboru")
|
||||
if verif["status"] == "fuzzy" and verif.get("rc_corrected") and nazev:
|
||||
rc_scan = re.sub(r"\D", "", info.get("rodne_cislo") or "")
|
||||
nazev = nazev.replace(rc_scan, verif["rc_corrected"], 1)
|
||||
print(f" → Název aktualizován s opraveným RČ")
|
||||
|
||||
# Sestavení info řádků pro dialog
|
||||
rc_from_scan = re.sub(r"\D", "", info.get("rodne_cislo") or "")
|
||||
status = verif["status"]
|
||||
patient = verif.get("patient")
|
||||
info_lines = []
|
||||
if status == "ok":
|
||||
info_lines.append(f"✓ Medicus: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
elif status == "fuzzy":
|
||||
info_lines.append(f"⚠ RČ ze skenu '{rc_from_scan}' → opraveno na {verif['rc_corrected']}")
|
||||
info_lines.append(f" Pacient: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
elif status == "not_found":
|
||||
info_lines.append(f"✗ RČ '{rc_from_scan}' nenalezeno v Medicus")
|
||||
else:
|
||||
info_lines.append("— Medicus nedostupný (offline)")
|
||||
if duplicity:
|
||||
info_lines.append(f"⚠ DUPLICITA: {', '.join(duplicity)}")
|
||||
|
||||
print()
|
||||
print("─" * 70)
|
||||
if nazev:
|
||||
print(f" Navržený název: {nazev}")
|
||||
print(" Otevírám dialog...")
|
||||
|
||||
odpoved = _rename_dialog(nazev or "", info_lines)
|
||||
|
||||
if odpoved is None:
|
||||
print(" Přeskočeno.")
|
||||
return False
|
||||
|
||||
if not odpoved.endswith(".pdf"):
|
||||
odpoved += ".pdf"
|
||||
final_name = sanitize_filename(odpoved)
|
||||
|
||||
if nazev and nazev != final_name:
|
||||
save_correction(nazev, final_name)
|
||||
|
||||
if not final_name or final_name == ".pdf":
|
||||
print(" Název je prázdný, přeskakuji.")
|
||||
return False
|
||||
|
||||
dest = PROCESSED / final_name
|
||||
if dest.exists():
|
||||
print(f" VAROVÁNÍ: '{final_name}' již existuje v Processed, přeskakuji.")
|
||||
return False
|
||||
|
||||
if pdf_path.suffix.lower() in (".jpg", ".jpeg", ".png"):
|
||||
from jpg_to_pdf import image_to_pdf
|
||||
image_to_pdf(pdf_path, dest, rotate_ccw=info.get("rotace") or 0)
|
||||
else:
|
||||
shutil.copy2(pdf_path, dest)
|
||||
|
||||
pdf_path.unlink()
|
||||
print(f" ✓ Uloženo: Processed/{final_name}")
|
||||
return True
|
||||
|
||||
|
||||
# ─── Hlavní logika ────────────────────────────────────────────────────────────
|
||||
|
||||
def _start_preview_process(pdf_path: Path):
|
||||
"""
|
||||
Otevře náhled PDF jako samostatný subprocess (žádné tkinter threading problémy).
|
||||
Pracuje s temp kopií — originál zůstane volný.
|
||||
Vrátí funkci close() pro ukončení procesu.
|
||||
"""
|
||||
import tempfile
|
||||
import shutil as _shutil
|
||||
|
||||
tmp = Path(tempfile.mktemp(suffix=pdf_path.suffix))
|
||||
_shutil.copy2(pdf_path, tmp)
|
||||
|
||||
viewer = Path(__file__).parent / "preview_viewer.py"
|
||||
proc = subprocess.Popen(
|
||||
[sys.executable, str(viewer), str(tmp), "--delete-on-close"],
|
||||
)
|
||||
|
||||
def close():
|
||||
try:
|
||||
proc.terminate()
|
||||
proc.wait(timeout=3)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
tmp.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return close
|
||||
|
||||
|
||||
def process_file(pdf_path: Path):
|
||||
close_preview = _start_preview_process(pdf_path)
|
||||
try:
|
||||
info = extract_patient_info(str(pdf_path))
|
||||
rc_from_scan = re.sub(r"\D", "", info.get("rodne_cislo") or "")
|
||||
print(f" Ověřuji v Medicus (RČ: {rc_from_scan})...")
|
||||
verif = verify_patient(rc_from_scan)
|
||||
print_verification(verif, rc_from_scan)
|
||||
interactive_rename(pdf_path, info, verif)
|
||||
finally:
|
||||
close_preview()
|
||||
|
||||
def process_folder(folder: Path):
|
||||
pdf_files = sorted(f for f in folder.iterdir()
|
||||
if f.suffix.lower() in (".pdf", ".jpg", ".jpeg", ".png"))
|
||||
if not pdf_files:
|
||||
print(f"Žádná PDF nenalezena v: {folder}")
|
||||
return
|
||||
|
||||
print(f"Nalezeno {len(pdf_files)} PDF soubor(ů).\n")
|
||||
for pdf_file in pdf_files:
|
||||
try:
|
||||
process_file(pdf_file)
|
||||
except Exception as e:
|
||||
print(f" CHYBA: {e}")
|
||||
|
||||
print("\nHotovo.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
target = Path(sys.argv[1])
|
||||
else:
|
||||
target = TO_PROCESS
|
||||
|
||||
PROCESSED.mkdir(exist_ok=True)
|
||||
TO_PROCESS.mkdir(exist_ok=True)
|
||||
|
||||
if target.is_file() and target.suffix.lower() in (".pdf", ".jpg", ".jpeg", ".png"):
|
||||
process_file(target)
|
||||
elif target.is_dir():
|
||||
process_folder(target)
|
||||
else:
|
||||
print("Použití: python extract_patient_info.py [soubor.pdf nebo složka]")
|
||||
sys.exit(1)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,538 @@
|
||||
[
|
||||
{
|
||||
"original": "505228025 2026-05-14 Titlbachová, Božena [Žádanka předoperační vyšetření GYNA] [Předop. vyšetření, dg. N890, malý výkon A, anestezie CA].pdf",
|
||||
"corrected": "505228025 2026-05-14 Titlbachová, Božena [žádanka předoperační vyšetření] [gynekologie, dg. N890, malý výkon A, anestezie CA].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6860241553 2026-02-12 Šímová, Helena [LZ neurologie] [VAS L páteře, iritačně zánikový radik sy L5/S1 vpravo, dg. M511].pdf",
|
||||
"corrected": "6860241553 2026-02-12 Šímová, Helena [LZ neurologie] [VAS L páteře, po PRT pod CT, krásné zlepšení, iritačně zánikový radik sy L5/S1 vpravo, dg. M511].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6860241553 2026-02-10 Šímová, Helena [denzitometrie] [osteopenie, L1-4 T-score -1,4, krček fem. l T-1,8, r T-2,3].pdf",
|
||||
"corrected": "6860241553 2026-02-10 Šímová, Helena [DXA] [osteopenie, L1-4 T-score -1.4, krček fem. l T-1.8, r T-2.3].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470629074 2026-03-31 Šebesta, Jaroslav [LZ kardiologie] [ECHO: EF 50%, hypokineza IVS a sp. stěny, dilatace LS, MR 1-2/4].pdf",
|
||||
"corrected": "470629074 2026-03-31 Šebesta, Jaroslav [LZ kardiologie] [ECHO: EF 50%, hypokineza IVS a sp. stěny, dilatace LS, MR 1-2/4, indikace lázně II_3].pdf"
|
||||
},
|
||||
{
|
||||
"original": "505809020 2026-01-14 Šebestová, Zdenka [LZ ortopedie] [TEP kyčle l.sin., kontrola 6 týdnů, chůze 2FH, doporučení lázně].pdf",
|
||||
"corrected": "505809020 2026-01-14 Šebestová, Zdenka [LZ ortopedie] [TEP kyčle l.sin., kontrola 6 týdnů, chůze 2FH, indikace lázně VII_10].pdf"
|
||||
},
|
||||
{
|
||||
"original": "505809020 2025-12-10 Šebestová, Zdenka [LZ ortopedie] [Fct. colli femor. l.sin., TEP kyčle l.sin., propuštění na RHB].pdf",
|
||||
"corrected": "505809020 2025-12-10 Šebestová, Zdenka [PZ ortopedie] [29NOV-10DEC2025 Fct. colli femor. l.sin., TEP kyčle l.sin., propuštění na RHB].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7308100426 2026-04-15 Janda, Martin [Laboratoř] [hypercholesterolemie, S_Urea 9,18, glukóza 5,9, moč erytrocyty 6/ul].pdf",
|
||||
"corrected": "7308100426 2026-04-15 Janda, Martin [Laboratoř] [Z000 hypercholesterolemie, S_Urea 9,18, glukóza 5,9, moč erytrocyty 6ul].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7454230454 2026-04-15 Zíková, Jana [Laboratoř] [moč kultivace negativní, dg. N309].pdf",
|
||||
"corrected": "7454230454 2026-04-15 Zíková, Jana [Laboratoř] [N309 moč kultivace negativní].pdf"
|
||||
},
|
||||
{
|
||||
"original": "0460142969 2026-04-15 Strnadová, Natálie [Laboratoř] [E660 koagulace: PT ratio 1,09, INR 1,10, aPTT 1,11, Fibrinogen 3,01].pdf",
|
||||
"corrected": "0460142969 2026-04-15 Strnadová, Natálie [Laboratoř] [E660 koagulace PT ratio 1.09, INR 1.10, aPTT 1.11, Fibrinogen 3.01, parametry koagulace normální].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5855280013 2026-04-15 Holubová, Daniela [Laboratoř] [Z000 CKD G2, DM, P_Glukóza 6.8, HbA1c 44, TG 1.77, leukocyty 11.9, moč bakterie].pdf",
|
||||
"corrected": "5855280013 2026-04-15 Holubová, Daniela [Laboratoř] [Z000 CKD G2, DM (prediabetes), P_Glukóza 6.8, HbA1c 44, TG 1.77, leukocyty 11.9, moč bakterie záplava].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2026-04-15 Holečková, Hana [Laboratoř] [E119 CKD G2, P_Glukóza 6.7, HbA1c 44, bili 28.13, moč hlen].pdf",
|
||||
"corrected": "5954110184 2026-04-15 Holečková, Hana [Laboratoř] [E119 CKD G2, P_Glukóza 6.7 (prediabetes), HbA1c 44, bili 28.13, moč hlen].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7556220452 2026-04-09 Štěpánová, Lenka [Laboratoř] [K20 Anti HAV IgM+total pozit, CKD G2, bili konj 5.58].pdf",
|
||||
"corrected": "7556220452 2026-04-09 Štěpánová, Lenka [Laboratoř] [K20 Anti HAV IgM+, total pozit, postoèkovací protilátky, CKD G2, bili konj 5.58].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5862236435 2026-04-14 Kopřivová, Erika [Laboratoř] [Z000 CKD G2, hypercholesterol, TG 2.16, HbA1c 50, ALT 0.80, moč leukocyty 75ul].pdf",
|
||||
"corrected": "5862236435 2026-04-14 Kopřivová, Erika [Laboratoř] [Z000 CKD G2, smíšená hypercholesterolémie, TG 2.16, HbA1c 50 (prediabetes), ALT 0.80, moč leukocyty 75ul].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6258130637 2026-04-14 Hofmannová, Oldřiška [Laboratoř] [D500 CKD G2, cholesterol 6.38, LDL 4.37, glukóza 7.1, moč bakterie záplava, nitrity poz].pdf",
|
||||
"corrected": "6258130637 2026-04-14 Hofmannová, Oldřiška [Laboratoř] [D500 CKD G2, èistá hypercholesterolémie 6.38, LDL 4.37, glukóza 7.1 (DM), moč bakterie záplava, nitrity+].pdf"
|
||||
},
|
||||
{
|
||||
"original": "8256060021 2026-04-10 Karešová, Barbora [Laboratoř] [Z000 hypercholesterol 6.54, LDL 3.73, ALT 1.00, CKD G2, moč erytrocyty 11ul].pdf",
|
||||
"corrected": "8256060021 2026-04-10 Karešová, Barbora [Laboratoř] [Z000 čistá hypercholesterolémie 6.54, LDL 3.73, ALT 1.00, CKD G2, moč erytrocyty 11ul].pdf"
|
||||
},
|
||||
{
|
||||
"original": "400424003 2026-04-07 Faměra, Jiří [Laboratoř] [I10 CKD G3b, anemie, S_Urea 16.45, kreatinin 155, glukóza 5.7].pdf",
|
||||
"corrected": "400424003 2026-04-07 Faměra, Jiří [Laboratoř] [I10 CKD G3b, anemie 116, S_Urea 16.45, kreatinin 155, glukóza 5.7].pdf"
|
||||
},
|
||||
{
|
||||
"original": "0460142969 2026-04-08 Strnadová, Natálie [Laboratoř] [E660 S_Urea 2.35, AST 0.18, CKD G1, krevní obraz v normě].pdf",
|
||||
"corrected": "0460142969 2026-04-08 Strnadová, Natálie [Laboratoř] [E660 krevní obraz v normě].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6055052157 2026-04-08 Frýdlová, Jana [Laboratoř] [Z000 CKD G2, prediabetes HbA1c 43, GGT 6.48, cholesterol 5.69, moč leukocyty 42ul].pdf",
|
||||
"corrected": "6055052157 2026-04-08 Frýdlová, Jana [Laboratoř] [Z000 CKD G2, prediabetes HbA1c 43, GGT 6.48 (susp. alkohol), čistá hypercholesterolémie 5.69, moč leukocyty 42ul].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7160239911 2026-04-01 Čenanovičová Krkičov, Sanja [Laboratoř] [E789 čistá hypercholesterolémie 6.68, LDL 4.76, Non-HDL 5.0].pdf",
|
||||
"corrected": "7160239911 2026-04-01 Čenanovičová Krkičov, Sanja [Laboratoř] [E789 čistá hypercholesterolémie 6.68, LDL 4.76, Non-HDL 5.0, CK v pořádku].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6212231861 2026-04-01 Novotný, Vladimír [Laboratoř] [D50 sideropenní anemie 105, Fe 5.9, TIBC 88.2, trombocytóza 609, mikrocytóza].pdf",
|
||||
"corrected": "6212231861 2026-04-01 Novotný, Vladimír [Laboratoř] [D50 sideropenická anémie 105, Fe 5.9, TIBC 88.2, trombocytóza 609 (známý stav), mikrocytóza].pdf"
|
||||
},
|
||||
{
|
||||
"original": "466103013 2026-04-01 Sixtová, Blanka [Laboratoř] [C188 CKD G3a, urea 12.32, kyselina močová 425, GGT 1.41, trombocytopenie 138].pdf",
|
||||
"corrected": "466103013 2026-04-01 Sixtová, Blanka [Laboratoř] [C188 CKD G3a, urea 12.32, kyselina močová 425, GGT 1.41, trombocytopenie 138, železa je stále nedostatek].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5601090550 2026-02-25 Psohlavec, Miroslav [Laboratoř] [D53 B12 134 (nízký), folát 7.90, albumin 41.3, celk. bílk. 78].pdf",
|
||||
"corrected": "5601090550 2026-02-25 Psohlavec, Miroslav [Laboratoř] [D53 B12 134 (nízký), folát 7.90 (nízký), albumin 41.3 OK, celk. bílk. 78 OK].pdf"
|
||||
},
|
||||
{
|
||||
"original": "0061010422 2026-04-10 Brabcová, Barbora [Laboratoř] [Z000 foláty 9.4 (nízké), CKD G2, TSH 2.030, krevní obraz v normě].pdf",
|
||||
"corrected": "0061010422 2026-04-10 Brabcová, Barbora [Laboratoř] [Z000 foláty 9.4 (nízké), B12 nízké, CKD G2, TSH 2.030, krevní obraz v normě].pdf"
|
||||
},
|
||||
{
|
||||
"original": "486020212 2025-03-27 Krausová, Anna [EGD] [K30 biliární duodenogastrický reflux, jinak přiměřený nález].pdf",
|
||||
"corrected": "486020212 2025-03-27 Krausová, Anna [gastroskopie] [K30 biliární duodenogastrický reflux, jinak přiměřený nález].pdf"
|
||||
},
|
||||
{
|
||||
"original": "466225409 2026-03-09 Teršová, Eva [Laboratoř] [E118 CKD G3b, glukóza 10.5, HbA1c 34, C-peptid nízký, LDL 3.32, TG 1.52].pdf",
|
||||
"corrected": "466225409 2026-03-09 Teršová, Eva [Laboratoř] [E118 CKD G3b, glukóza 10.5 (DM), HbA1c 34, C-peptid nízký, LDL 3.32, TG 1.52].pdf"
|
||||
},
|
||||
{
|
||||
"original": "476027162 2026-02-25 Buňková, Zuzana [LZ endokrinologie] [E063 kompenz. imunogenní hypothyreoza, uzlová přestavba, TSH 0.818].pdf",
|
||||
"corrected": "476027162 2026-02-25 Buňková, Zuzana [LZ endokrinologie] [E063 kompenz. imunogenní hypothyreoza, uzlová přestavba, TSH 0.818, na substituci].pdf"
|
||||
},
|
||||
{
|
||||
"original": "385312025 2026-03-30 Aubrechtová, Iva [medikace] [Furon, Eliquis, Cordarone, Digoxin, Tezeo, Dilatrend, Betaserc, Xalacom].pdf",
|
||||
"corrected": "385312025 2026-03-30 Aubrechtová, Iva [přehled užívané medikace] [od pacientky].pdf"
|
||||
},
|
||||
{
|
||||
"original": "480529193 2026-01-22 Klikorka, Václav [LZ kardiologie] [I482, EF LK 60%, konc. hypertrofie LK, diastol. dysfunkce I, Mi regurg. 2+].pdf",
|
||||
"corrected": "480529193 2026-01-22 Klikorka, Václav [LZ kardiologie] [I482, EF LK 60%, konc. hypertrofie LK, diastol. dysfunkce I, Mi regurg. 2+, kontrola +6m].pdf"
|
||||
},
|
||||
{
|
||||
"original": "480529193 2025-09-04 Klikorka, Václav [LZ oční] [Z961 pseudofakie, ERM ok. dx., kontrola OCTA za 4M].pdf",
|
||||
"corrected": "480529193 2025-09-04 Klikorka, Václav [LZ oční] [Z961 pseudofakie, ERM ok. dx., kontrola OCTA za 4m].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6258130637 2026-02-23 Hofmannová, Oldřiška [LZ interní] [Z039 atypická bolest na hrudi, epigastralgie, vertigo, BNH].pdf",
|
||||
"corrected": "6258130637 2026-02-23 Hofmannová, Oldřiška [LZ interní] [Z039 atypická bolest na hrudi, přivezla RZS, epigastralgie, vertigo, bez známek ICHS].pdf"
|
||||
},
|
||||
{
|
||||
"original": "436212054 2026-04-01 Těšitelová, Jana [LZ rehabilitační] [M159 st.p. TEP gen l.dx., TEP coxae l.sin., amputace PDK, inkontinence II.st.].pdf",
|
||||
"corrected": "436212054 2026-04-01 Těšitelová, Jana [PZ Lázně Velichovky] [11MAR-01APR2026, indikace VII_8, M159 st.p. TEP gen l.dx., TEP coxae l.sin., amputace PDK, inkontinence II.st., všechno v lázních OK].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6104260668 2026-04-09 Neuwirth, Richard [LZ neurologie] [R42 vertigo, etanol 1.19 g/l, CT mozku bez ak. změn, kongenit. nystagmus].pdf",
|
||||
"corrected": "6104260668 2026-04-09 Neuwirth, Richard [LZ neurologie] [RZS, R42 vertigo, etanol 1.19 gl, CT mozku bez ak. změn, kongenit. nystagmus].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2015-02-10 Holečková, Hana [LZ radiační onkologie] [C50 l.dx., st.p. ablaci 2004, kompletní remise, MMG norm.].pdf",
|
||||
"corrected": "5954110184 2015-02-10 Holečková, Hana [LZ radiační onkologie] [vyšetření pro posudkovou komisi, C50 l.dx., st.p. ablaci 2004, kompletní remise, MMG norm.].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2011-11-03 Holečková, Hana [LZ chirurgie] [K800 cholecystolithiasis, cholecystektomie laparoskopicky 31.10.2011].pdf",
|
||||
"corrected": "5954110184 2011-11-03 Holečková, Hana [PZ chirurgie] [30OCT-03NOV2025, K800 cholecystolithiasis, cholecystektomie laparoskopicky 31.10.2011].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2023-09-12 Holečková, Hana [LZ endokrinologie] [eutyreoza, malá štítnice, stacion. uzel v PL benigní, BMI 26.7].pdf",
|
||||
"corrected": "5954110184 2023-09-12 Holečková, Hana [LZ endokrinologie] [eutyreoza, malá štítnice, stacion. uzel v PL benigní, BMI 26.7, kontrola +1r].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2023-08-28 Holečková, Hana [LZ rehabilitační] [M7737 ostruha patní kosti, pes planus, ultrazvuk 10x].pdf",
|
||||
"corrected": "5954110184 2023-08-28 Holečková, Hana [LZ rehabilitace] [M7737 ostruha patní kosti, pes planus, ultrazvuk 10x].pdf"
|
||||
},
|
||||
{
|
||||
"original": "285703963 2026-04-08 Bartáková, Hilde [Laboratoř] [biochemie, KO, Fe: mírná anémie, kreatinin↑, CKD G3b, B12↑].pdf",
|
||||
"corrected": "285703963 2026-04-08 Bartáková, Hilde [Laboratoř] [biochemie, KO, Fe mírná anémie, kreatinin↑, CKD G3b, B12↑].pdf"
|
||||
},
|
||||
{
|
||||
"original": "495831175 2026-04-07 Kazdová, Daniela [Laboratoř] [biochemie, hepatitidy: ALT↑, GGT↑, ALP↑, anti HAV total pozitivní].pdf",
|
||||
"corrected": "495831175 2026-04-07 Kazdová, Daniela [Laboratoř] [biochemie, hepatitidy ALT↑, GGT↑, ALP↑, anti HAV total pozitivní, antiHAV IgM negativní, anamnestické protilátky].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2024-04-23 Holečková, Hana [LZ radiační onkologie] [C50 l.dx., st.p. ablaci 2004, CR 20 let, MMG benigní, předání PL].pdf",
|
||||
"corrected": "5954110184 2024-04-23 Holečková, Hana [LZ radiační onkologie] [C50 l.dx., st.p. ablaci 2004, CR 20 let, MMG benigní, předání PL, konec jejich dispenzarizace].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6104260668 2026-04-08 Neuwirth, Richard [Laboratoř] [biochemie, moč: cholesterol↑, TG↑, HDL↓, glukóza↑, CKD G1 A1].pdf",
|
||||
"corrected": "6104260668 2026-04-08 Neuwirth, Richard [Laboratoř] [biochemie, moč cholesterol↑, TG↑, HDL↓, glukóza↑, CKD G1 A1].pdf"
|
||||
},
|
||||
{
|
||||
"original": "475915002 2026-04-13 Protivová, Lidmila [Laboratoř] [biochemie, moč, KO cholesterol↑, TG↑, glukóza↑, CKD G2, leukocyty↓].pdf",
|
||||
"corrected": "475915002 2026-04-13 Protivová, Lidmila [Laboratoř] [biochemie, moč, KO smíšená hyperlipidémie, cholesterol↑, TG↑, glukóza↑ (prediabetes), CKD G2, leukocyty↓].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2024-03-25 Holečková, Hana [EKG vyšetření] [sinusový rytmus 76/min, intermed poloha, fyziologický záznam].pdf",
|
||||
"corrected": "5954110184 2024-03-25 Holečková, Hana [EKG] [sinusový rytmus 76min, intermed poloha, fyziologický záznam].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5954110184 2020-03-10 Holečková, Hana [EKG] [sinusový rytmus 64/min, intermed poloha, fyziologický záznam].pdf",
|
||||
"corrected": "5954110184 2020-03-10 Holečková, Hana [EKG] [sinusový rytmus 64min, intermed poloha, fyziologický záznam].pdf"
|
||||
},
|
||||
{
|
||||
"original": "null 2026-04-20 null [Laboratoř] [moč LEU+2 125 WBC/uL, ERY+3 200 RBC/uL, pH 6, SG 1.005].pdf",
|
||||
"corrected": "475915054 Žabová, Vìra 2026-04-20 [uritex] [moč LEU+2 125 WBCuL, ERY+3 200 RBCuL, pH 6, SG 1.005].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7409240399 2026-04-17 Bukvář, Martin [LZ ortopedie] [M7126 cystis politelais gensu, st.p. achilodyniam, punkce 14ml, Diop+Kort].pdf",
|
||||
"corrected": "7409240399 2026-04-17 Bukvář, Martin [LZ ortopedie] [M7126 cystis politelais genu, st.p. achilodyniam, punkce 14ml, aplikace kortikoidu].pdf"
|
||||
},
|
||||
{
|
||||
"original": "515705039 2026-04-01 Cahová, Daniela [LZ neurologie] [G20 Parkinson, klidový třes LHK+LDK, wearing off, bolest pravého ramene, m.deltoideus].pdf",
|
||||
"corrected": "515705039 2026-04-01 Cahová, Daniela [LZ neurologie] [G20 Parkinson, klidový třes LHK+LDK, wearing off, bolest pravého ramene, m.deltoideus, ad MRI].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7862150351 2018-10-30 Braunspergerová, Eva [RTG LS páteře] [dextrokonvexní skolióza, lordóza, osteochondróza L5-S1, spina bifida oculta S1].pdf",
|
||||
"corrected": "7862150351 2018-10-30 Braunspergerová, Eva [RTG LS páteře] [dextrokonvexní skolióza, lordóza, osteochondróza L5-S1, spina bifida oculta S1, pooperaèní svorky].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7862150351 2008-09-23 Braunspergerová, Eva [PZ infekční] [hepatitida A, ikterus, BLR↑, HAV IgM poz., anti HCV neg., zlepšení].pdf",
|
||||
"corrected": "7862150351 2008-09-23 Braunspergerová, Eva [PZ infekce] [19-23SEP2008, hepatitida A, ikterus, BLR↑, HAV IgM poz., anti HCV neg., zlepšení].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5855280013 2023-06-12 Holubová, Daniela [LZ endokrinologie] [adenom l.nadledviny 12x17x12mm, DM2 PAD+inzulin, dyslipidémie].pdf",
|
||||
"corrected": "5855280013 2023-06-12 Holubová, Daniela [LZ endokrinologie] [adenom l.nadledviny 12x17x12mm, DM2 PAD+inzulin, dyslipidémie, konzultace s výsledky].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5855280013 2026-03-03 Holubová, Daniela [CT břicha] [kalykolitiáza l.ledviny, stacionární adenom l.nadledviny 10mm].pdf",
|
||||
"corrected": "5855280013 2026-02-27 Holubová, Daniela [CT břicha] [kalykolitiáza l.ledviny, stacionární adenom l.nadledviny 10mm, vyšetøeno pro hematurii].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7862150351 2024-06-12 Braunspergerová, Eva [EKG] [sinusový rytmus 60min, hraniční i.v. vedení, bez čerstvých změn].pdf",
|
||||
"corrected": "7862150351 2024-06-12 Braunspergerová, Eva [EKG] [sinusový rytmus 60min, hraniční i.v. vedení, bez čerstvých změn, stacionární].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7755260271 2026-04-16 Straková, Barbara [žádost o předání zdravotních informací] [převzetí do péče, žádost o zaslání zdravotní dokumentace].pdf",
|
||||
"corrected": "7755260271 2026-04-16 Straková, Barbara [žádost o předání zdravotních informací] [převzetí do péče, žádost o zaslání zdravotní dokumentace, VeleòMedic s.r.o.].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7602044780 2026-04-18 Suchý, Vladimír [PZ nefrologie] [SLE+APS, renální biopsie, 2.puls Endoxan, VRE/ESBL, defekt PDK].pdf",
|
||||
"corrected": "7602044780 2026-04-18 Suchý, Vladimír [PZ nefrologie] [14-18APR2026, SLE+APS, renální biopsie, 2.puls Endoxan, VREESBL, defekt PDK].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6709150613 2026-04-15 Rutrle, Petr [LZ ORL] [laryngitis chr., vestibul.sy vpravo, percepční porucha sluchu bil.].pdf",
|
||||
"corrected": "6709150613 2026-04-15 Rutrle, Petr [LZ ORL] [laryngitis chr., vestibul.sy vpravo, percepční porucha sluchu bil., ad sono karotid, Rp Helicid, zakoupí Tanakan].pdf"
|
||||
},
|
||||
{
|
||||
"original": "460614110 null Galus, Karel [LZ nefrologie] [CHOPN III.st, DM2, ICHS, CKD-EPI 35ml/s/kor, incipientní dia nefropatie].pdf",
|
||||
"corrected": "460614110 2026-04-09 Galus, Karel [LZ nefrologie] [CHOPN III.st, DM2, ICHS, CKD-EPI 35mlskor, incipientní dia nefropatie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "435624102 2026-03-31 Hovorková, Eva [PZ ortopedie] [26-31MAR2026, gonartróza l.dx., TEP kolene, Zimmer Nexgen CR F/6/10].pdf",
|
||||
"corrected": "435624102 2026-03-31 Hovorková, Eva [PZ ortopedie] [26-31MAR2026, gonartróza l.dx., TEP kolene].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470629074 2026-04-21 Šebesta, Jaroslav [oznámení ZP správní řízení] [zahájení správního řízení, LRPéče indikace II/3 hypertenzní choroba II-III.st].pdf",
|
||||
"corrected": "470629074 2026-04-21 Šebesta, Jaroslav [oznámení ZP správní řízení] [zahájení správního řízení, návrh lázně, indikace II3 hypertenzní choroba II-III.st].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5503040026 2026-02-17 Koubek, Jiří [LZ kardiologie] [ECHO: EF 65%, konc.hypertrofie, diastol.dysfunkce I.st, Bevimlar 20mg].pdf",
|
||||
"corrected": "5503040026 2026-02-17 Koubek, Jiří [LZ kardiologie] [ECHO EF 65%, konc.hypertrofie, diastol.dysfunkce I.st, Bevimlar 20mg].pdf"
|
||||
},
|
||||
{
|
||||
"original": "480529219 2026-04-17 Nytra, Vlastimil [Laboratoř] [osteomarkery, Ca, P, ALP, vit.D 67,1 snížen, PTH, Beta-CrossLaps].pdf",
|
||||
"corrected": "480529219 2026-04-17 Nytra, Vlastimil [Laboratoř] [osteomarkery, Ca, P, ALP, vit.D 67.1 snížen, PTH, Beta-CrossLaps].pdf"
|
||||
},
|
||||
{
|
||||
"original": "435520110 2026-04-20 Nechodomová, Marie [sonografie břicha] [hypersekr.žaludku, lipomatoza pankreatu, steatoza jat., cholecystolithiaza, splenomegalie].pdf",
|
||||
"corrected": "435520110 2026-04-20 Nechodomová, Marie [sonografie břicha] [zesílení stěny žaludku - dovyšetřit, hypersekr.žaludku, lipomatoza pankreatu, steatoza jat., cholecystolithiaza, splenomegalie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6903020080 2026-04-20 Novotný, Martin [Laboratoř] [cholesterol 5.54, LDL 3.25, TG 2.06, glukoza 6.1, HbA1c 38].pdf",
|
||||
"corrected": "6903020080 2026-04-20 Novotný, Martin [Laboratoř] [smíšená hyperlipidémie, prediabetes, cholesterol 5.54, LDL 3.25, TG 2.06, glukoza 6.1, HbA1c 38].pdf"
|
||||
},
|
||||
{
|
||||
"original": "480529219 2026-04-17 Nytra, Vlastimil [Laboratoř] [ELFO bílkovin, bílkovina 69.0, albumin 0.581, gama-globuliny 0.125].pdf",
|
||||
"corrected": "480529219 2026-04-17 Nytra, Vlastimil [Laboratoř] [ELFO bílkovin OK, bílkovina 69.0, albumin 0.581, gama-globuliny 0.125].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5556046672 2026-04-07 Simionová, Lýdia [Laboratoř] [močový konkrement, whewellit 100%, 6x3mm, hnědý, bradavičnatý].pdf",
|
||||
"corrected": "5556046672 2026-04-07 Simionová, Lýdia [Laboratoř] [močový konkrement analýza, whewellit 100%, 6x3mm, hnědý, bradavičnatý].pdf"
|
||||
},
|
||||
{
|
||||
"original": "510802325 2026-04-20 Simion, Vladimír [LZ chirurgie] [chronický vřed kůže, TMT amputace IV.+V.prstu PDK, defekt LDK 5x3.5cm].pdf",
|
||||
"corrected": "510802325 2026-04-20 Simion, Vladimír [LZ chirurgie] [chronický vřed kůže, TMT amputace IV.+V.prstu PDK, defekt LDK 5x3.5cm, DP 3xt].pdf"
|
||||
},
|
||||
{
|
||||
"original": "436114002 2026-03-17 Petrovská, Eliška [LZ kardiologie] [fibrilace síní paroxysmální, sinus, st.p.kardioverzi, rivaroxaban].pdf",
|
||||
"corrected": "436114002 2026-03-17 Petrovská, Eliška [LZ kardiologie] [fibrilace síní paroxysmální, sinus, st.p.kardioverzi, rivaroxaban, ad Holter EKG, bisoprolol vysadí].pdf"
|
||||
},
|
||||
{
|
||||
"original": "436114002 2026-03-14 Petrovská, Eliška [LZ interna] [fibrilace síní paroxysmální, kardioverze, sinusový rytmus, rivaroxaban].pdf",
|
||||
"corrected": "436114002 2026-03-14 Petrovská, Eliška [LZ interna urgent] [fibrilace síní paroxysmální, kardioverze, sinusový rytmus, rivaroxaban].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2026-04-20 Nikitin, Petro [Laboratoř] [urea 9.47 zvýš, CKD-EPI G2, glukoza 6.6, osmolalita 296, MCV 81.5].pdf",
|
||||
"corrected": "6008091738 2026-04-20 Nikitin, Petro [Laboratoř] [Z000 prediabetes, mikrocyty, urea 9.47 zvýš, CKD-EPI G2, glukoza 6.6, osmolalita 296, MCV 81.5].pdf"
|
||||
},
|
||||
{
|
||||
"original": "440802018 2026-04-20 Havelka, Miroslav [Laboratoř] [CKD-EPI G2, NT-proBNP 6128 zvýš, CRP 6.6, MCV 81.8, MCHC 314].pdf",
|
||||
"corrected": "440802018 2026-04-20 Havelka, Miroslav [Laboratoř] [srdeční selhání, mikrocyty, CKD-EPI G2, NT-proBNP 6128 zvýš, CRP 6.6, MCV 81.8, MCHC 314].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7857260422 2023-02-28 Jindrová, Kateřina [LZ ORL] [st.p. incizi inflam aterom P tváře - zhojeno, extirpace atheromu P tváře].pdf",
|
||||
"corrected": "7857260422 2023-02-28 Jindrová, Kateřina [LZ ORL] [st.p. incizi inflam aterom P tváře - zhojeno, extirpace atheromu P tváře domluveno].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7857260422 2021-05-06 Jindrová, Kateřina [LZ angiologie] [CVI II. st. dle CEAP C4, ortostáza, flebitida/flebotrombóza bilat. neprokázána].pdf",
|
||||
"corrected": "7857260422 2021-05-06 Jindrová, Kateřina [LZ angiologie] [CVI II. st. dle CEAP C4, ortostáza, flebitidaflebotrombóza bilat. neprokázána].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7857260422 2021-05-20 Jindrová, Kateřina [LZ neurologie] [VAS C-pá, porucha statodynamiky C úseku, tinitus auric. bilat., ad rehab].pdf",
|
||||
"corrected": "7857260422 2021-05-20 Jindrová, Kateřina [LZ neurologie] [VAS Cp, porucha statodynamiky C úseku, tinitus auric. bilat., ad RHB].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7857260422 2024-02-12 Jindrová, Kateřina [EKG] [sinusový rytmus 70/min, semivertik poloha, osa 55st, fyziol záznam].pdf",
|
||||
"corrected": "7857260422 2024-02-12 Jindrová, Kateřina [EKG] [sinusový rytmus 70min, semivertik poloha, osa 55st, fyziol záznam].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5958260660 2026-02-04 Masopustová, Ivana [LZ interna] [viroza 1/25, RTH, CRP norm, klacid, únava, kašel, dušnost, zlepšuje se].pdf",
|
||||
"corrected": "5958260660 2026-02-04 Masopustová, Ivana [LZ interna] [viroza 125, RTH, CRP norm, klacid, únava, kašel, dušnost, zlepšuje se].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5958260660 2026-03-03 Masopustová, Ivana [LZ kardiologie] [Benig. kom. extrasystolie, Art. hypertenze komp., HLP, thyreopathie disp. + OA].pdf",
|
||||
"corrected": "5958260660 2026-03-03 Masopustová, Ivana [LZ kardiologie] [Benig. kom. extrasystolie, Art. hypertenze komp., HLP, thyreopathie disp. + OA, důvodu nerozumím].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7857260422 2026-04-21 Jindrová, Kateřina [Laboratoř] [Z000, erytrocyty 5.27 zvýš, hemoglobin 161 zvýš, hematokrit 0.475 zvýš].pdf",
|
||||
"corrected": "7857260422 2026-04-21 Jindrová, Kateřina [Laboratoř] [Z000, erytrocyty 5.27 zvýš, hemoglobin 161 zvýš, hematokrit 0.475 zvýš, nic zvláštního].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7555270085 2026-04-20 Křížová, Lucie [Laboratoř] [CKD-EPI G2, hemoglobin 113 sníž, MCV 78 sníž, MCH 23.9 sníž, MCHC 307 sníž].pdf",
|
||||
"corrected": "7555270085 2026-04-20 Křížová, Lucie [Laboratoř] [E119, CKD-EPI G2, hemoglobin 113 sníž, MCV 78 sníž, MCH 23.9 sníž, MCHC 307 sníž, sideropenická anémie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "505218025 2026-04-14 Beznosková, Milena [LZ diabetologie] [DM 2.typu, zlepšení kompenzace, HbA1c 49 mmol/mol, léčba PAD a dieta].pdf",
|
||||
"corrected": "505218025 2026-04-14 Beznosková, Milena [LZ diabetologie] [DM 2.typu, zlepšení kompenzace, HbA1c 49 mmolmol, léčba PAD a dieta].pdf"
|
||||
},
|
||||
{
|
||||
"original": "495524246 2026-03-30 Dusilová, Jana [LZ urologie] [RCC pT1G1 st.p. NE I26, restaging: bez obtíží, nál. přiměř., CT s kontrastem plán].pdf",
|
||||
"corrected": "495524246 2026-03-30 Dusilová, Jana [LZ urologie] [RCC pT1G1 st.p. NE I26, restaging bez obtíží, nál. přiměř., CT s kontrastem plán].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5452020420 2026-04-20 Uhlířová, Jana [RTG LS páteře] [anterolistéza L5 11mm gr.2, snížení disků L4-S1, spondylofyty L2-S1, spondyloartróza bilat.].pdf",
|
||||
"corrected": "5452020420 2026-04-20 Uhlířová, Jana [RTG LSp] [revize nálezu, anterolistéza L5 11mm gr.2, snížení disků L4-S1, spondylofyty L2-S1, spondyloartróza bilat.].pdf"
|
||||
},
|
||||
{
|
||||
"original": "9451210054 2026-04-21 Bódisová, Barbara [LZ interna] [bolest Thp po zvedání břemene, DDimer slabě poz., EKG ektop. síňový rytmus].pdf",
|
||||
"corrected": "9451210054 2026-04-21 Bódisová, Barbara [LZ interna] [bolest Thp po zvedání břemene, DDimer slabě poz., EKG ektop. síňový rytmus, není kardiologické].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6761150341 2026-04-21 Písaříková, Helena [Laboratoř] [Z000, CKD-EPI G2, cholesterol 5.76 zvýš, Non-HDL 3.9 zvýš, glukóza 5.7 zvýš].pdf",
|
||||
"corrected": "6761150341 2026-04-21 Písaříková, Helena [Laboratoř] [Z000, CKD-EPI G2, cholesterol 5.76 zvýš, Non-HDL 3.9 zvýš, glukóza 5.7 zvýš, prediabetes, hypercholesterolémie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5558270113 2026-03-03 Knejslíková, Alena [LZ radiační onkologie] [pokračuje v CHRT, sliznice G0, urogen G0, dolní GIT G0, další CHT 12.3.26].pdf",
|
||||
"corrected": "5558270113 2026-03-03 Knejslíková, Alena [LZ radiační onkologie] [kontrola při radioterapii, pokračuje v CHRT, sliznice G0, urogen G0, dolní GIT G0, další CHT 12.3.26].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5558270113 2026-02-12 Knejslíková, Alena [LZ radiační onkologie] [Ca endometria pT1b pN1 FIGO IIIC1, indik. adjuv. CHRT PORTEC3, CHT CDDP+CBDCA+PTX].pdf",
|
||||
"corrected": "5558270113 2026-02-12 Knejslíková, Alena [LZ radiační onkologie] [chemopohovor, Ca endometria pT1b pN1 FIGO IIIC1, indik. adjuv. CHRT PORTEC3, CHT CDDP+CBDCA+PTX].pdf"
|
||||
},
|
||||
{
|
||||
"original": "465201175 2026-02-05 Voříšková, Helena [PZ kardiologie] [FIS a Flutter síní, paroxysmus Flutteru, AK Clexane, art. hypertenze].pdf",
|
||||
"corrected": "465201175 2026-02-05 Voříšková, Helena [PZ kardiologie] [4-5FEB2026, FIS a Flutter síní, paroxysmus Flutteru, AK Clexane, art. hypertenze].pdf"
|
||||
},
|
||||
{
|
||||
"original": "465201175 2026-01-07 Voříšková, Helena [PZ gynekologie-porodnictví] [I480 paroxyzmální FiS, pád, bolest kyčlí a levého hemithoraxu, analgetika].pdf",
|
||||
"corrected": "465201175 2026-01-07 Voříšková, Helena [PZ gynekologie] [z interny pro lůžkovou tíseň, I480 paroxyzmální FiS, pád, bolest kyčlí a levého hemithoraxu, analgetika].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470916013 2026-04-14 Dvořák, Josef [LZ plicní] [IPF dg. HRCT+klinika, MDT 2/2024, Ofev 100mg, Vigantol 3 kapky].pdf",
|
||||
"corrected": "470916013 2026-04-14 Dvořák, Josef [LZ plicní] [IPF dg. HRCT+klinika, MDT 22024, Ofev 100mg, Vigantol 3 kapky, ad kožní].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470916013 2026-04-02 Dvořák, Josef [Návrh na lázeňskou léčbu] [intersticiální plicní fibróza, indikace V/6, J84.1, Luhačovice+Mariánské Lázně].pdf",
|
||||
"corrected": "470916013 2026-04-02 Dvořák, Josef [Návrh na lázeňskou léčbu] [intersticiální plicní fibróza, indikace V6, J84.1, Luhačovice+Mariánské Lázně].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470916013 2026-04-02 Dvořák, Josef [Návrh na lázeňskou léčbu] [intersticiální plicní fibróza, indikace V6, J84.1, Luhačovice+Mariánské Lázně].pdf",
|
||||
"corrected": "470916013 2026-04-02 Dvořák, Josef [Návrh na lázeňskou léčbu příloha] [intersticiální plicní fibróza, indikace V6, J84.1, Luhačovice+Mariánské Lázně].pdf"
|
||||
},
|
||||
{
|
||||
"original": "470916013 2026-04-21 Dvořák, Josef [LZ interna] [revize před lázněmi, IPF, AVNRT, AH, DM2, dyslipidémie, hypotyreóza].pdf",
|
||||
"corrected": "470916013 2026-04-21 Dvořák, Josef [LZ interna] [vyšetření před lázněmi, IPF, AVNRT, AH, DM2, dyslipidémie, hypotyreóza].pdf"
|
||||
},
|
||||
{
|
||||
"original": "445318078 2026-04-23 Kusáková, Jaroslava [LZ revmatologie] [gonartróza III-IV st., susp. atypická PMR, klesající zánět, v plánu TEP 5/2026].pdf",
|
||||
"corrected": "445318078 2026-04-23 Kusáková, Jaroslava [LZ revmatologie] [gonartróza III-IV st., susp. atypická PMR, klesající zánět, v plánu TEP 52026].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5521946540 2025-12-30 Peterková, Eliška [PZ chirurgie] [S02.00 Fissura calvae, pád na eskalátoru, odlomení kost. fragmentu okcipitalně].pdf",
|
||||
"corrected": "5521946540 2025-12-30 Peterková, Eliška [PZ chirurgie] [29-30DEC2025, pád v metru, bezvìdomí, S02.00 Fissura calvae, pád na eskalátoru, odlomení kost. fragmentu okcipitalně].pdf"
|
||||
},
|
||||
{
|
||||
"original": "0552194654 2025-11-07 Peterková, Eliška [PZ psychiatrie] [F432, TS intox. venlafaxinem, 5x Epi záchvat, emočně nestab. osobnost].pdf",
|
||||
"corrected": "0552194654 2025-11-07 Peterková, Eliška [PZ psychiatrie] [31OCT-07NOV2025, pokus o sebevraždu, F432, TS intox. venlafaxinem, 5x Epi záchvat, emočně nestab. osobnost].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5505290252 2026-04-21 Flek, Zbyněk [LZ urologie] [Ca prostatae pT2cGS 3+4 po dvRP 6/2019, iPSA 6,26, PSA 0,081, drobné parapelv. cysty ledvin].pdf",
|
||||
"corrected": "5505290252 2026-04-21 Flek, Zbyněk [LZ urologie] [kontrola, Ca prostatae pT2cGS 3+4 po dvRP 62019, iPSA 6,26, PSA 0,081, drobné parapelv. cysty ledvin, trvá complete remission].pdf"
|
||||
},
|
||||
{
|
||||
"original": "8452 2026-04-02 Věkrbeová [Laboratoř] [moč chemicky: ERY trace, ostatní neg., pH 6, SG 1.020].pdf",
|
||||
"corrected": "8755120429 2026-04-02 [uritex] [moč chemicky ERY trace, ostatní neg., pH 6, SG 1.020].pdf"
|
||||
},
|
||||
{
|
||||
"original": "461001479 2026-04-21 Šťastný, Libor [LZ endokrinologie] [St.p. TTE dx a STE sin 5/18, strumiprivní hypotyreóza substituovaná, Letrox 150ug].pdf",
|
||||
"corrected": "461001479 2026-04-21 Šťastný, Libor [LZ endokrinologie] [St.p. TTE dx a STE sin 518, strumiprivní hypotyreóza substituovaná, Letrox 150ug].pdf"
|
||||
},
|
||||
{
|
||||
"original": "9901040000 2026-04-26 Tvrz, Matěj [export zdraví krevní tlak] [prům. 153/74 mmHg, hypertenze 5d, emergentní hypertenzní stav 1d].pdf",
|
||||
"corrected": "9901040000 2026-04-26 Tvrz, Matěj [export zdraví krevní tlak] [prům. 15374 mmHg, hypertenze 5d, emergentní hypertenzní stav 1d].pdf"
|
||||
},
|
||||
{
|
||||
"original": "395907022 2026-04-10 Herzová, Marie [LZ ortopedie] [Gonarthrosis bilat., obstr. kortik. +M i.a. vlevo, indik. lázně VII7, M179].pdf",
|
||||
"corrected": "395907022 2026-04-10 Herzová, Marie [LZ ortopedie] [indikace lázně VII7, M179, gonarthrosis bilat., obstr. kortik. +M i.a. vlevo].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2020-07-15 Nikitin, Petro [LZ gastroenterologie] [Antrumgastritida, inkompetentní kardie, gastroesophageální reflux].pdf",
|
||||
"corrected": "6008091738 2020-07-15 Nikitin, Petro [LZ gastro] [gastroskopie, antrumgastritida, inkompetentní kardie, gastroesophageální reflux].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2025-11-24 Nikitin, Petro [LZ dermatologie] [seboroická verruka].pdf",
|
||||
"corrected": "6008091738 2025-11-24 Nikitin, Petro [LZ kožní [seboroická verruka L tváøe, abraze].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2025-08-25 Nikitin, Petro [LZ kožní] [pigmentové névy tč. klidné, bez onkosuspekce].pdf",
|
||||
"corrected": "6008091738 2025-08-25 Nikitin, Petro [LZ kožní] [vyšetøení dermatoskopem, pigmentové névy tč. klidné, bez onkosuspekce].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2025-05-20 Nikitin, Petro [LZ kardiologie] [ICHS, po PCI RIA 2018, EF LK 65%, mírná dilatace aort. kořene bez progrese].pdf",
|
||||
"corrected": "6008091738 2025-05-20 Nikitin, Petro [LZ kardiologie] [kontrola, ICHS, po PCI RIA 2018, EF LK 65%, mírná dilatace aort. kořene bez progrese].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6008091738 2018-08-24 Nikitin, Petro [RTG páteře] [C páteř: lordosa oploštělá, C56 zúžen, spondylóza; Th: skolióza, kyfóza, Th7-10].pdf",
|
||||
"corrected": "6008091738 2018-08-24 Nikitin, Petro [RTG páteře] [C páteř lordosa oploštělá, C56 zúžen, spondylóza; Th skolióza, kyfóza, Th7-10].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7109203893 2026-04-07 Deyak, Mykhaylo [Laboratoř] [glukóza 7,1, HbA1c 36, chol. 4,49, LDL 3,07, HDL 0,99, osmolalita 301, PSA 1,438].pdf",
|
||||
"corrected": "7109203893 2026-04-07 Deyak, Mykhaylo [Laboratoř] [Z000, glukóza 7,1, HbA1c 36, chol. 4,49, LDL 3,07, HDL 0,99, osmolalita 301, PSA 1,438].pdf"
|
||||
},
|
||||
{
|
||||
"original": "415414073 2026-04-21 Pekárková, Vlasta [Laboratoř] [Z000, K 5,8, osmolalita 296, glukóza 5,7, HbA1c 41, CKD-EPI 0,92 G3a, trombocyty 140].pdf",
|
||||
"corrected": "415414073 2026-04-21 Pekárková, Vlasta [Laboratoř] [Z000, prediabetes, K 5,8, osmolalita 296, glukóza 5,7, HbA1c 41, CKD-EPI 0,92 G3a, trombocyty 140].pdf"
|
||||
},
|
||||
{
|
||||
"original": "505218025 2026-04-22 Beznosková, Milena [Laboratoř] [E789, urea 8,31, CKD-EPI 1,33 G2, osmolalita 302, glukóza 7,5, CK 5,49].pdf",
|
||||
"corrected": "505218025 2026-04-22 Beznosková, Milena [Laboratoř] [E789, diabetes, urea 8,31, CKD-EPI 1,33 G2, osmolalita 302, glukóza 7,5, CK 5,49].pdf"
|
||||
},
|
||||
{
|
||||
"original": "500206172 2026-04-22 Beznoska, Miloslav [Laboratoř] [E789, CKD-EPI 1,21 G2, glukóza 5,9, HbA1c 41, LDL 3,29].pdf",
|
||||
"corrected": "500206172 2026-04-22 Beznoska, Miloslav [Laboratoř] [E789, prediabetes, CKD-EPI 1,21 G2, glukóza 5,9, HbA1c 41, LDL 3,29].pdf"
|
||||
},
|
||||
{
|
||||
"original": "475915054 2026-04-20 Žabová, Věra [Laboratoř] [moč: E. coli 10E5 CFU/ml, citlivá na ampicilin, cefuroxim, cotrimoxazol, pivmecilinam].pdf",
|
||||
"corrected": "475915054 2026-04-20 Žabová, Věra [Laboratoř] [N309, kultivace a citlivost, moč E. coli 10E5 CFUml, citlivá na ampicilin, cefuroxim, cotrimoxazol, pivmecilinam].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7059087629 2026-04-13 Tůmová, Renáta [Laboratoř] [E789, chol. 7,34, LDL 4,52, non-HDL 5,53, glukóza 5,83, CKD-EPI 1,42 G2].pdf",
|
||||
"corrected": "7059087629 2026-04-13 Tůmová, Renáta [Laboratoř] [E789, smíšená hyperlipidémie, prediabetes, chol. 7,34, LDL 4,52, non-HDL 5,53, glukóza 5,83, CKD-EPI 1,42 G2].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7352200328 2026-04-10 Vališová, Gabriela [Laboratoř] [Z000, chol. 5,62, LDL 3,19, HDL 1,13, TG 4,29, non-HDL 4,5, glukóza 5,4].pdf",
|
||||
"corrected": "7352200328 2026-04-10 Vališová, Gabriela [Laboratoř] [Z000, smíšená hyperlipidémie, chol. 5,62, LDL 3,19, HDL 1,13, TG 4,29, non-HDL 4,5, glukóza 5,4].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6757100592 2026-04-16 Slabá, Radka [Laboratoř] [E789, CKD-EPI 1,31 G2, TG 1,90, glukóza 5,8, HbA1c 36, chol. 4,35, LDL 2,10].pdf",
|
||||
"corrected": "6757100592 2026-04-16 Slabá, Radka [Laboratoř] [E789, prediabetes, CKD-EPI 1,31 G2, TG 1,90, glukóza 5,8, HbA1c 36, chol. 4,35, LDL 2,10].pdf"
|
||||
},
|
||||
{
|
||||
"original": "395907022 2026-04-10 Herzová, Marie [LZ ortopedie] [gonarthrosis bilat, obstřik kortik+M i.a., indik. lázeňská terapie VII/7 M179].pdf",
|
||||
"corrected": "395907022 2026-04-10 Herzová, Marie [LZ ortopedie] [gonarthrosis bilat, obstřik kortik+M i.a., indik. lázeňská terapie VII7 M179].pdf"
|
||||
},
|
||||
{
|
||||
"original": "356031017 2025-10-27 Mejstříková, Marcela [LZ gastroenterologie] [inkompetence kardie, lehce polyp. GE junkce, antrální gastropatie, biopsie].pdf",
|
||||
"corrected": "356031017 2025-10-27 Mejstříková, Marcela [LZ gastroenterologie] [gastroskopie, inkompetence kardie, lehce polyp. GE junkce, antrální gastropatie, biopsie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "356031017 2026-01-27 Mejstříková, Marcela [SONO krku] [drobné koloidní uzlíky a spongiformní uzel levého laloku š.ž.].pdf",
|
||||
"corrected": "356031017 2026-01-27 Mejstříková, Marcela [sono ŠŽ] [drobné koloidní uzlíky a spongiformní uzel levého laloku ŠŽ].pdf"
|
||||
},
|
||||
{
|
||||
"original": "346204097 2025-11-14 Kopřivíková, Jarmila [PZ neurologie] [11–14NOV2025 embolus M2 ACM sin, trombektomie TICl2c, iCMP].pdf",
|
||||
"corrected": "346204097 2025-11-14 Kopřivíková, Jarmila [PZ neurologie] [11–14NOV2025, iktus, embolus M2 ACM sin, trombektomie TICl2c, iCMP].pdf"
|
||||
},
|
||||
{
|
||||
"original": "8351112693 2026-04-27 Zelenková, Petra [sono mamm.] [fibrozní dysplazie, vícečetné fibromy bilat., expanzivní proces ZHQ vlevo s benigními markantami].pdf",
|
||||
"corrected": "8351112693 2026-04-27 Zelenková, Petra [sono prsù] [fibrozní dysplazie, vícečetné fibromy bilat., expanzivní proces ZHQ vlevo s benigními markantami].pdf"
|
||||
},
|
||||
{
|
||||
"original": "450113005 2025-01-16 Fiala, Václav [LZ angiologie] [Ektazie AP bilat., sono žil DKK: bil. hluboký žilní syst. bez trombozy, varikozity bilat.].pdf",
|
||||
"corrected": "450113005 2025-01-16 Fiala, Václav [LZ angiologie] [Ektazie AP bilat., sono žil DKK bil. hluboký žilní syst. bez trombozy, varikozity bilat.].pdf"
|
||||
},
|
||||
{
|
||||
"original": "450113005 2025-03-28 Fiala, Václav [CT krku, hrudníku, břicha a pánve] [progrese nadbráničníí lymfadenopatie, NHL MZL KS IV A, 1. relaps].pdf",
|
||||
"corrected": "450113005 2025-03-28 Fiala, Václav [CT krku, hrudníku, břicha a pánve] [progrese nadbráničníí lymfadenopatie, NHL MZL KS IV A, 1. relaps, nekompletní zpráva].pdf"
|
||||
},
|
||||
{
|
||||
"original": "450113005 2019-02-10 Fiala, Václav [LZ denzitometrie] [snížení kostní denzity v pásmu osteopenie, nehomogenní rozložení denzity].pdf",
|
||||
"corrected": "450113005 2019-02-10 Fiala, Václav [LZ denzitometrie] [T-1.6, osteopenie, snížení kostní denzity v pásmu osteopenie, nehomogenní rozložení denzity].pdf"
|
||||
},
|
||||
{
|
||||
"original": "450113005 2026-02-18 Fiala, Václav [LZ hematologie] [MZL, lymfocytóza 42.98, B-NHL CD20+ 65.4%, lymfadenopatie, FIS po kardioverzi].pdf",
|
||||
"corrected": "450113005 2026-02-18 Fiala, Václav [LZ hematologie] [NHL, lymfocytóza 42.98, B-NHL CD20+ 65.4%, lymfadenopatie, FIS po kardioverzi].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5862236435 2026-03-18 Kopřivová, Erika [sono břicha] [st.p. IK resekci, IK anastomóza, neo-TI, jaterní steatóza, korové cysty pravé ledviny].pdf",
|
||||
"corrected": "5862236435 2026-03-18 Kopřivová, Erika [sono břicha] [Crohnova nemoc, st.p. IK resekci, IK anastomóza, neo-TI, jaterní steatóza, korové cysty pravé ledviny].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5862236435 2026-01-12 Kopřivová, Erika [LZ gastroenterologie] [Crohnova nemoc, st.p. LPSK IK resekci, terapie Entyvio, switch z Remsima 1/2021].pdf",
|
||||
"corrected": "5862236435 2026-01-12 Kopřivová, Erika [LZ gastroenterologie] [Crohnova nemoc, st.p. LPSK IK resekci, terapie Entyvio, switch z Remsima 12021].pdf"
|
||||
},
|
||||
{
|
||||
"original": "365123089 2026-04-22 Opršalová, Libuše [Laboratoř] [dg. I839, warfarin, PT ratio 2.32*, INR 2.48*].pdf",
|
||||
"corrected": "365123089 2026-04-22 Opršalová, Libuše [Laboratoř] [dg. I839, warfarin, PT ratio 2.32, INR 2.48].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7361130040 2021-12-07 Šilhavá, Simona [LZ plicní] [FVP: FEV1 61%, FEV1/FVC 79%, PEF 48%, TLco 65%, obstrukce].pdf",
|
||||
"corrected": "7361130040 2021-12-07 Šilhavá, Simona [LZ plicní] [FVP FEV1 61%, FEV1FVC 79%, PEF 48%, TLco 65%, obstrukce].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5862236435 2026-02-23 Kopřivová, Erika [LZ interna] [hypertenze TK 161/95, BMI 31.87, Crohn-Entyvio, DM2, HLP, hyperurikémie, CPAP].pdf",
|
||||
"corrected": "5862236435 2026-02-23 Kopřivová, Erika [LZ interna] [hypertenze TK 16195, BMI 31.87, Crohn-Entyvio, DM2, HLP, hyperurikémie, CPAP, ad urologie].pdf"
|
||||
},
|
||||
{
|
||||
"original": "425412434 null Hornofová, Helena [LZ algologie] [P ramenní funkční, min. bolestivý; L ramenní bolestivý, hybnost dobrá; Biofenac, RTG ramen].pdf",
|
||||
"corrected": "425412434 2026-04-28 Hornofová, Helena [LZ ambulance bolesti] [nevím datum, P ramenní funkční, min. bolestivý; L ramenní bolestivý, hybnost dobrá; Biofenac, RTG ramen].pdf"
|
||||
},
|
||||
{
|
||||
"original": "7701120955 2026-04-21 Moudrý, Michal [LZ interna] [EKG sinusový rytmus 64/min, křivka v mezích normy, arteriální hypertenze].pdf",
|
||||
"corrected": "7701120955 2026-04-21 Moudrý, Michal [LZ interna] [EKG sinusový rytmus 64min, křivka v mezích normy, arteriální hypertenze].pdf"
|
||||
},
|
||||
{
|
||||
"original": "471126130 2026-04-07 Procházka, Vladimír [LZ angiologie] [CVI bez progrese, bez TEN, varikozity přibývají, Duplex UŽ DK bez obliterace].pdf",
|
||||
"corrected": "471126130 2026-04-07 Procházka, Vladimír [LZ cévní] [CVI bez progrese, bez TEN, varikozity přibývají, Duplex UŽ DK bez obliterace].pdf"
|
||||
},
|
||||
{
|
||||
"original": "471126130 2026-02-12 Procházka, Vladimír [LZ kardiologie] [FS 114min, QRS 110, LAH, indikována reablace FS].pdf",
|
||||
"corrected": "471126130 2026-02-12 Procházka, Vladimír [LZ kardiologie] [plánovaná kontrola, FS 114min, QRS 110, LAH, indikována reablace FS].pdf"
|
||||
},
|
||||
{
|
||||
"original": "null 2026-04-27 Drakpelova [Laboratoř] [DM2, Glucophage XR 1000 0-0-1, HbA1c 4.5-4.2, Chol 3.07-3.77].pdf",
|
||||
"corrected": "515820013 2026-04-27 Drakselová, Daniela [INR karta] [07APR2025-10MAR2026].pdf"
|
||||
},
|
||||
{
|
||||
"original": "5951231044 2026-04-21 Vašinová, Jiřina [PZ ortopedie] [13–21APR2026 impl. TEP coxae l.dx., koxartróza l.dx. KL IV.].pdf",
|
||||
"corrected": "5951231044 2026-04-21 Vašinová, Jiřina [PZ ortopedie] [13–21APR2026 impl. TEP coxae l.dx., koxartróza l.dx., indikace KLL VII_10].pdf"
|
||||
},
|
||||
{
|
||||
"original": "480416072 2026-03-09 Štrup, Petr [žádanka OZP] [žádanka o vyšetření zdravotního stavu pro průkaz OZP, komplexní vyšetření 958 Kč].pdf",
|
||||
"corrected": "480416072 2026-03-09 Štrup, Petr [žádanka IPZS] [žádanka o vyšetření zdravotního stavu pro průkaz OZP, komplexní vyšetření 958 Kč].pdf"
|
||||
},
|
||||
{
|
||||
"original": "9301280417 2026-03-25 Vaňous, Jakub [žádost o předání zdravotních informací] [registrace u MUDr. Panáčkové, žádost o zaslání dokumentace].pdf",
|
||||
"corrected": "9301280417 2026-03-25 Vaňous, Jakub [žádost o předání zdravotních informací] [ResTrial s.r.o.].pdf"
|
||||
},
|
||||
{
|
||||
"original": "Binder1.pdf",
|
||||
"corrected": "8056010149 2026-04-28 [výbìr nekompletních zpráv] [od pacientky].pdf"
|
||||
},
|
||||
{
|
||||
"original": "6709150613 2026-04-28 Rutrle, Petr [LZ ORL] [PVS - v.s m. Menier, t.č hypaksuis perc. apicochlearis].pdf",
|
||||
"corrected": "6709150613 2026-04-28 Rutrle, Petr [LZ ORL] [PVS - v.s m. Menier, t.č hypakusis perc. apicochlearis, doporučena hyperbarická komora].pdf"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,468 @@
|
||||
"""
|
||||
Zpracování naskenovaných PDF — nová verze.
|
||||
1. Preview originálu + Claude Vision API
|
||||
2. Rename dialog
|
||||
3. 5 variant komprese → uživatel vybere
|
||||
4. Uložit do Processed, smazat originál
|
||||
"""
|
||||
import base64
|
||||
import gc
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
if sys.platform == "win32":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
import anthropic
|
||||
from pdf2image import convert_from_path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from Knihovny.najdi_dropbox import get_dropbox_root
|
||||
from Knihovny.najdi_medicus import get_medicus_config
|
||||
|
||||
def _load_env():
|
||||
env_path = Path(__file__).parent.parent / ".env"
|
||||
if env_path.exists():
|
||||
for line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if "=" in line and not line.startswith("#"):
|
||||
k, v = line.split("=", 1)
|
||||
os.environ[k.strip()] = v.strip()
|
||||
|
||||
_load_env()
|
||||
|
||||
POPPLER_PATH = r"C:/Poppler/Library/bin"
|
||||
CORRECTIONS = True # True = corrections.json se načítá a ukládá; False = ignorovat
|
||||
|
||||
_DROPBOX = Path(get_dropbox_root())
|
||||
TO_PROCESS = _DROPBOX / r"Ordinace\Dokumentace_ke_zpracování\Ricoh Fi-8040\KeZpracování"
|
||||
PROCESSED = _DROPBOX / r"Ordinace\Dokumentace_ke_zpracování\Ricoh Fi-8040\Zpracováno"
|
||||
CORRECTIONS_FILE = Path(__file__).parent / "corrections.json"
|
||||
NAMING_RULES_FILE = Path(__file__).parent / "naming_rules.md"
|
||||
DOKUMENTACE = _DROPBOX / r"Ordinace\Dokumentace_zpracovaná"
|
||||
|
||||
import threading
|
||||
|
||||
_dokumentace_index: set[str] = set()
|
||||
_dokumentace_ready = threading.Event()
|
||||
|
||||
def _load_dokumentace_index_bg():
|
||||
if DOKUMENTACE.exists():
|
||||
names = {f.name for f in DOKUMENTACE.iterdir() if f.is_file()}
|
||||
else:
|
||||
names = set()
|
||||
global _dokumentace_index
|
||||
_dokumentace_index = names
|
||||
_dokumentace_ready.set()
|
||||
print(f" Index dokumentace: {len(names)} souborů načteno.")
|
||||
|
||||
def start_dokumentace_index():
|
||||
t = threading.Thread(target=_load_dokumentace_index_bg, daemon=True)
|
||||
t.start()
|
||||
|
||||
VIEWER = Path(__file__).parent / "preview_viewer.py"
|
||||
RENAME_DIALOG = Path(__file__).parent / "rename_dialog.py"
|
||||
VARIANT_PICKER = Path(__file__).parent / "variant_picker.py"
|
||||
|
||||
# 5 kompresních variant
|
||||
COMPRESS_VARIANTS = [
|
||||
("300 DPI / q90", 300, 90),
|
||||
("200 DPI / q85", 200, 85),
|
||||
("150 DPI / q80", 150, 80),
|
||||
("120 DPI / q75", 120, 75),
|
||||
( "96 DPI / q70", 96, 70),
|
||||
]
|
||||
|
||||
|
||||
# ─── Komprese jedné varianty ──────────────────────────────────────────────────
|
||||
|
||||
def compress_to_temp(pdf_path: Path, dpi: int, quality: int) -> Path:
|
||||
import fitz
|
||||
src = fitz.open(str(pdf_path))
|
||||
mat = fitz.Matrix(dpi / 72.0, dpi / 72.0)
|
||||
out = fitz.open()
|
||||
for page in src:
|
||||
pix = page.get_pixmap(matrix=mat, colorspace=fitz.csRGB)
|
||||
img_bytes = pix.tobytes("jpeg", jpg_quality=quality)
|
||||
img_doc = fitz.open("pdf", fitz.open("jpeg", img_bytes).convert_to_pdf())
|
||||
rect = page.rect
|
||||
np = out.new_page(width=rect.width, height=rect.height)
|
||||
np.show_pdf_page(np.rect, img_doc, 0)
|
||||
src.close()
|
||||
tmp = Path(tempfile.mktemp(suffix=".pdf"))
|
||||
out.save(tmp, deflate=True, garbage=4)
|
||||
out.close()
|
||||
return tmp
|
||||
|
||||
|
||||
# ─── Medicus ověření ─────────────────────────────────────────────────────────
|
||||
|
||||
def _medicus_connect():
|
||||
try:
|
||||
import fdb
|
||||
cfg = get_medicus_config()
|
||||
return fdb.connect(dsn=cfg.dsn, user="SYSDBA", password="masterkey", charset="win1250")
|
||||
except Exception as e:
|
||||
print(f" [Medicus] Nepřipojeno: {e}")
|
||||
return None
|
||||
|
||||
def _lookup_by_rc(cur, rc_digits: str) -> dict | None:
|
||||
cur.execute(
|
||||
"SELECT IDPAC, PRIJMENI, JMENO, RODCIS FROM KAR "
|
||||
"WHERE REPLACE(RODCIS, '/', '') = ?", (rc_digits,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return {"idpac": row[0], "prijmeni": row[1].strip(), "jmeno": row[2].strip(), "rodcis": row[3].strip()}
|
||||
return None
|
||||
|
||||
def _rc_candidates(rc: str) -> list[str]:
|
||||
similar = {"0": "8", "8": "0", "1": "7", "7": "1", "5": "6", "6": "5", "3": "8"}
|
||||
candidates = set()
|
||||
for i in range(len(rc)):
|
||||
candidates.add(rc[:i] + rc[i+1:])
|
||||
for i in range(len(rc) + 1):
|
||||
candidates.add(rc[:i] + "0" + rc[i:])
|
||||
for i, ch in enumerate(rc):
|
||||
if ch in similar:
|
||||
candidates.add(rc[:i] + similar[ch] + rc[i+1:])
|
||||
candidates.discard(rc)
|
||||
return sorted(c for c in candidates if len(c) in (9, 10))
|
||||
|
||||
def _rc_checksum_ok(rc: str) -> bool:
|
||||
digits = re.sub(r"\D", "", rc)
|
||||
if len(digits) == 10:
|
||||
return int(digits) % 11 == 0
|
||||
return True
|
||||
|
||||
def verify_patient(rc_raw: str) -> dict:
|
||||
rc = re.sub(r"\D", "", rc_raw or "")
|
||||
if not rc:
|
||||
return {"status": "not_found", "patient": None, "rc_corrected": None}
|
||||
con = _medicus_connect()
|
||||
if con is None:
|
||||
return {"status": "offline", "patient": None, "rc_corrected": None}
|
||||
try:
|
||||
cur = con.cursor()
|
||||
patient = _lookup_by_rc(cur, rc)
|
||||
if patient:
|
||||
return {"status": "ok", "patient": patient, "rc_corrected": None}
|
||||
candidates = _rc_candidates(rc)
|
||||
matches = [(c, _lookup_by_rc(cur, c)) for c in candidates]
|
||||
matches = [(c, p) for c, p in matches if p]
|
||||
if not matches:
|
||||
return {"status": "not_found", "patient": None, "rc_corrected": None}
|
||||
matches.sort(key=lambda x: (0 if _rc_checksum_ok(x[0]) else 1))
|
||||
best_rc, best_patient = matches[0]
|
||||
return {"status": "fuzzy", "patient": best_patient, "rc_corrected": best_rc, "all_matches": matches}
|
||||
finally:
|
||||
con.close()
|
||||
|
||||
def check_duplicates(rc: str, datum: str) -> list[str]:
|
||||
if not rc or not datum:
|
||||
return []
|
||||
# Počkej max 15s na dokončení indexu (typicky hotovo za dobu volání Claude)
|
||||
_dokumentace_ready.wait(timeout=15)
|
||||
prefix = f"{rc} {datum}"
|
||||
return [name for name in _dokumentace_index if name.startswith(prefix)]
|
||||
|
||||
|
||||
# ─── Korekce (few-shot příklady) ─────────────────────────────────────────────
|
||||
|
||||
def load_corrections() -> list[dict]:
|
||||
if CORRECTIONS_FILE.exists():
|
||||
return json.loads(CORRECTIONS_FILE.read_text(encoding="utf-8"))
|
||||
return []
|
||||
|
||||
def save_correction(original: str, corrected: str):
|
||||
if not CORRECTIONS:
|
||||
return
|
||||
corrections = load_corrections()
|
||||
for c in corrections:
|
||||
if c["original"] == original and c["corrected"] == corrected:
|
||||
return
|
||||
corrections.append({"original": original, "corrected": corrected})
|
||||
CORRECTIONS_FILE.write_text(
|
||||
json.dumps(corrections, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
print(f" ✓ Korekce uložena ({len(corrections)} celkem)")
|
||||
|
||||
def load_naming_rules() -> str:
|
||||
if NAMING_RULES_FILE.exists():
|
||||
content = NAMING_RULES_FILE.read_text(encoding="utf-8").strip()
|
||||
if content:
|
||||
return f"Pravidla pro pojmenování souborů (dodržuj vždy):\n{content}\n\n"
|
||||
return ""
|
||||
|
||||
def build_corrections_prompt() -> str:
|
||||
if not CORRECTIONS:
|
||||
return ""
|
||||
corrections = load_corrections()
|
||||
if not corrections:
|
||||
return ""
|
||||
lines = ["Příklady korekcí z minulých běhů (uč se z nich):"]
|
||||
for c in corrections[-10:]:
|
||||
lines.append(f' - špatně: "{c["original"]}"')
|
||||
lines.append(f' správně: "{c["corrected"]}"')
|
||||
return "\n".join(lines) + "\n\n"
|
||||
|
||||
|
||||
# ─── Claude Vision API ────────────────────────────────────────────────────────
|
||||
|
||||
def extract_info(pdf_path: Path) -> dict:
|
||||
print(" Převádím na obrázek...")
|
||||
suffix = pdf_path.suffix.lower()
|
||||
if suffix in (".jpg", ".jpeg", ".png"):
|
||||
from PIL import Image
|
||||
img = Image.open(pdf_path)
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format="JPEG", quality=95)
|
||||
img.close()
|
||||
else:
|
||||
images = convert_from_path(str(pdf_path), poppler_path=POPPLER_PATH, dpi=300)
|
||||
buf = io.BytesIO()
|
||||
images[0].save(buf, format="JPEG", quality=95)
|
||||
del images
|
||||
gc.collect()
|
||||
image_b64 = base64.standard_b64encode(buf.getvalue()).decode("utf-8")
|
||||
|
||||
prompt = (
|
||||
load_naming_rules() +
|
||||
build_corrections_prompt() +
|
||||
"Toto je naskenovaná lékařská zpráva v češtině. "
|
||||
"Vrať JSON s těmito poli:\n"
|
||||
"- \"jmeno\": celé jméno pacienta (příjmení + jméno + případný titul)\n"
|
||||
"- \"rodne_cislo\": rodné číslo pacienta BEZ lomítka (pouze číslice)\n"
|
||||
"- \"datum_zpravy\": datum zprávy ve formátu YYYY-MM-DD\n"
|
||||
"- \"typ_dokumentu\": typ dokumentu — "
|
||||
"\"LZ {oddělení}\" = ambulantní/lékařská zpráva (např. \"LZ chirurgie\", \"LZ kardiologie\", \"LZ plicní\", \"LZ ORL\"); "
|
||||
"\"PZ {oddělení}\" = propouštěcí zpráva z hospitalizace (např. \"PZ interna\", \"PZ neurologie\"). "
|
||||
"Jiné typy: \"Laboratoř\", \"CT břicha\", \"MRI páteře\", \"kolonoskopie\", "
|
||||
"\"operační protokol oční\", \"poukaz FT\", \"diagnostická mamografie\" atd.\n"
|
||||
"- \"poznamka\": krátká klinická poznámka česky, max 80 znaků. "
|
||||
"DŮLEŽITÉ: pokud zpráva obsahuje sekci \"Závěr:\" nebo \"Závěr vyšetření:\", "
|
||||
"použij VÝHRADNĚ obsah této sekce — je nejdůležitější. "
|
||||
"Teprve pokud závěr chybí, shrň obsah z celé zprávy.\n"
|
||||
"- \"nazev_souboru\": název souboru ve formátu "
|
||||
"\"{rodne_cislo} {datum_zpravy} {Příjmení}, {Jméno} [{typ_dokumentu}] [{poznamka}].pdf\" "
|
||||
"(jméno bez titulu, RČ bez lomítka)\n"
|
||||
"- \"rotace\": o kolik stupňů CCW je třeba otočit obrázek aby byl text čitelně na výšku nebo šířku "
|
||||
"(hodnoty: 0, 90, 180, 270). Pokud je text již správně orientovaný, vrať 0.\n\n"
|
||||
"Pokud pole nenajdeš, použij null. Nepiš nic jiného než JSON."
|
||||
)
|
||||
|
||||
print(" Volám Claude Vision API...")
|
||||
try:
|
||||
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-6",
|
||||
max_tokens=400,
|
||||
messages=[{"role": "user", "content": [
|
||||
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_b64}},
|
||||
{"type": "text", "text": prompt},
|
||||
]}],
|
||||
)
|
||||
usage = response.usage
|
||||
print(f" Tokeny: {usage.input_tokens} in + {usage.output_tokens} out = ${usage.input_tokens*3/1e6 + usage.output_tokens*15/1e6:.4f}")
|
||||
|
||||
raw = response.content[0].text.strip()
|
||||
if raw.startswith("```"):
|
||||
raw = raw.split("```")[1]
|
||||
if raw.startswith("json"):
|
||||
raw = raw[4:]
|
||||
try:
|
||||
return json.loads(raw.strip())
|
||||
except json.JSONDecodeError:
|
||||
print(f" VAROVÁNÍ: nelze parsovat JSON: {raw!r}")
|
||||
return {"nazev_souboru": None, "raw": raw}
|
||||
except Exception as e:
|
||||
print(f" VAROVÁNÍ: Claude API selhalo ({e}) — otevírám dialog pro ruční vyplnění.")
|
||||
return {"nazev_souboru": None}
|
||||
|
||||
|
||||
# ─── Subprocess helpers ───────────────────────────────────────────────────────
|
||||
|
||||
def open_preview(pdf_path: Path) -> tuple[subprocess.Popen, Path]:
|
||||
geom_file = Path(tempfile.mktemp(suffix=".json"))
|
||||
proc = subprocess.Popen([sys.executable, str(VIEWER), str(pdf_path), f"--write-geometry={geom_file}"])
|
||||
return proc, geom_file
|
||||
|
||||
|
||||
def read_preview_bottom(geom_file: Path, timeout: float = 5.0) -> int:
|
||||
import time
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
if geom_file.exists():
|
||||
geom = json.loads(geom_file.read_text(encoding="utf-8"))
|
||||
geom_file.unlink(missing_ok=True)
|
||||
return geom["y"] + geom["h"] + 30 # +30 pro title bar
|
||||
time.sleep(0.1)
|
||||
geom_file.unlink(missing_ok=True)
|
||||
return None
|
||||
|
||||
|
||||
def run_rename_dialog(nazev: str, info_lines: list, below_y: int = None) -> str | None:
|
||||
tmp = Path(tempfile.mktemp(suffix=".json"))
|
||||
tmp.write_text(json.dumps({"nazev": nazev, "info_lines": info_lines}, ensure_ascii=False), encoding="utf-8")
|
||||
args = [sys.executable, str(RENAME_DIALOG), str(tmp)]
|
||||
if below_y is not None:
|
||||
args.append(f"--below-y={below_y}")
|
||||
env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
|
||||
proc = subprocess.run(args, capture_output=True, text=True, encoding="utf-8", env=env)
|
||||
tmp.unlink(missing_ok=True)
|
||||
out = proc.stdout.strip()
|
||||
return json.loads(out).get("value") if out else None
|
||||
|
||||
|
||||
def run_variant_picker(variants_data: list) -> str | None:
|
||||
tmp = Path(tempfile.mktemp(suffix=".json"))
|
||||
tmp.write_text(json.dumps(variants_data, ensure_ascii=False), encoding="utf-8")
|
||||
proc = subprocess.run(
|
||||
[sys.executable, str(VARIANT_PICKER), str(tmp)],
|
||||
capture_output=True, text=True, encoding="utf-8",
|
||||
)
|
||||
tmp.unlink(missing_ok=True)
|
||||
if proc.returncode != 0 or not proc.stdout.strip():
|
||||
print(f" [variant_picker] returncode={proc.returncode}")
|
||||
if proc.stderr.strip():
|
||||
print(f" [variant_picker] CHYBA:\n{proc.stderr.strip()}")
|
||||
out = proc.stdout.strip()
|
||||
return json.loads(out).get("chosen") if out else None
|
||||
|
||||
|
||||
# ─── Hlavní flow ──────────────────────────────────────────────────────────────
|
||||
|
||||
def process_file(pdf_path: Path):
|
||||
print(f"\nSoubor: {pdf_path.name}")
|
||||
|
||||
# Spusť načítání indexu dokumentace na pozadí — hotovo za dobu volání Claude
|
||||
start_dokumentace_index()
|
||||
|
||||
# 1. Otevři preview originálu
|
||||
preview, geom_file = open_preview(pdf_path)
|
||||
below_y = read_preview_bottom(geom_file)
|
||||
|
||||
# 2. Claude Vision API
|
||||
info = extract_info(pdf_path)
|
||||
nazev = info.get("nazev_souboru") or pdf_path.name
|
||||
|
||||
# 3. Medicus ověření + fuzzy matching RČ
|
||||
rc_from_scan = re.sub(r"\D", "", info.get("rodne_cislo") or "")
|
||||
print(f" Ověřuji v Medicus (RČ: {rc_from_scan})...")
|
||||
verif = verify_patient(rc_from_scan)
|
||||
|
||||
# Oprava RČ při fuzzy matchi
|
||||
if verif["status"] == "fuzzy" and verif.get("rc_corrected") and nazev:
|
||||
nazev = nazev.replace(rc_from_scan, verif["rc_corrected"], 1)
|
||||
print(f" → RČ opraveno: {rc_from_scan} → {verif['rc_corrected']}")
|
||||
|
||||
# Info řádky pro dialog
|
||||
status = verif["status"]
|
||||
patient = verif.get("patient")
|
||||
info_lines = []
|
||||
if status == "ok":
|
||||
info_lines.append(f"✓ Medicus: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
elif status == "fuzzy":
|
||||
info_lines.append(f"⚠ RČ ze skenu '{rc_from_scan}' → opraveno na {verif['rc_corrected']}")
|
||||
info_lines.append(f" Pacient: {patient['prijmeni']} {patient['jmeno']} | RČ {patient['rodcis']}")
|
||||
elif status == "not_found":
|
||||
info_lines.append(f"✗ RČ '{rc_from_scan}' nenalezeno v Medicus")
|
||||
else:
|
||||
info_lines.append("— Medicus nedostupný (offline)")
|
||||
|
||||
# Duplicity
|
||||
rc_final = re.sub(r"\D", "", verif["patient"]["rodcis"] if patient else rc_from_scan)
|
||||
duplicity = check_duplicates(rc_final, info.get("datum_zpravy") or "")
|
||||
if duplicity:
|
||||
info_lines.append(f"⚠ DUPLICITA: {', '.join(duplicity)}")
|
||||
|
||||
if not info_lines:
|
||||
info_lines = ["[Claude nevrátil název — uprav ručně]"]
|
||||
print(" Otevírám dialog pro schválení názvu...")
|
||||
final_name = run_rename_dialog(nazev, info_lines, below_y=below_y)
|
||||
|
||||
preview.terminate()
|
||||
|
||||
if not final_name:
|
||||
print(" Přeskočeno.")
|
||||
return
|
||||
|
||||
if not final_name.endswith(".pdf"):
|
||||
final_name += ".pdf"
|
||||
final_name = re.sub(r'[<>:"/\\|?*]', '', final_name)
|
||||
|
||||
if nazev and final_name != nazev:
|
||||
save_correction(nazev, final_name)
|
||||
|
||||
print(f" Schválený název: {final_name}")
|
||||
|
||||
# 4. Generuj kompresní varianty (originál + 5 variant)
|
||||
print(" Generuji kompresní varianty...")
|
||||
temp_files = []
|
||||
orig_kb = round(pdf_path.stat().st_size / 1024)
|
||||
variants_data = [{"path": str(pdf_path), "label": "Originál", "size_kb": orig_kb}]
|
||||
for label, dpi, quality in COMPRESS_VARIANTS:
|
||||
tmp = compress_to_temp(pdf_path, dpi, quality)
|
||||
size_kb = round(tmp.stat().st_size / 1024)
|
||||
temp_files.append(tmp)
|
||||
variants_data.append({"path": str(tmp), "label": label, "size_kb": size_kb})
|
||||
print(f" {label}: {size_kb} kB")
|
||||
|
||||
# 5. Vyber variantu
|
||||
print(" Vyber variantu v okně...")
|
||||
chosen = run_variant_picker(variants_data)
|
||||
|
||||
if not chosen:
|
||||
print(" Žádná varianta nevybrána, přeskakuji.")
|
||||
for t in temp_files:
|
||||
t.unlink(missing_ok=True)
|
||||
return
|
||||
|
||||
# 6. Ulož do Processed
|
||||
PROCESSED.mkdir(exist_ok=True)
|
||||
dest = PROCESSED / final_name
|
||||
if dest.exists():
|
||||
print(f" Přepisuji existující: {dest.name}")
|
||||
shutil.copy2(chosen, dest)
|
||||
pdf_path.unlink()
|
||||
print(f" ✓ Uloženo: {dest.name}")
|
||||
|
||||
for t in temp_files:
|
||||
t.unlink(missing_ok=True) # originál mezi temp_files není, je bezpečné
|
||||
|
||||
|
||||
def process_folder(folder: Path):
|
||||
files = sorted(f for f in folder.iterdir() if f.suffix.lower() in (".pdf", ".jpg", ".jpeg", ".png"))
|
||||
if not files:
|
||||
print(f"Žádné soubory v: {folder}")
|
||||
return
|
||||
print(f"Nalezeno {len(files)} soubor(ů).")
|
||||
for f in files:
|
||||
try:
|
||||
process_file(f)
|
||||
except Exception as e:
|
||||
print(f" CHYBA: {e}")
|
||||
print("\nHotovo.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PROCESSED.mkdir(exist_ok=True)
|
||||
TO_PROCESS.mkdir(exist_ok=True)
|
||||
|
||||
target = Path(sys.argv[1]) if len(sys.argv) > 1 else TO_PROCESS
|
||||
|
||||
if target.is_file():
|
||||
process_file(target)
|
||||
elif target.is_dir():
|
||||
process_folder(target)
|
||||
else:
|
||||
print("Použití: python extract_patient_info_novy.py [soubor.pdf nebo složka]")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Konverze JPG/PNG → PDF se správnou orientací stránky (A4).
|
||||
|
||||
Řeší:
|
||||
- EXIF orientaci (fotky z telefonu/skeneru bývají otočené)
|
||||
- Správné umístění na A4 stránce (na výšku nebo na šířku dle obsahu)
|
||||
- Zachování kvality
|
||||
|
||||
Použití:
|
||||
python jpg_to_pdf.py soubor.jpg
|
||||
python jpg_to_pdf.py soubor.jpg vystup.pdf
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
# A4 rozměry v mm
|
||||
A4_W_MM = 210
|
||||
A4_H_MM = 297
|
||||
MARGIN_MM = 0 # bez okraje, tisk si řeší Acrobat (Fit to Print)
|
||||
|
||||
|
||||
def fix_orientation(img: Image.Image) -> Image.Image:
|
||||
"""Opraví rotaci podle EXIF dat (tag 274)."""
|
||||
return ImageOps.exif_transpose(img)
|
||||
|
||||
|
||||
def image_to_pdf(src: Path, dst: Path, dpi: int = 150, quality: int = 80, rotate_ccw: int = 0):
|
||||
img = Image.open(src)
|
||||
print(f" Originál: {img.size[0]}×{img.size[1]} px, mode={img.mode}, format={img.format}")
|
||||
|
||||
# 1. Oprav EXIF orientaci
|
||||
img = fix_orientation(img)
|
||||
print(f" Po EXIF korekci: {img.size[0]}×{img.size[1]} px")
|
||||
|
||||
# 2. Rotace dle parametru (od Claude nebo ručně)
|
||||
if rotate_ccw and rotate_ccw != 0:
|
||||
img = img.rotate(rotate_ccw, expand=True)
|
||||
print(f" Po rotaci {rotate_ccw}° CCW: {img.size[0]}×{img.size[1]} px")
|
||||
|
||||
# 2. Převeď na RGB (PDF nepodporuje RGBA/P)
|
||||
if img.mode in ("RGBA", "P", "LA"):
|
||||
img = img.convert("RGB")
|
||||
|
||||
# 3. Urči orientaci stránky podle poměru stran obrázku
|
||||
img_w, img_h = img.size
|
||||
if img_w > img_h:
|
||||
# Obrázek na šířku → stránka na šířku (A4 landscape)
|
||||
page_w_mm, page_h_mm = A4_H_MM, A4_W_MM
|
||||
print(f" Orientace stránky: na šířku (landscape)")
|
||||
else:
|
||||
# Obrázek na výšku → stránka na výšku (A4 portrait)
|
||||
page_w_mm, page_h_mm = A4_W_MM, A4_H_MM
|
||||
print(f" Orientace stránky: na výšku (portrait)")
|
||||
|
||||
# 4. Vypočti cílovou velikost s okrajem (mm → px při daném DPI)
|
||||
mm_to_px = dpi / 25.4
|
||||
max_w_px = int((page_w_mm - 2 * MARGIN_MM) * mm_to_px)
|
||||
max_h_px = int((page_h_mm - 2 * MARGIN_MM) * mm_to_px)
|
||||
|
||||
# 5. Škáluj obrázek na stránku (zachovej poměr stran)
|
||||
img.thumbnail((max_w_px, max_h_px), Image.LANCZOS)
|
||||
print(f" Výsledná velikost obrázku: {img.size[0]}×{img.size[1]} px")
|
||||
|
||||
# 6. Vlož obrázek na bílé A4 plátno
|
||||
page_w_px = int(page_w_mm * mm_to_px)
|
||||
page_h_px = int(page_h_mm * mm_to_px)
|
||||
canvas = Image.new("RGB", (page_w_px, page_h_px), "white")
|
||||
|
||||
offset_x = (page_w_px - img.size[0]) // 2
|
||||
offset_y = (page_h_px - img.size[1]) // 2
|
||||
canvas.paste(img, (offset_x, offset_y))
|
||||
|
||||
# 7. Ulož jako PDF
|
||||
canvas.save(dst, "PDF", resolution=dpi, quality=quality)
|
||||
print(f" ✓ Uloženo: {dst.name} ({dst.stat().st_size // 1024} KB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.platform == "win32":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Použití: python jpg_to_pdf.py soubor.jpg [vystup.pdf] [rotace_ccw]")
|
||||
print(" rotace_ccw: 0 / 90 / 180 / 270 (výchozí: 0)")
|
||||
sys.exit(1)
|
||||
|
||||
src = Path(sys.argv[1])
|
||||
if not src.exists():
|
||||
print(f"Soubor nenalezen: {src}")
|
||||
sys.exit(1)
|
||||
|
||||
dst = Path(sys.argv[2]) if len(sys.argv) > 2 else src.with_suffix(".pdf")
|
||||
rotate_ccw = int(sys.argv[3]) if len(sys.argv) > 3 else 0
|
||||
|
||||
print(f"Konvertuji: {src.name} → {dst.name}")
|
||||
image_to_pdf(src, dst, rotate_ccw=rotate_ccw)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Pravidla pro přejmenování souborů
|
||||
|
||||
Tato pravidla platí vždy při generování polí `poznamka` a `nazev_souboru`.
|
||||
|
||||
1. Název souboru má vždy tvar: `RODNECISLO YYYY-MM-DD Příjmení, Jméno [TYP ODBORNOST] [popis].pdf`
|
||||
- TYP je vždy buď `LZ` (lékařská zpráva / ambulantní zpráva) nebo `PZ` (propouštěcí zpráva z hospitalizace).
|
||||
- Jiné typy dokumentů (Laboratoř, CT, MRI, kolonoskopie, poukaz FT apod.) nemají TYP prefix — píší se celým názvem: `[Laboratoř]`, `[CT břicha]` atd.
|
||||
- Příklady: `[LZ chirurgie]`, `[PZ interna]`, `[Laboratoř]`, `[CT břicha]`
|
||||
|
||||
2. Když je typ dokumentu PZ (propouštěcí zpráva), umísti do druhé závorky jako první věc data hospitalizace ve tvaru `DDMMMYYYY–DDMMMYYYY` (měsíc třemi písmeny anglicky, velká, bez mezer), za pomlčkou pak popis.
|
||||
- Příklad: `[PZ interna] [12–15APR2026 srdeční selhání]`
|
||||
- Pokud je datum přijetí a propuštění ve stejném měsíci, stačí: `[12–15APR2026 ...]`
|
||||
- Pokud datum hospitalizace nelze určit, druhou závorku napiš bez datumu.
|
||||
|
||||
3. Když je dokument typ "Laboratoř", do `poznamka` uváděj POUZE hodnoty mimo normu (patologické nálezy) — hodnoty v normě vynech. Osmolalitu séra nikdy nezmiňuj, ani když je mimo normu.
|
||||
4. Pokud laboratorní výsledky obsahují glomerulární filtraci — bývá označena jako eGFR, CKD-EPI nebo CK-EPI — do `poznamka` nikdy nepiš číselnou hodnotu eGFR. Místo toho uveď pouze klasifikaci: eGFR ≥ 90 → CHRIG1, 60–89 → CHRIG2, 45–59 → CHRIG3a, 30–44 → CHRIG3b, 15–29 → CHRIG4, < 15 → CHRIG5. Klasifikaci uváděj pouze pokud je CHRIG2 nebo horší (tj. eGFR < 90) — CHRIG1 je v normě, nezmiňuj ho.
|
||||
5. Když je dokument typ "Laboratoř" a zpráva obsahuje diagnózu (dg., dg:, diagnóza), umísti ji do `nazev_souboru` jako první část druhé závorky, tedy: `[Laboratoř] [dg. XY00 - stručná poznamka]`.
|
||||
6. Zkratky a pojmenování: slovo „sono" (sonografie/ultrazvuk) piš vždy malými písmeny — `sono břicha`, `sono ŠŽ`, nikoli `SONO`. Štítnou žlázu označuj vždy zkratkou `ŠŽ`. Sonografii prsu/prsů (sono mamm., sono mamografie, sono mamma apod.) piš vždy jako `sono prsů`. Denzitometrii (DEXA, DXA, denzitometrie) piš vždy pouze jako `[DXA]` — bez prefixu LZ. Algologii piš vždy jako `[LZ léčba bolesti]`. Dermatovenerologii (dermatologie, dermatovenerologie, kožní) piš vždy jako `[LZ kožní]`. Angiologii piš vždy jako `[LZ cévní]`.
|
||||
7. V číselných hodnotách VŽDY používej desetinnou tečku, nikoli desetinnou čárku. Toto pravidlo platí absolutně pro všechna čísla v `poznamka` i `nazev_souboru` — např. `TG 4.73`, nikoli `TG 4,73`.
|
||||
|
||||
8. Rozpoznávání vzorců — sideropenická anémie: Pokud laboratorní výsledky splňují typický obraz sideropenické (železo-deficitní) anémie, přidej diagnózu jako první část druhé závorky ve tvaru `[sideropenická anémie, ...]`.
|
||||
Typický obraz (stačí kombinace několika z těchto nálezů):
|
||||
- Krevní obraz: ↓ Hb, ↓ Htk, ↓ MCV (mikrocytóza), ↓ MCH nebo ↓ MCHC (hypochromie), ↑ RDW (anisocytóza)
|
||||
- Metabolismus železa: ↓ sérové Fe (železo), ↓ ferritin, ↑ transferrin (nebo TIBC), ↓ saturace transferrinu
|
||||
- Diagnózu uveď pouze pokud je obraz dostatečně přesvědčivý (alespoň ↓ Hb + ↓ MCV nebo ↓ Fe/ferritin).
|
||||
- Příklad výsledného názvu: `[Laboratoř] [sideropenická anémie, Hb 98, MCV 71, Fe 5.2]`
|
||||
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
Standalone PDF/obrázek náhled — spouští se jako subprocess z extract_patient_info.py.
|
||||
Argumenty: preview_viewer.py <soubor> [--delete-on-close]
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit(1)
|
||||
|
||||
pdf_path = Path(sys.argv[1])
|
||||
delete_on_close = "--delete-on-close" in sys.argv
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageTk
|
||||
import fitz
|
||||
except ImportError:
|
||||
sys.exit(2)
|
||||
|
||||
suffix = pdf_path.suffix.lower()
|
||||
if suffix in (".jpg", ".jpeg", ".png"):
|
||||
pil_img = Image.open(pdf_path)
|
||||
doc = None
|
||||
else:
|
||||
doc = fitz.open(str(pdf_path))
|
||||
pil_img = None
|
||||
|
||||
root = tk.Tk()
|
||||
root.tk.call("encoding", "system", "utf-8")
|
||||
|
||||
sh = root.winfo_screenheight()
|
||||
page_count = len(doc) if doc else 1
|
||||
current = [0]
|
||||
photo_ref = [None]
|
||||
|
||||
def render(n) -> Image.Image:
|
||||
if doc is not None:
|
||||
page = doc[n]
|
||||
zoom = min(700 / page.rect.width, (sh - 150) / page.rect.height)
|
||||
pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
|
||||
return Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
|
||||
else:
|
||||
img = pil_img.copy()
|
||||
img.thumbnail((700, sh - 150), Image.LANCZOS)
|
||||
return img
|
||||
|
||||
def on_close():
|
||||
if doc:
|
||||
try:
|
||||
doc.close()
|
||||
except Exception:
|
||||
pass
|
||||
if delete_on_close:
|
||||
try:
|
||||
pdf_path.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
root.destroy()
|
||||
|
||||
root.title(pdf_path.stem)
|
||||
root.attributes("-topmost", True)
|
||||
root.resizable(False, False)
|
||||
root.protocol("WM_DELETE_WINDOW", on_close)
|
||||
|
||||
lbl_img = tk.Label(root)
|
||||
lbl_img.pack()
|
||||
|
||||
frame_nav = tk.Frame(root)
|
||||
frame_nav.pack(pady=4)
|
||||
|
||||
lbl_page = tk.Label(frame_nav, font=("Segoe UI", 9))
|
||||
lbl_page.pack(side="left", padx=10)
|
||||
|
||||
def show(n):
|
||||
current[0] = n
|
||||
img = render(n)
|
||||
photo_ref[0] = ImageTk.PhotoImage(img)
|
||||
lbl_img.config(image=photo_ref[0])
|
||||
lbl_page.config(text=f"Strana {n + 1} / {page_count}")
|
||||
btn_prev.config(state="normal" if n > 0 else "disabled")
|
||||
btn_next.config(state="normal" if n < page_count - 1 else "disabled")
|
||||
|
||||
btn_prev = tk.Button(frame_nav, text="◄ Předchozí", command=lambda: show(current[0] - 1))
|
||||
btn_prev.pack(side="left")
|
||||
btn_next = tk.Button(frame_nav, text="Další ►", command=lambda: show(current[0] + 1))
|
||||
btn_next.pack(side="left")
|
||||
|
||||
show(0)
|
||||
root.update_idletasks()
|
||||
sw = root.winfo_screenwidth()
|
||||
w = root.winfo_width()
|
||||
h = root.winfo_height()
|
||||
x = (sw - w) // 2
|
||||
root.geometry(f"+{x}+0")
|
||||
|
||||
# Zapiš geometrii do souboru pokud byl předán argument --write-geometry=<cesta>
|
||||
import json as _json
|
||||
for arg in sys.argv:
|
||||
if arg.startswith("--write-geometry="):
|
||||
geom_path = Path(arg.split("=", 1)[1])
|
||||
geom_path.write_text(_json.dumps({"x": x, "y": 0, "w": w, "h": h}), encoding="utf-8")
|
||||
break
|
||||
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Standalone dialog pro schválení / opravu názvu souboru.
|
||||
Spouští se jako subprocess z extract_patient_info.py.
|
||||
Argumenty: rename_dialog.py <json_soubor>
|
||||
JSON vstup: { "nazev": "...", "info_lines": [...] }
|
||||
JSON výstup: { "value": "..." } nebo { "value": null }
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
from ctypes import windll
|
||||
windll.shcore.SetProcessDpiAwareness(1)
|
||||
except Exception:
|
||||
pass
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(json.dumps({"value": None}))
|
||||
sys.exit(0)
|
||||
|
||||
data = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
nazev = data.get("nazev") or ""
|
||||
info_lines = data.get("info_lines") or []
|
||||
|
||||
result = {"value": None}
|
||||
|
||||
root = tk.Tk()
|
||||
root.title("Schválení názvu souboru")
|
||||
root.resizable(True, False)
|
||||
root.attributes("-topmost", True)
|
||||
root.tk.call("encoding", "system", "utf-8")
|
||||
os.environ.setdefault("TCL_ENCODING", "utf-8")
|
||||
|
||||
pad = {"padx": 12, "pady": 6}
|
||||
|
||||
frame_info = tk.Frame(root, bg="#f0f0f0", bd=1, relief="sunken")
|
||||
frame_info.pack(fill="x", **pad)
|
||||
for line in info_lines:
|
||||
color = "#b00000" if line.startswith("⚠") else "#004080" if line.startswith("✓") else "#333"
|
||||
tk.Label(frame_info, text=line, anchor="w", bg="#f0f0f0",
|
||||
fg=color, font=("Segoe UI", 10)).pack(fill="x", padx=8, pady=1)
|
||||
|
||||
tk.Label(root, text="Název souboru (bez .pdf):", anchor="w",
|
||||
font=("Segoe UI", 9, "bold")).pack(fill="x", padx=12, pady=(10, 2))
|
||||
|
||||
nazev_bez = nazev[:-4] if nazev.endswith(".pdf") else nazev
|
||||
var = tk.StringVar(value=nazev_bez)
|
||||
entry = tk.Entry(root, textvariable=var, font=("Segoe UI", 10), width=135)
|
||||
entry.pack(fill="x", padx=12, pady=(0, 10))
|
||||
entry.icursor(tk.END)
|
||||
entry.focus_set()
|
||||
|
||||
frame_btn = tk.Frame(root)
|
||||
frame_btn.pack(pady=(0, 12))
|
||||
|
||||
def schvalit(event=None):
|
||||
result["value"] = var.get().strip()
|
||||
root.destroy()
|
||||
|
||||
def preskocit(event=None):
|
||||
result["value"] = None
|
||||
root.destroy()
|
||||
|
||||
tk.Button(frame_btn, text="✓ Schválit (Enter)", command=schvalit,
|
||||
bg="#2a7a2a", fg="white", font=("Segoe UI", 10, "bold"),
|
||||
padx=16, pady=6).pack(side="left", padx=8)
|
||||
tk.Button(frame_btn, text="✗ Přeskočit (Esc)", command=preskocit,
|
||||
bg="#7a2a2a", fg="white", font=("Segoe UI", 10),
|
||||
padx=16, pady=6).pack(side="left", padx=8)
|
||||
|
||||
root.bind("<Return>", schvalit)
|
||||
root.bind("<Escape>", preskocit)
|
||||
|
||||
root.update_idletasks()
|
||||
sw = root.winfo_screenwidth()
|
||||
w = root.winfo_width()
|
||||
x = (sw - w) // 2
|
||||
|
||||
# Pozice pod preview oknem pokud byl předán argument --below-y=N
|
||||
below_y = None
|
||||
for arg in sys.argv:
|
||||
if arg.startswith("--below-y="):
|
||||
below_y = int(arg.split("=", 1)[1])
|
||||
break
|
||||
y = below_y if below_y is not None else (root.winfo_screenheight() - root.winfo_height() - 60)
|
||||
root.geometry(f"+{x}+{y}")
|
||||
|
||||
root.lift()
|
||||
root.focus_force()
|
||||
root.mainloop()
|
||||
|
||||
print(json.dumps({"value": result["value"]}, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
Jedno okno pro výběr kompresní varianty PDF.
|
||||
Nahoře tlačítka 1–N pro přepínání, tlačítko "Tohle beru" pro potvrzení.
|
||||
Argumenty: variant_picker.py <json_soubor>
|
||||
JSON vstup: [{"path": "...", "label": "150 DPI / q80", "size_kb": 139}, ...]
|
||||
JSON výstup (stdout): {"chosen": "cesta/k/souboru"}
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from PIL import Image, ImageTk
|
||||
import fitz
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit(1)
|
||||
|
||||
variants = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
chosen = {"path": None}
|
||||
docs = [fitz.open(v["path"]) for v in variants]
|
||||
current = [0]
|
||||
photo_ref = [None]
|
||||
|
||||
root = tk.Tk()
|
||||
root.tk.call("encoding", "system", "utf-8")
|
||||
root.attributes("-topmost", True)
|
||||
|
||||
sh = root.winfo_screenheight()
|
||||
sw = root.winfo_screenwidth()
|
||||
win_h = sh - 80 # odečteme taskbar + title bar
|
||||
img_h = win_h - 160
|
||||
img_w = sw // 2 # šířka okna = polovina monitoru
|
||||
|
||||
x = (sw - img_w) // 2
|
||||
root.geometry(f"{img_w}x{win_h}+{x}+0")
|
||||
root.resizable(False, False)
|
||||
|
||||
# ── Horní panel s tlačítky variant ──
|
||||
frame_top = tk.Frame(root, bg="#222")
|
||||
frame_top.pack(fill="x")
|
||||
|
||||
btn_variants = []
|
||||
current_page = [0]
|
||||
|
||||
def show(n, page_n=0):
|
||||
current[0] = n
|
||||
current_page[0] = page_n
|
||||
doc = docs[n]
|
||||
page = doc[page_n]
|
||||
zoom = min(img_w / page.rect.width, img_h / page.rect.height)
|
||||
pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
|
||||
img = Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
|
||||
photo_ref[0] = ImageTk.PhotoImage(img)
|
||||
lbl_img.config(image=photo_ref[0])
|
||||
page_count = len(doc)
|
||||
root.title(f"Varianta {n+1}: {variants[n]['label']} ({variants[n]['size_kb']} kB) — strana {page_n+1}/{page_count}")
|
||||
for i, b in enumerate(btn_variants):
|
||||
b.config(bg="#2a5a9a" if i == n else "#444")
|
||||
btn_prev_page.config(state="normal" if page_n > 0 else "disabled")
|
||||
btn_next_page.config(state="normal" if page_n < page_count - 1 else "disabled")
|
||||
|
||||
for i, v in enumerate(variants):
|
||||
b = tk.Button(
|
||||
frame_top,
|
||||
text=f"{i+1}. {v['label']}\n{v['size_kb']} kB",
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
bg="#444", fg="white",
|
||||
relief="flat", padx=8, pady=6,
|
||||
command=lambda n=i: show(n),
|
||||
)
|
||||
b.pack(side="left", padx=2, pady=4)
|
||||
btn_variants.append(b)
|
||||
|
||||
# ── Tlačítka Beru / Přeskočit — stejný styl jako varianty ──
|
||||
def beru():
|
||||
chosen["path"] = variants[current[0]]["path"]
|
||||
root.destroy()
|
||||
|
||||
def preskocit():
|
||||
root.destroy()
|
||||
|
||||
tk.Button(
|
||||
frame_top,
|
||||
text="✓ Tohle beru\n",
|
||||
command=beru,
|
||||
bg="#2a7a2a", fg="white",
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
relief="flat", padx=8, pady=6,
|
||||
).pack(side="left", padx=2, pady=4)
|
||||
|
||||
tk.Button(
|
||||
frame_top,
|
||||
text="✗ Přeskočit\n",
|
||||
command=preskocit,
|
||||
bg="#7a2a2a", fg="white",
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
relief="flat", padx=8, pady=6,
|
||||
).pack(side="left", padx=2, pady=4)
|
||||
|
||||
# ── Navigace stran — úplně vpravo ──
|
||||
btn_next_page = tk.Button(
|
||||
frame_top,
|
||||
text="Další ►\n",
|
||||
command=lambda: show(current[0], current_page[0] + 1),
|
||||
bg="#555", fg="white",
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
relief="flat", padx=8, pady=6,
|
||||
)
|
||||
btn_next_page.pack(side="right", padx=2, pady=4)
|
||||
|
||||
btn_prev_page = tk.Button(
|
||||
frame_top,
|
||||
text="◄ Před.\n",
|
||||
command=lambda: show(current[0], current_page[0] - 1),
|
||||
bg="#555", fg="white",
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
relief="flat", padx=8, pady=6,
|
||||
)
|
||||
btn_prev_page.pack(side="right", padx=2, pady=4)
|
||||
|
||||
# ── Obrázek ──
|
||||
lbl_img = tk.Label(root, bg="black")
|
||||
lbl_img.pack(fill="both", expand=True)
|
||||
|
||||
root.bind("<Key-1>", lambda e: show(0))
|
||||
root.bind("<Key-2>", lambda e: show(1))
|
||||
root.bind("<Key-3>", lambda e: show(2))
|
||||
root.bind("<Key-4>", lambda e: show(3))
|
||||
root.bind("<Key-5>", lambda e: show(4))
|
||||
root.bind("<Return>", lambda e: beru())
|
||||
root.bind("<Escape>", lambda e: preskocit())
|
||||
|
||||
show(0)
|
||||
root.mainloop()
|
||||
|
||||
for d in docs:
|
||||
try:
|
||||
d.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print(json.dumps({"chosen": chosen["path"]}, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user