Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| from reportlab.lib import colors | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.enums import TA_CENTER | |
| import datetime | |
| AUTHOR_NAME = "prof. dr hab. n. med. Rafał Obuchowicz" | |
| AUTHOR_AFFILIATION = "Luxmed Diagnostyka" | |
| def yes_no(value: bool) -> str: | |
| return "Tak" if value else "Nie" | |
| def validate_form( | |
| first_episode, recurrent_episode, | |
| no_deficit, root_deficit, multilevel_bilateral, cord_signs, | |
| conservative_none, conservative_lt6, conservative_ge6, | |
| improved, no_improvement, worsening | |
| ): | |
| warnings = [] | |
| if first_episode and recurrent_episode: | |
| warnings.append("Zaznaczono jednocześnie: 'Pierwszy epizod' i 'Nawrót / zaostrzenie przewlekłe'.") | |
| neuro_flags = [no_deficit, root_deficit, multilevel_bilateral, cord_signs] | |
| if no_deficit and any([root_deficit, multilevel_bilateral, cord_signs]): | |
| warnings.append("Zaznaczono jednocześnie 'Brak deficytu' i obecność deficytu neurologicznego.") | |
| treatment_flags = [conservative_none, conservative_lt6, conservative_ge6] | |
| if sum(treatment_flags) > 1: | |
| warnings.append("W sekcji leczenia zachowawczego wybrano więcej niż jedną sprzeczną opcję czasową.") | |
| response_flags = [improved, no_improvement, worsening] | |
| if sum(response_flags) > 1: | |
| warnings.append("W sekcji odpowiedzi na leczenie zaznaczono więcej niż jedną sprzeczną opcję.") | |
| return warnings | |
| def build_recommendation( | |
| spine_section, | |
| duration, | |
| first_episode, | |
| recurrent_episode, | |
| post_op, | |
| axial_pain, | |
| radicular_pain, | |
| paresthesia, | |
| weakness, | |
| gait_balance, | |
| sphincter, | |
| saddle, | |
| progressive_deficit, | |
| myelopathy, | |
| cancer_history, | |
| weight_loss, | |
| night_pain, | |
| fever, | |
| immunosuppression, | |
| infection_suspicion, | |
| trauma_high, | |
| trauma_low_risk, | |
| osteoporosis_steroids, | |
| no_deficit, | |
| root_deficit, | |
| multilevel_bilateral, | |
| cord_signs, | |
| conservative_none, | |
| conservative_lt6, | |
| conservative_ge6, | |
| improved, | |
| no_improvement, | |
| worsening, | |
| suspect_disc, | |
| suspect_cord, | |
| suspect_tumor, | |
| suspect_infection, | |
| qualify_surgery, | |
| qualify_injection, | |
| treatment_impact, | |
| ): | |
| reasons = [] | |
| alternatives = [] | |
| urgency = "routine" | |
| contrast = "nie" | |
| imaging = "Brak wskazań do MR na tym etapie" | |
| category = "🟢 BRAK WSKAZAŃ DO MR" | |
| clinical_path = "Leczenie zachowawcze / obserwacja" | |
| if sphincter or saddle or progressive_deficit or myelopathy or cord_signs: | |
| category = "🔴 MR PILNE" | |
| imaging = f"Pilne MR odcinka {spine_section}" | |
| clinical_path = "Pilna konsultacja specjalistyczna / tryb pilny" | |
| urgency = "pilne" | |
| reasons.append("Stwierdzono objawy alarmowe neurologiczne") | |
| if sphincter: | |
| reasons.append("Zaburzenia zwieraczy") | |
| if saddle: | |
| reasons.append("Znieczulenie okolicy krocza") | |
| if progressive_deficit: | |
| reasons.append("Postępujący deficyt neurologiczny") | |
| if myelopathy or cord_signs: | |
| reasons.append("Objawy mielopatii / uszkodzenia rdzenia") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#b91c1c", | |
| } | |
| if ( | |
| cancer_history | |
| or weight_loss | |
| or night_pain | |
| or fever | |
| or immunosuppression | |
| or infection_suspicion | |
| or suspect_tumor | |
| or suspect_infection | |
| or (post_op and (worsening or weakness or paresthesia or radicular_pain)) | |
| ): | |
| category = "🟠 MR PRZYSPIESZONE" | |
| imaging = f"MR odcinka {spine_section}" | |
| clinical_path = "Przyspieszona diagnostyka specjalistyczna" | |
| urgency = "przyspieszone" | |
| contrast = "rozważyć" | |
| reasons.append("Występują przesłanki nowotworowe, infekcyjne lub pooperacyjne") | |
| if cancer_history: | |
| reasons.append("Wywiad nowotworowy") | |
| if weight_loss: | |
| reasons.append("Niezamierzona utrata masy ciała") | |
| if night_pain: | |
| reasons.append("Ból nocny / niemechaniczny") | |
| if fever: | |
| reasons.append("Gorączka") | |
| if immunosuppression: | |
| reasons.append("Immunosupresja") | |
| if infection_suspicion: | |
| reasons.append("Podejrzenie infekcji") | |
| if post_op: | |
| reasons.append("Stan po operacji kręgosłupa") | |
| alternatives.append("Rozważyć badania laboratoryjne: CRP, OB, morfologia") | |
| if trauma_high or trauma_low_risk: | |
| alternatives.append("Jeśli współistnieje uraz: rozważyć RTG/TK struktur kostnych") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#ea580c", | |
| } | |
| if trauma_high or trauma_low_risk or osteoporosis_steroids: | |
| category = "🔵 RTG / TK NAJPIERW" | |
| imaging = f"RTG lub TK odcinka {spine_section}" | |
| clinical_path = "Najpierw diagnostyka kostna; MR warunkowo" | |
| urgency = "pilne" if trauma_high else "routine" | |
| reasons.append("Dominują wskazania urazowe / kostne") | |
| if trauma_high: | |
| reasons.append("Uraz wysokiej energii") | |
| if trauma_low_risk: | |
| reasons.append("Uraz niskoenergetyczny z ryzykiem") | |
| if osteoporosis_steroids: | |
| reasons.append("Osteoporoza / przewlekła steroidoterapia") | |
| alternatives.append("MR rozważyć przy deficycie neurologicznym lub podejrzeniu uszkodzeń tkanek miękkich") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#2563eb", | |
| } | |
| radicular_pattern = radicular_pain or paresthesia or weakness or root_deficit or multilevel_bilateral | |
| if ( | |
| radicular_pattern | |
| and conservative_ge6 | |
| and (no_improvement or worsening) | |
| and treatment_impact == "TAK" | |
| ): | |
| category = "🟡 MR PLANOWE" | |
| imaging = f"MR odcinka {spine_section} bez kontrastu" | |
| clinical_path = "Planowa diagnostyka obrazowa" | |
| urgency = "planowe" | |
| reasons.append("Objawy korzeniowe / neurologiczne utrzymują się mimo leczenia") | |
| reasons.append("Leczenie zachowawcze ≥ 6 tygodni") | |
| if no_improvement: | |
| reasons.append("Brak poprawy") | |
| if worsening: | |
| reasons.append("Pogorszenie") | |
| reasons.append("Wynik badania może wpłynąć na leczenie") | |
| if qualify_surgery: | |
| reasons.append("Pacjent potencjalnie kwalifikowany do zabiegu") | |
| if qualify_injection: | |
| reasons.append("Pacjent potencjalnie kwalifikowany do blokady / iniekcji") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#ca8a04", | |
| } | |
| if ( | |
| axial_pain | |
| and not radicular_pattern | |
| and not weakness | |
| and not gait_balance | |
| and not conservative_ge6 | |
| and treatment_impact != "TAK" | |
| ): | |
| category = "🟢 BRAK WSKAZAŃ DO MR" | |
| imaging = "Brak wskazań do MR na tym etapie" | |
| clinical_path = "Leczenie zachowawcze / obserwacja / kontrola" | |
| urgency = "routine" | |
| reasons.append("Dominują objawy bólu osiowego bez czerwonych flag") | |
| reasons.append("Brak przesłanek do pilnej diagnostyki obrazowej") | |
| alternatives.append("Kontynuacja leczenia zachowawczego") | |
| alternatives.append("Rozważyć fizjoterapię / farmakoterapię / kontrolę kliniczną") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#15803d", | |
| } | |
| category = "🟡 WYMAGA DOPRECYZOWANIA" | |
| imaging = "Rozważyć MR po uzupełnieniu oceny klinicznej" | |
| clinical_path = "Uzupełnić wywiad / badanie / odpowiedź na leczenie" | |
| urgency = "routine" | |
| reasons.append("Dane nie są jednoznaczne dla automatycznej kwalifikacji") | |
| if treatment_impact == "NIEPEWNE": | |
| reasons.append("Niepewny wpływ wyniku MR na decyzję terapeutyczną") | |
| alternatives.append("Zweryfikować czas leczenia zachowawczego") | |
| alternatives.append("Uściślić deficyty neurologiczne i pytanie kliniczne") | |
| return { | |
| "category": category, | |
| "imaging": imaging, | |
| "clinical_path": clinical_path, | |
| "urgency": urgency, | |
| "contrast": contrast, | |
| "reasons": reasons, | |
| "alternatives": alternatives, | |
| "color": "#a16207", | |
| } | |
| def generate_pdf(data, result, warnings): | |
| filename = "raport_mr_kregoslup_pro_plus.pdf" | |
| doc = SimpleDocTemplate(filename, pagesize=A4, rightMargin=36, leftMargin=36, topMargin=36, bottomMargin=36) | |
| styles = getSampleStyleSheet() | |
| title_style = ParagraphStyle( | |
| "CustomTitle", | |
| parent=styles["Title"], | |
| alignment=TA_CENTER, | |
| fontSize=18, | |
| spaceAfter=10 | |
| ) | |
| subtitle_style = ParagraphStyle( | |
| "CustomSubtitle", | |
| parent=styles["Normal"], | |
| alignment=TA_CENTER, | |
| fontSize=10, | |
| textColor=colors.grey | |
| ) | |
| story = [] | |
| story.append(Paragraph("Raport kwalifikacji do badania MR kręgosłupa", title_style)) | |
| story.append(Paragraph("Clinical Decision Support – PRO+", subtitle_style)) | |
| story.append(Spacer(1, 8)) | |
| story.append(Paragraph(f"Autor narzędzia: <b>{AUTHOR_NAME}</b>", styles["Normal"])) | |
| story.append(Paragraph(f"Afiliacja: <b>{AUTHOR_AFFILIATION}</b>", styles["Normal"])) | |
| story.append(Paragraph(f"Data wygenerowania: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}", styles["Normal"])) | |
| story.append(Spacer(1, 14)) | |
| summary_table = Table( | |
| [ | |
| ["Rekomendacja", result["category"]], | |
| ["Sugerowane badanie", result["imaging"]], | |
| ["Tryb", result["urgency"]], | |
| ["Kontrast", result["contrast"]], | |
| ["Ścieżka kliniczna", result["clinical_path"]], | |
| ], | |
| colWidths=[160, 350], | |
| ) | |
| summary_table.setStyle( | |
| TableStyle( | |
| [ | |
| ("BACKGROUND", (0, 0), (0, -1), colors.HexColor("#EAEAEA")), | |
| ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), | |
| ("VALIGN", (0, 0), (-1, -1), "TOP"), | |
| ("FONTSIZE", (0, 0), (-1, -1), 10), | |
| ("PADDING", (0, 0), (-1, -1), 6), | |
| ] | |
| ) | |
| ) | |
| story.append(summary_table) | |
| story.append(Spacer(1, 14)) | |
| if warnings: | |
| story.append(Paragraph("<b>Ostrzeżenia walidacyjne:</b>", styles["Heading3"])) | |
| for w in warnings: | |
| story.append(Paragraph(f"• {w}", styles["Normal"])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>Uzasadnienie:</b>", styles["Heading3"])) | |
| for r in result["reasons"]: | |
| story.append(Paragraph(f"• {r}", styles["Normal"])) | |
| story.append(Spacer(1, 10)) | |
| if result["alternatives"]: | |
| story.append(Paragraph("<b>Alternatywy / dalsze kroki:</b>", styles["Heading3"])) | |
| for a in result["alternatives"]: | |
| story.append(Paragraph(f"• {a}", styles["Normal"])) | |
| story.append(Spacer(1, 10)) | |
| details = [ | |
| ["Odcinek", data["spine_section"]], | |
| ["Czas objawów", data["duration"]], | |
| ["Pierwszy epizod", data["first_episode"]], | |
| ["Nawrót", data["recurrent_episode"]], | |
| ["Stan po operacji", data["post_op"]], | |
| ["Ból osiowy", data["axial_pain"]], | |
| ["Ból promieniujący", data["radicular_pain"]], | |
| ["Parestezje", data["paresthesia"]], | |
| ["Osłabienie siły", data["weakness"]], | |
| ["Zaburzenia chodu / równowagi", data["gait_balance"]], | |
| ["Zaburzenia zwieraczy", data["sphincter"]], | |
| ["Znieczulenie krocza", data["saddle"]], | |
| ["Postępujący deficyt", data["progressive_deficit"]], | |
| ["Objawy mielopatii", data["myelopathy"]], | |
| ["Wywiad nowotworowy", data["cancer_history"]], | |
| ["Utrata masy ciała", data["weight_loss"]], | |
| ["Ból nocny", data["night_pain"]], | |
| ["Gorączka", data["fever"]], | |
| ["Immunosupresja", data["immunosuppression"]], | |
| ["Podejrzenie infekcji", data["infection_suspicion"]], | |
| ["Uraz wysokiej energii", data["trauma_high"]], | |
| ["Uraz niskoenergetyczny + ryzyko", data["trauma_low_risk"]], | |
| ["Osteoporoza / steroidy", data["osteoporosis_steroids"]], | |
| ["Brak deficytu", data["no_deficit"]], | |
| ["Deficyt korzeniowy", data["root_deficit"]], | |
| ["Deficyt wielopoziomowy / obustronny", data["multilevel_bilateral"]], | |
| ["Objawy rdzeniowe", data["cord_signs"]], | |
| ["Brak leczenia zachowawczego", data["conservative_none"]], | |
| ["Leczenie < 6 tygodni", data["conservative_lt6"]], | |
| ["Leczenie ≥ 6 tygodni", data["conservative_ge6"]], | |
| ["Poprawa", data["improved"]], | |
| ["Brak poprawy", data["no_improvement"]], | |
| ["Pogorszenie", data["worsening"]], | |
| ["Podejrzenie dyskopatii", data["suspect_disc"]], | |
| ["Podejrzenie ucisku rdzenia", data["suspect_cord"]], | |
| ["Podejrzenie nowotworu", data["suspect_tumor"]], | |
| ["Podejrzenie infekcji", data["suspect_infection"]], | |
| ["Kwalifikacja do zabiegu", data["qualify_surgery"]], | |
| ["Kwalifikacja do blokady", data["qualify_injection"]], | |
| ["Czy wynik MR zmieni leczenie?", data["treatment_impact"]], | |
| ] | |
| details_table = Table(details, colWidths=[220, 290]) | |
| details_table.setStyle( | |
| TableStyle( | |
| [ | |
| ("BACKGROUND", (0, 0), (0, -1), colors.HexColor("#F3F4F6")), | |
| ("GRID", (0, 0), (-1, -1), 0.4, colors.lightgrey), | |
| ("VALIGN", (0, 0), (-1, -1), "TOP"), | |
| ("FONTSIZE", (0, 0), (-1, -1), 9), | |
| ("PADDING", (0, 0), (-1, -1), 5), | |
| ] | |
| ) | |
| ) | |
| story.append(Paragraph("<b>Dane formularza:</b>", styles["Heading3"])) | |
| story.append(details_table) | |
| story.append(Spacer(1, 14)) | |
| story.append(Paragraph("Uwaga: narzędzie ma charakter wspomagający i nie zastępuje oceny lekarza.", styles["Italic"])) | |
| doc.build(story) | |
| return filename | |
| def render_html_banner(category, imaging, color): | |
| return f""" | |
| <div style="padding:16px;border-radius:14px;background:{color};color:white; | |
| font-size:18px;font-weight:700;text-align:center;margin:8px 0;"> | |
| {category}<br> | |
| <span style="font-size:14px;font-weight:500;">{imaging}</span> | |
| </div> | |
| """ | |
| def run_app( | |
| spine_section, | |
| duration, | |
| first_episode, | |
| recurrent_episode, | |
| post_op, | |
| axial_pain, | |
| radicular_pain, | |
| paresthesia, | |
| weakness, | |
| gait_balance, | |
| sphincter, | |
| saddle, | |
| progressive_deficit, | |
| myelopathy, | |
| cancer_history, | |
| weight_loss, | |
| night_pain, | |
| fever, | |
| immunosuppression, | |
| infection_suspicion, | |
| trauma_high, | |
| trauma_low_risk, | |
| osteoporosis_steroids, | |
| no_deficit, | |
| root_deficit, | |
| multilevel_bilateral, | |
| cord_signs, | |
| conservative_none, | |
| conservative_lt6, | |
| conservative_ge6, | |
| improved, | |
| no_improvement, | |
| worsening, | |
| suspect_disc, | |
| suspect_cord, | |
| suspect_tumor, | |
| suspect_infection, | |
| qualify_surgery, | |
| qualify_injection, | |
| treatment_impact, | |
| ): | |
| warnings = validate_form( | |
| first_episode, recurrent_episode, | |
| no_deficit, root_deficit, multilevel_bilateral, cord_signs, | |
| conservative_none, conservative_lt6, conservative_ge6, | |
| improved, no_improvement, worsening | |
| ) | |
| result = build_recommendation( | |
| spine_section, | |
| duration, | |
| first_episode, | |
| recurrent_episode, | |
| post_op, | |
| axial_pain, | |
| radicular_pain, | |
| paresthesia, | |
| weakness, | |
| gait_balance, | |
| sphincter, | |
| saddle, | |
| progressive_deficit, | |
| myelopathy, | |
| cancer_history, | |
| weight_loss, | |
| night_pain, | |
| fever, | |
| immunosuppression, | |
| infection_suspicion, | |
| trauma_high, | |
| trauma_low_risk, | |
| osteoporosis_steroids, | |
| no_deficit, | |
| root_deficit, | |
| multilevel_bilateral, | |
| cord_signs, | |
| conservative_none, | |
| conservative_lt6, | |
| conservative_ge6, | |
| improved, | |
| no_improvement, | |
| worsening, | |
| suspect_disc, | |
| suspect_cord, | |
| suspect_tumor, | |
| suspect_infection, | |
| qualify_surgery, | |
| qualify_injection, | |
| treatment_impact, | |
| ) | |
| reasons_text = "\n".join([f"- {x}" for x in result["reasons"]]) if result["reasons"] else "-" | |
| alt_text = "\n".join([f"- {x}" for x in result["alternatives"]]) if result["alternatives"] else "-" | |
| warnings_text = "\n".join([f"- {x}" for x in warnings]) if warnings else "Brak ostrzeżeń walidacyjnych." | |
| form_data = { | |
| "spine_section": spine_section, | |
| "duration": duration, | |
| "first_episode": yes_no(first_episode), | |
| "recurrent_episode": yes_no(recurrent_episode), | |
| "post_op": yes_no(post_op), | |
| "axial_pain": yes_no(axial_pain), | |
| "radicular_pain": yes_no(radicular_pain), | |
| "paresthesia": yes_no(paresthesia), | |
| "weakness": yes_no(weakness), | |
| "gait_balance": yes_no(gait_balance), | |
| "sphincter": yes_no(sphincter), | |
| "saddle": yes_no(saddle), | |
| "progressive_deficit": yes_no(progressive_deficit), | |
| "myelopathy": yes_no(myelopathy), | |
| "cancer_history": yes_no(cancer_history), | |
| "weight_loss": yes_no(weight_loss), | |
| "night_pain": yes_no(night_pain), | |
| "fever": yes_no(fever), | |
| "immunosuppression": yes_no(immunosuppression), | |
| "infection_suspicion": yes_no(infection_suspicion), | |
| "trauma_high": yes_no(trauma_high), | |
| "trauma_low_risk": yes_no(trauma_low_risk), | |
| "osteoporosis_steroids": yes_no(osteoporosis_steroids), | |
| "no_deficit": yes_no(no_deficit), | |
| "root_deficit": yes_no(root_deficit), | |
| "multilevel_bilateral": yes_no(multilevel_bilateral), | |
| "cord_signs": yes_no(cord_signs), | |
| "conservative_none": yes_no(conservative_none), | |
| "conservative_lt6": yes_no(conservative_lt6), | |
| "conservative_ge6": yes_no(conservative_ge6), | |
| "improved": yes_no(improved), | |
| "no_improvement": yes_no(no_improvement), | |
| "worsening": yes_no(worsening), | |
| "suspect_disc": yes_no(suspect_disc), | |
| "suspect_cord": yes_no(suspect_cord), | |
| "suspect_tumor": yes_no(suspect_tumor), | |
| "suspect_infection": yes_no(suspect_infection), | |
| "qualify_surgery": yes_no(qualify_surgery), | |
| "qualify_injection": yes_no(qualify_injection), | |
| "treatment_impact": treatment_impact, | |
| } | |
| pdf_file = generate_pdf(form_data, result, warnings) | |
| banner_html = render_html_banner(result["category"], result["imaging"], result["color"]) | |
| return ( | |
| banner_html, | |
| result["category"], | |
| result["imaging"], | |
| result["clinical_path"], | |
| result["urgency"], | |
| result["contrast"], | |
| reasons_text, | |
| alt_text, | |
| warnings_text, | |
| pdf_file, | |
| ) | |
| with gr.Blocks(theme=gr.themes.Soft(), title="MR Spine CDS PRO+") as app: | |
| gr.Markdown( | |
| f""" | |
| # MR Spine Clinical Decision Support – PRO+ | |
| **Autor:** {AUTHOR_NAME} | |
| **Afiliacja:** {AUTHOR_AFFILIATION} | |
| Ustrukturyzowany formularz kwalifikacji do badania MR kręgosłupa. | |
| **Uwaga:** narzędzie wspomagające decyzję kliniczną, nie zastępuje oceny lekarza. | |
| """ | |
| ) | |
| with gr.Group(): | |
| gr.Markdown("## A. Identyfikacja problemu") | |
| with gr.Row(): | |
| spine_section = gr.Radio( | |
| ["szyjnego", "piersiowego", "lędźwiowego"], | |
| label="Odcinek kręgosłupa", | |
| value="lędźwiowego", | |
| ) | |
| duration = gr.Radio( | |
| ["< 2 tygodni", "2–6 tygodni", "6–12 tygodni", "> 12 tygodni"], | |
| label="Czas trwania objawów", | |
| value="2–6 tygodni", | |
| ) | |
| with gr.Row(): | |
| first_episode = gr.Checkbox(label="Pierwszy epizod") | |
| recurrent_episode = gr.Checkbox(label="Nawrót / zaostrzenie przewlekłe") | |
| post_op = gr.Checkbox(label="Pacjent po operacji kręgosłupa") | |
| with gr.Group(): | |
| gr.Markdown("## B. Charakter objawów") | |
| with gr.Row(): | |
| axial_pain = gr.Checkbox(label="Ból miejscowy (osiowy)") | |
| radicular_pain = gr.Checkbox(label="Ból promieniujący (radikulopatia)") | |
| paresthesia = gr.Checkbox(label="Parestezje / drętwienie") | |
| weakness = gr.Checkbox(label="Osłabienie siły mięśniowej") | |
| gait_balance = gr.Checkbox(label="Zaburzenia chodu / równowagi") | |
| with gr.Group(): | |
| gr.Markdown("## C. Red flags") | |
| with gr.Row(): | |
| sphincter = gr.Checkbox(label="Zaburzenia zwieraczy") | |
| saddle = gr.Checkbox(label="Znieczulenie krocza") | |
| progressive_deficit = gr.Checkbox(label="Postępujący niedowład") | |
| myelopathy = gr.Checkbox(label="Objawy mielopatii") | |
| with gr.Row(): | |
| cancer_history = gr.Checkbox(label="Wywiad nowotworowy") | |
| weight_loss = gr.Checkbox(label="Utrata masy ciała") | |
| night_pain = gr.Checkbox(label="Ból nocny / niemechaniczny") | |
| with gr.Row(): | |
| fever = gr.Checkbox(label="Gorączka") | |
| immunosuppression = gr.Checkbox(label="Immunosupresja") | |
| infection_suspicion = gr.Checkbox(label="Podejrzenie infekcji") | |
| with gr.Row(): | |
| trauma_high = gr.Checkbox(label="Uraz wysokiej energii") | |
| trauma_low_risk = gr.Checkbox(label="Uraz niskoenergetyczny + ryzyko") | |
| osteoporosis_steroids = gr.Checkbox(label="Osteoporoza / przewlekłe steroidy") | |
| with gr.Group(): | |
| gr.Markdown("## D. Badanie neurologiczne") | |
| with gr.Row(): | |
| no_deficit = gr.Checkbox(label="Brak deficytu") | |
| root_deficit = gr.Checkbox(label="Deficyt korzeniowy") | |
| multilevel_bilateral = gr.Checkbox(label="Deficyt wielopoziomowy / obustronny") | |
| cord_signs = gr.Checkbox(label="Objawy rdzeniowe") | |
| with gr.Group(): | |
| gr.Markdown("## E. Dotychczasowe leczenie") | |
| with gr.Row(): | |
| conservative_none = gr.Checkbox(label="Brak leczenia zachowawczego") | |
| conservative_lt6 = gr.Checkbox(label="Leczenie < 6 tygodni") | |
| conservative_ge6 = gr.Checkbox(label="Leczenie ≥ 6 tygodni") | |
| with gr.Row(): | |
| improved = gr.Checkbox(label="Poprawa") | |
| no_improvement = gr.Checkbox(label="Brak poprawy") | |
| worsening = gr.Checkbox(label="Pogorszenie") | |
| with gr.Group(): | |
| gr.Markdown("## F. Pytanie kliniczne") | |
| with gr.Row(): | |
| suspect_disc = gr.Checkbox(label="Podejrzenie dyskopatii") | |
| suspect_cord = gr.Checkbox(label="Podejrzenie ucisku rdzenia") | |
| suspect_tumor = gr.Checkbox(label="Podejrzenie nowotworu") | |
| suspect_infection = gr.Checkbox(label="Podejrzenie infekcji") | |
| with gr.Row(): | |
| qualify_surgery = gr.Checkbox(label="Kwalifikacja do zabiegu") | |
| qualify_injection = gr.Checkbox(label="Kwalifikacja do blokady / iniekcji") | |
| treatment_impact = gr.Radio( | |
| ["TAK", "NIE", "NIEPEWNE"], | |
| label="Czy wynik MR zmieni leczenie?", | |
| value="NIEPEWNE", | |
| ) | |
| with gr.Row(): | |
| run_button = gr.Button("Generuj rekomendację", variant="primary") | |
| clear_button = gr.Button("Wyczyść formularz") | |
| gr.Markdown("## Wynik") | |
| out_banner = gr.HTML() | |
| out_category = gr.Textbox(label="Kategoria rekomendacji") | |
| out_imaging = gr.Textbox(label="Sugerowane badanie") | |
| out_path = gr.Textbox(label="Ścieżka kliniczna") | |
| out_urgency = gr.Textbox(label="Tryb") | |
| out_contrast = gr.Textbox(label="Kontrast") | |
| out_reasons = gr.Textbox(label="Uzasadnienie", lines=8) | |
| out_alternatives = gr.Textbox(label="Alternatywy / dalsze kroki", lines=5) | |
| out_warnings = gr.Textbox(label="Ostrzeżenia walidacyjne", lines=4) | |
| out_pdf = gr.File(label="Pobierz raport PDF") | |
| inputs = [ | |
| spine_section, | |
| duration, | |
| first_episode, | |
| recurrent_episode, | |
| post_op, | |
| axial_pain, | |
| radicular_pain, | |
| paresthesia, | |
| weakness, | |
| gait_balance, | |
| sphincter, | |
| saddle, | |
| progressive_deficit, | |
| myelopathy, | |
| cancer_history, | |
| weight_loss, | |
| night_pain, | |
| fever, | |
| immunosuppression, | |
| infection_suspicion, | |
| trauma_high, | |
| trauma_low_risk, | |
| osteoporosis_steroids, | |
| no_deficit, | |
| root_deficit, | |
| multilevel_bilateral, | |
| cord_signs, | |
| conservative_none, | |
| conservative_lt6, | |
| conservative_ge6, | |
| improved, | |
| no_improvement, | |
| worsening, | |
| suspect_disc, | |
| suspect_cord, | |
| suspect_tumor, | |
| suspect_infection, | |
| qualify_surgery, | |
| qualify_injection, | |
| treatment_impact, | |
| ] | |
| outputs = [ | |
| out_banner, | |
| out_category, | |
| out_imaging, | |
| out_path, | |
| out_urgency, | |
| out_contrast, | |
| out_reasons, | |
| out_alternatives, | |
| out_warnings, | |
| out_pdf, | |
| ] | |
| run_button.click(fn=run_app, inputs=inputs, outputs=outputs) | |
| clear_button.click( | |
| fn=lambda: ( | |
| "lędźwiowego", | |
| "2–6 tygodni", | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| False, | |
| "NIEPEWNE", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| "", | |
| None, | |
| ), | |
| inputs=[], | |
| outputs=inputs + outputs, | |
| ) | |
| app.launch() |