| | """ |
| | Nursing Case Study Builder |
| | Streamlit app β Hugging Face Spaces (free CPU tier) |
| | """ |
| |
|
| | import streamlit as st |
| | from cases.bank import ( |
| | get_all_cases, get_categories, get_by_category, get_by_difficulty, |
| | get_by_id, search_cases, get_difficulties, |
| | ) |
| |
|
| | |
| | |
| | |
| | st.set_page_config( |
| | page_title="Nursing Case Studies β Student Nurses", |
| | page_icon="π₯", |
| | layout="wide", |
| | initial_sidebar_state="expanded", |
| | ) |
| |
|
| | |
| | |
| | |
| | st.markdown(""" |
| | <style> |
| | .case-card { |
| | background:#f8fafc; border:1px solid #d0dae8; |
| | border-radius:10px; padding:1.2rem 1.4rem; margin-bottom:1rem; |
| | } |
| | .patient-banner { |
| | background:#e3f2fd; border-left:5px solid #1565c0; |
| | padding:0.8rem 1.2rem; border-radius:4px; margin-bottom:0.8rem; |
| | } |
| | .warning-banner { |
| | background:#fff3e0; border-left:5px solid #e65100; |
| | padding:0.8rem 1.2rem; border-radius:4px; margin-bottom:0.8rem; |
| | } |
| | .critical-banner { |
| | background:#fce4ec; border-left:5px solid #c62828; |
| | padding:0.8rem 1.2rem; border-radius:4px; margin-bottom:0.8rem; |
| | } |
| | .success-banner { |
| | background:#e8f5e9; border-left:5px solid #2e7d32; |
| | padding:0.8rem 1.2rem; border-radius:4px; margin-bottom:0.8rem; |
| | } |
| | .info-banner { |
| | background:#e8eaf6; border-left:5px solid #3949ab; |
| | padding:0.8rem 1.2rem; border-radius:4px; margin-bottom:0.8rem; |
| | } |
| | .badge-beginner { background:#e8f5e9; color:#2e7d32; padding:3px 10px; |
| | border-radius:12px; font-size:0.78em; font-weight:700; } |
| | .badge-intermediate { background:#fff8e1; color:#f57f17; padding:3px 10px; |
| | border-radius:12px; font-size:0.78em; font-weight:700; } |
| | .badge-advanced { background:#fce4ec; color:#c62828; padding:3px 10px; |
| | border-radius:12px; font-size:0.78em; font-weight:700; } |
| | .vital-box { |
| | background:#f5f5f5; border-radius:8px; padding:0.6rem 1rem; |
| | text-align:center; margin-bottom:0.4rem; |
| | } |
| | .nd-card { |
| | background:#fafafa; border:1px solid #e0e0e0; |
| | border-radius:8px; padding:1rem 1.2rem; margin-bottom:0.8rem; |
| | } |
| | </style> |
| | """, unsafe_allow_html=True) |
| |
|
| |
|
| | |
| | |
| | |
| | DIFF_COLOURS = {"Beginner": "#2e7d32", "Intermediate": "#f57f17", "Advanced": "#c62828"} |
| | CAT_ICONS = { |
| | "Cardiovascular": "β€οΈ", |
| | "Respiratory": "π«", |
| | "Endocrine": "π©Έ", |
| | "Neurological": "π§ ", |
| | "Multi-system / Infectious": "π¦ ", |
| | "Musculoskeletal / Surgical": "π¦΄", |
| | "Maternal / Obstetric": "π€°", |
| | "Paediatric": "πΆ", |
| | "Mental Health / Toxicology": "π", |
| | "Renal": "π§", |
| | } |
| |
|
| |
|
| | def diff_badge(difficulty: str) -> str: |
| | cls = f"badge-{difficulty.lower()}" |
| | return f'<span class="{cls}">{difficulty}</span>' |
| |
|
| |
|
| | def cat_icon(category: str) -> str: |
| | return CAT_ICONS.get(category, "π") |
| |
|
| |
|
| | def init_state(): |
| | if "selected_case_id" not in st.session_state: |
| | st.session_state.selected_case_id = None |
| | if "adpie_answers" not in st.session_state: |
| | st.session_state.adpie_answers = {} |
| | if "quiz_answers" not in st.session_state: |
| | st.session_state.quiz_answers = {} |
| | if "quiz_submitted" not in st.session_state: |
| | st.session_state.quiz_submitted = False |
| | if "careplan_items" not in st.session_state: |
| | st.session_state.careplan_items = {} |
| |
|
| |
|
| | init_state() |
| |
|
| | |
| | |
| | |
| | with st.sidebar: |
| | st.markdown("## π₯ Case Studies") |
| | st.divider() |
| |
|
| | all_cases = get_all_cases() |
| | categories = get_categories() |
| | difficulties = get_difficulties() |
| |
|
| | st.markdown("**Quick Stats**") |
| | st.markdown(f"- π {len(all_cases)} clinical cases") |
| | st.markdown(f"- π·οΈ {len(categories)} body systems") |
| | diff_counts = {d: len(get_by_difficulty(d)) for d in difficulties} |
| | for d, n in diff_counts.items(): |
| | colour = DIFF_COLOURS[d] |
| | st.markdown(f'- <span style="color:{colour}">⬀</span> {d}: {n} cases', unsafe_allow_html=True) |
| |
|
| | st.divider() |
| | st.markdown("**Cases by System**") |
| | for cat in categories: |
| | icon = cat_icon(cat) |
| | n = len(get_by_category(cat)) |
| | st.markdown(f"{icon} **{cat}** β {n}") |
| |
|
| | st.divider() |
| | st.markdown("**ADPIE Framework**") |
| | st.markdown(""" |
| | - **A**ssessment |
| | - **D**iagnosis |
| | - **P**lanning |
| | - **I**mplementation |
| | - **E**valuation |
| | """) |
| | st.divider() |
| | st.caption( |
| | "Part of the [Nursing Citizen Development](https://huggingface.co/NurseCitizenDeveloper) suite" |
| | ) |
| |
|
| | |
| | |
| | |
| | st.title("π₯ Nursing Case Studies") |
| | st.caption( |
| | "10 clinical scenarios across major body systems Β· ADPIE Framework Β· " |
| | "Nursing Diagnoses Β· Care Planning Β· For educational purposes only" |
| | ) |
| |
|
| | |
| | |
| | |
| | tab_lib, tab_case, tab_adpie, tab_quiz, tab_plan = st.tabs([ |
| | "π Case Library", |
| | "π Full Case", |
| | "π§ ADPIE Reasoning", |
| | "β Quiz Questions", |
| | "π Care Plan", |
| | ]) |
| |
|
| |
|
| | |
| | with tab_lib: |
| | st.subheader("π Clinical Case Library") |
| | st.caption("Browse 10 evidence-based patient scenarios. Click any case to open it.") |
| |
|
| | col_search, col_diff, col_cat = st.columns([3, 2, 2]) |
| | with col_search: |
| | lib_query = st.text_input("π Search cases", placeholder="e.g. sepsis, chest pain, stroke") |
| | with col_diff: |
| | diff_filter = st.selectbox("Difficulty", ["All"] + difficulties) |
| | with col_cat: |
| | cat_filter = st.selectbox("Body system", ["All"] + categories) |
| |
|
| | st.divider() |
| |
|
| | |
| | if lib_query.strip(): |
| | display_cases = search_cases(lib_query) |
| | if not display_cases: |
| | st.info(f"No cases found for '{lib_query}'.") |
| | elif diff_filter != "All" and cat_filter != "All": |
| | display_cases = [c for c in get_by_category(cat_filter) |
| | if c["difficulty"] == diff_filter] |
| | elif diff_filter != "All": |
| | display_cases = get_by_difficulty(diff_filter) |
| | elif cat_filter != "All": |
| | display_cases = get_by_category(cat_filter) |
| | else: |
| | display_cases = all_cases |
| |
|
| | st.markdown(f"**Showing {len(display_cases)} case(s)**") |
| | st.markdown("") |
| |
|
| | for case in display_cases: |
| | icon = cat_icon(case["category"]) |
| | col_a, col_b = st.columns([5, 1]) |
| | with col_a: |
| | st.markdown( |
| | f'<div class="case-card">' |
| | f'<b>{icon} {case["title"]}</b> ' |
| | f'{diff_badge(case["difficulty"])}<br/>' |
| | f'<small>π·οΈ {case["category"]} Β· ' |
| | f'π€ {case["patient"]["name"]}, {case["patient"]["age"]}yo Β· ' |
| | f'π {", ".join(case["tags"][:4])}</small>' |
| | f'</div>', |
| | unsafe_allow_html=True |
| | ) |
| | with col_b: |
| | if st.button("Open β", key=f"open_{case['id']}", use_container_width=True): |
| | st.session_state.selected_case_id = case["id"] |
| | st.session_state.quiz_answers = {} |
| | st.session_state.quiz_submitted = False |
| | st.session_state.adpie_answers = {} |
| | st.success(f"β
Case loaded: **{case['title']}** β navigate to other tabs.") |
| |
|
| |
|
| | |
| | with tab_case: |
| | st.subheader("π Full Case Study") |
| |
|
| | if not st.session_state.selected_case_id: |
| | st.info("π‘ Select a case from the **Case Library** tab to begin.") |
| | else: |
| | case = get_by_id(st.session_state.selected_case_id) |
| | if not case: |
| | st.error("Case not found.") |
| | else: |
| | p = case["patient"] |
| |
|
| | |
| | icon = cat_icon(case["category"]) |
| | st.markdown( |
| | f'<div class="patient-banner">' |
| | f'<h3 style="margin:0">{icon} {case["title"]}</h3>' |
| | f'<span>{diff_badge(case["difficulty"])}</span> ' |
| | f'<small>{case["category"]}</small>' |
| | f'</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | |
| | with st.expander("π― Learning Objectives", expanded=False): |
| | for lo in case["learning_objectives"]: |
| | st.markdown(f"β’ {lo}") |
| |
|
| | st.divider() |
| |
|
| | |
| | col1, col2 = st.columns(2) |
| | with col1: |
| | st.markdown("### π€ Patient Profile") |
| | st.markdown(f"**Name:** {p['name']}") |
| | st.markdown(f"**Age / Gender:** {p['age']} years Β· {p['gender']}") |
| | st.markdown(f"**Weight / Height:** {p['weight_kg']} kg Β· {p['height_cm']} cm") |
| | st.markdown(f"**Allergies:** {', '.join(p['allergies'])}") |
| |
|
| | with col2: |
| | st.markdown("### π Medical History") |
| | st.markdown("**Past Medical History:**") |
| | for h in p["pmhx"]: |
| | st.markdown(f"β’ {h}") |
| | st.markdown(f"**Medications:** {', '.join(p['medications'])}") |
| | st.markdown(f"**Social:** {p['social']}") |
| | st.markdown(f"**Family History:** {p.get('family_hx', 'Nil relevant')}") |
| |
|
| | st.divider() |
| |
|
| | |
| | st.markdown("### π¨ Presentation") |
| | st.markdown( |
| | f'<div class="warning-banner">π {case["presentation"]}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | |
| | st.markdown("### π Vital Signs") |
| | vitals = case["vitals"] |
| | v_keys = list(vitals.keys()) |
| | cols = st.columns(min(len(v_keys), 5)) |
| | for i, key in enumerate(v_keys): |
| | with cols[i % len(cols)]: |
| | st.markdown( |
| | f'<div class="vital-box"><small>{key}</small><br/>' |
| | f'<b>{vitals[key]}</b></div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | |
| | st.markdown("### π©Ί Physical Examination") |
| | st.markdown(case["physical_exam"]) |
| |
|
| | st.divider() |
| |
|
| | |
| | st.markdown("### π¬ Investigations") |
| | inv = case["investigations"] |
| | inv_keys = list(inv.keys()) |
| | col_a, col_b = st.columns(2) |
| | for i, key in enumerate(inv_keys): |
| | col = col_a if i % 2 == 0 else col_b |
| | with col: |
| | val = inv[key] |
| | flag = "π΄ " if ("ββ" in val or "CRITICAL" in val or "severe" in val.lower()) else "" |
| | st.markdown(f"**{key}:** {flag}{val}") |
| |
|
| | st.divider() |
| |
|
| | |
| | st.markdown("### π₯ Medical Diagnosis") |
| | st.markdown( |
| | f'<div class="critical-banner">βοΈ <b>{case["medical_diagnosis"]}</b></div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | |
| | st.markdown("### π Nursing Priorities") |
| | for i, priority in enumerate(case["nursing_priorities"], 1): |
| | colour = "#c62828" if i <= 3 else "#e65100" if i <= 6 else "#2e7d32" |
| | st.markdown( |
| | f'<div style="padding:4px 0"><span style="color:{colour};font-weight:700">' |
| | f'{i}.</span> {priority}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| |
|
| | |
| | with tab_adpie: |
| | st.subheader("π§ ADPIE Clinical Reasoning Framework") |
| | st.caption("Work through the case using the ADPIE nursing process. Reveal the model answer when ready.") |
| |
|
| | if not st.session_state.selected_case_id: |
| | st.info("π‘ Select a case from the **Case Library** tab first.") |
| | else: |
| | case = get_by_id(st.session_state.selected_case_id) |
| | adpie = case["adpie"] |
| |
|
| | st.markdown( |
| | f'<div class="patient-banner">π₯ Active Case: <b>{case["title"]}</b> Β· ' |
| | f'π€ {case["patient"]["name"]}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | st.markdown("**Instructions:** Read the case, then write your own answers before revealing the model answers.") |
| | st.divider() |
| |
|
| | steps = [ |
| | ("A", "Assessment", "π", "What are the key subjective and objective findings?", "assessment"), |
| | ("D", "Nursing Diagnosis", "π΄", "Identify priority nursing diagnoses (NANDA format: problem r/t aetiology AEB evidence)", "diagnosis"), |
| | ("P", "Planning", "π―", "What are your SMART nursing goals (short and long term)?", "planning"), |
| | ("I", "Implementation", "βοΈ", "What nursing interventions will you implement and why?", "implementation"), |
| | ("E", "Evaluation", "β
", "How will you evaluate if goals were met? What actually happened?", "evaluation"), |
| | ] |
| |
|
| | for letter, title, icon, prompt, key in steps: |
| | st.markdown(f"### {icon} {letter} β {title}") |
| | st.markdown(f"*{prompt}*") |
| |
|
| | |
| | ans_key = f"adpie_{case['id']}_{key}" |
| | student_ans = st.text_area( |
| | f"Your {title}:", |
| | value=st.session_state.adpie_answers.get(ans_key, ""), |
| | height=100, |
| | key=f"adpie_input_{case['id']}_{key}", |
| | placeholder=f"Write your {title.lower()} here...", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.adpie_answers[ans_key] = student_ans |
| |
|
| | |
| | with st.expander(f"π Reveal Model Answer β {title}"): |
| | st.markdown(adpie[key]) |
| |
|
| | st.markdown("") |
| |
|
| | |
| | st.divider() |
| | st.markdown("### π΄ Nursing Diagnoses β Detailed") |
| | for nd in case["nursing_diagnoses"]: |
| | with st.expander(f"π {nd['diagnosis']}", expanded=False): |
| | st.markdown( |
| | f'<div class="nd-card">' |
| | f'<b>Supporting Evidence:</b> {nd["evidence"]}<br/><br/>' |
| | f'<b>Goal:</b> {nd["goal"]}' |
| | f'</div>', |
| | unsafe_allow_html=True |
| | ) |
| | st.markdown("**Nursing Interventions:**") |
| | for inv in nd["interventions"]: |
| | st.markdown(f"β’ {inv}") |
| |
|
| |
|
| | |
| | with tab_quiz: |
| | st.subheader("β Case-Based Quiz Questions") |
| | st.caption("NCLEX-style questions based on the active case. Select answers and submit for feedback.") |
| |
|
| | if not st.session_state.selected_case_id: |
| | st.info("π‘ Select a case from the **Case Library** tab first.") |
| | else: |
| | case = get_by_id(st.session_state.selected_case_id) |
| |
|
| | st.markdown( |
| | f'<div class="patient-banner">π₯ Active Case: <b>{case["title"]}</b></div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | |
| | nd_names = [nd["diagnosis"].split(" related to")[0] for nd in case["nursing_diagnoses"]] |
| | priorities = case["nursing_priorities"] |
| |
|
| | |
| | questions = [ |
| | { |
| | "q": f"Based on the case of {case['patient']['name']}, which nursing diagnosis should be prioritised FIRST?", |
| | "options": nd_names + ["Pain management only"], |
| | "answer": 0, |
| | "rationale": ( |
| | f"The priority nursing diagnosis is '{nd_names[0]}'. " |
| | f"Using Maslow's hierarchy, physiological and safety needs are addressed first. " |
| | f"The supporting evidence is: {case['nursing_diagnoses'][0]['evidence']}." |
| | ), |
| | }, |
| | { |
| | "q": f"What is the FIRST priority nursing action for {case['patient']['name']}?", |
| | "options": [ |
| | priorities[0] if len(priorities) > 0 else "Call the doctor", |
| | priorities[2] if len(priorities) > 2 else "Reassess vitals", |
| | "Complete full medication history", |
| | "Discharge planning", |
| | ], |
| | "answer": 0, |
| | "rationale": ( |
| | f"The immediate priority is: '{priorities[0]}'. " |
| | "Nursing priorities are ordered by urgency β life-threatening physiological " |
| | "problems must be addressed before less urgent concerns." |
| | ), |
| | }, |
| | { |
| | "q": f"Which assessment finding in {case['patient']['name']}'s case is MOST concerning?", |
| | "options": list(case["vitals"].items())[:4], |
| | "answer": 0, |
| | "rationale": ( |
| | "The most concerning vital sign is the first listed, which is outside normal limits. " |
| | "Always assess using ABCDE β Airway, Breathing, Circulation, Disability, Exposure β " |
| | "to prioritise life-threatening abnormalities." |
| | ), |
| | "is_vitals": True, |
| | }, |
| | { |
| | "q": f"The goal for the priority nursing diagnosis in this case is: '{case['nursing_diagnoses'][0]['goal']}'. Which nursing intervention BEST addresses this goal?", |
| | "options": case["nursing_diagnoses"][0]["interventions"][:4], |
| | "answer": 0, |
| | "rationale": ( |
| | f"The first listed intervention directly addresses the priority goal. " |
| | f"Rationale: {case['nursing_diagnoses'][0]['interventions'][0]}" |
| | ), |
| | }, |
| | { |
| | "q": f"In evaluating the outcomes for {case['patient']['name']}, which finding would indicate the PRIORITY nursing goal has been MET?", |
| | "options": [ |
| | case["nursing_diagnoses"][0]["goal"], |
| | "Patient is pain-free", |
| | "Family is satisfied with care", |
| | "All documentation is complete", |
| | ], |
| | "answer": 0, |
| | "rationale": ( |
| | f"The priority goal for this case is: '{case['nursing_diagnoses'][0]['goal']}'. " |
| | "Goals must be patient-centred, measurable, and time-bound (SMART). " |
| | "This outcome directly measures resolution of the priority nursing diagnosis." |
| | ), |
| | }, |
| | ] |
| |
|
| | if st.button("π Reset Quiz", use_container_width=False): |
| | st.session_state.quiz_answers = {} |
| | st.session_state.quiz_submitted = False |
| | st.rerun() |
| |
|
| | st.divider() |
| |
|
| | for i, q in enumerate(questions): |
| | st.markdown(f"**Question {i + 1}:** {q['q']}") |
| |
|
| | if q.get("is_vitals"): |
| | opts = [f"{k}: {v}" for k, v in q["options"]] |
| | else: |
| | opts = q["options"] |
| |
|
| | selected = st.radio( |
| | f"Q{i+1}", |
| | options=opts, |
| | key=f"quiz_{case['id']}_q{i}", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.quiz_answers[i] = selected |
| |
|
| | if st.session_state.quiz_submitted: |
| | correct_opt = opts[q["answer"]] |
| | if selected == correct_opt: |
| | st.markdown( |
| | f'<div class="success-banner">β
<b>Correct!</b> {q["rationale"]}</div>', |
| | unsafe_allow_html=True |
| | ) |
| | else: |
| | st.markdown( |
| | f'<div class="critical-banner">β <b>Incorrect.</b> ' |
| | f'Correct answer: <b>{correct_opt}</b><br/>{q["rationale"]}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | st.markdown("") |
| |
|
| | col_sub, col_score = st.columns([2, 3]) |
| | with col_sub: |
| | if st.button("β
Submit Answers", type="primary", use_container_width=True): |
| | st.session_state.quiz_submitted = True |
| | st.rerun() |
| |
|
| | if st.session_state.quiz_submitted: |
| | score = 0 |
| | for i, q in enumerate(questions): |
| | if q.get("is_vitals"): |
| | opts = [f"{k}: {v}" for k, v in q["options"]] |
| | else: |
| | opts = q["options"] |
| | if st.session_state.quiz_answers.get(i) == opts[q["answer"]]: |
| | score += 1 |
| |
|
| | pct = round((score / len(questions)) * 100) |
| | colour = "#2e7d32" if pct >= 80 else "#e65100" if pct >= 60 else "#c62828" |
| | with col_score: |
| | st.markdown( |
| | f'<div style="padding:0.6rem 1rem; background:#f5f5f5; border-radius:8px; ' |
| | f'font-size:1.1em; font-weight:700; color:{colour};">' |
| | f'Score: {score} / {len(questions)} ({pct}%)</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| |
|
| | |
| | with tab_plan: |
| | st.subheader("π Nursing Care Plan Builder") |
| | st.caption("Build a structured care plan for the active case using the NANDA-NIC-NOC framework.") |
| |
|
| | if not st.session_state.selected_case_id: |
| | st.info("π‘ Select a case from the **Case Library** tab first.") |
| | else: |
| | case = get_by_id(st.session_state.selected_case_id) |
| |
|
| | st.markdown( |
| | f'<div class="patient-banner">π₯ <b>{case["patient"]["name"]}</b> Β· ' |
| | f'{case["patient"]["age"]} y/o Β· {case["medical_diagnosis"]}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | st.markdown("Complete the care plan below. Toggle **Show Model Answer** to reveal guidance.") |
| | st.divider() |
| |
|
| | for idx, nd in enumerate(case["nursing_diagnoses"]): |
| | st.markdown(f"### π Nursing Diagnosis {idx + 1}") |
| | st.markdown( |
| | f'<div class="nd-card"><b>{nd["diagnosis"]}</b></div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| | cp_key = f"cp_{case['id']}_{idx}" |
| | if cp_key not in st.session_state.careplan_items: |
| | st.session_state.careplan_items[cp_key] = { |
| | "diagnosis": "", "goal": "", "interventions": "", "evaluation": "" |
| | } |
| |
|
| | c1, c2 = st.columns(2) |
| |
|
| | with c1: |
| | st.markdown("**Your Nursing Diagnosis (NANDA format):**") |
| | nd_input = st.text_area( |
| | "nd", |
| | value=st.session_state.careplan_items[cp_key]["diagnosis"], |
| | height=80, |
| | placeholder="[Problem] related to [Aetiology] as evidenced by [Signs/Symptoms]", |
| | key=f"{cp_key}_nd", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.careplan_items[cp_key]["diagnosis"] = nd_input |
| |
|
| | st.markdown("**Your SMART Goal:**") |
| | goal_input = st.text_area( |
| | "goal", |
| | value=st.session_state.careplan_items[cp_key]["goal"], |
| | height=80, |
| | placeholder="Patient will [outcome] by [time] as measured by [indicator]", |
| | key=f"{cp_key}_goal", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.careplan_items[cp_key]["goal"] = goal_input |
| |
|
| | with c2: |
| | st.markdown("**Your Nursing Interventions (with rationale):**") |
| | inv_input = st.text_area( |
| | "interventions", |
| | value=st.session_state.careplan_items[cp_key]["interventions"], |
| | height=80, |
| | placeholder="1. Intervention (Rationale)\n2. Intervention (Rationale)\n3. ...", |
| | key=f"{cp_key}_inv", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.careplan_items[cp_key]["interventions"] = inv_input |
| |
|
| | st.markdown("**Evaluation Criteria:**") |
| | eval_input = st.text_area( |
| | "evaluation", |
| | value=st.session_state.careplan_items[cp_key]["evaluation"], |
| | height=80, |
| | placeholder="Goal met / partially met / not met because...", |
| | key=f"{cp_key}_eval", |
| | label_visibility="collapsed", |
| | ) |
| | st.session_state.careplan_items[cp_key]["evaluation"] = eval_input |
| |
|
| | |
| | with st.expander("π Show Model Answer"): |
| | st.markdown(f"**Diagnosis:** {nd['diagnosis']}") |
| | st.markdown(f"**Evidence:** {nd['evidence']}") |
| | st.markdown(f"**Goal:** {nd['goal']}") |
| | st.markdown("**Model Interventions:**") |
| | for i in nd["interventions"]: |
| | st.markdown(f"β’ {i}") |
| |
|
| | st.divider() |
| |
|
| | |
| | if any( |
| | any(v for v in st.session_state.careplan_items.get(f"cp_{case['id']}_{i}", {}).values()) |
| | for i in range(len(case["nursing_diagnoses"])) |
| | ): |
| | st.markdown("### π Care Plan Summary") |
| | summary_lines = [f"# Care Plan: {case['patient']['name']}\n", |
| | f"**Diagnosis:** {case['medical_diagnosis']}\n"] |
| | for idx, nd in enumerate(case["nursing_diagnoses"]): |
| | cp_key = f"cp_{case['id']}_{idx}" |
| | item = st.session_state.careplan_items.get(cp_key, {}) |
| | summary_lines.append(f"\n## Nursing Diagnosis {idx+1}\n") |
| | summary_lines.append(f"**Diagnosis:** {item.get('diagnosis', 'β')}\n") |
| | summary_lines.append(f"**Goal:** {item.get('goal', 'β')}\n") |
| | summary_lines.append(f"**Interventions:** {item.get('interventions', 'β')}\n") |
| | summary_lines.append(f"**Evaluation:** {item.get('evaluation', 'β')}\n") |
| |
|
| | summary_text = "\n".join(summary_lines) |
| | st.download_button( |
| | "β¬οΈ Download Care Plan (.txt)", |
| | data=summary_text, |
| | file_name=f"care_plan_{case['id']}.txt", |
| | mime="text/plain", |
| | use_container_width=False, |
| | ) |
| |
|
| |
|
| | |
| | |
| | |
| | st.divider() |
| | st.caption( |
| | "Cases are fictional composite scenarios for educational purposes only. " |
| | "Always follow your institution's clinical guidelines, evidence-based practice, " |
| | "and clinical supervisor guidance in real patient care." |
| | ) |
| |
|