Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from pathlib import Path
|
| 3 |
import asyncio
|
| 4 |
|
| 5 |
# Import backend components
|
| 6 |
from app.prediction import PredictionPipeline
|
| 7 |
-
from app.database import add_patient_record, get_all_records
|
| 8 |
|
| 9 |
# --- Initialization ---
|
| 10 |
prediction_pipeline = PredictionPipeline()
|
|
@@ -35,16 +37,15 @@ async def refresh_history_table():
|
|
| 35 |
# --- Gradio UI Definition ---
|
| 36 |
css = """
|
| 37 |
/* --- Professional Dark Theme & Fonts --- */
|
| 38 |
-
:root { --primary-hue: 220 !important; --secondary-hue: 210 !important; --neutral-hue: 210 !important; --body-background-fill: #111827 !important; --block-background-fill: #
|
| 39 |
-
/* --- Header & Title Styling --- */
|
| 40 |
#app_header { text-align: center; max-width: 900px; margin: 0 auto; }
|
| 41 |
-
#app_title { font-size:
|
| 42 |
-
#app_subtitle { font-size: 1.
|
| 43 |
/* --- Layout and Spacing --- */
|
| 44 |
-
#main_container { gap: 2rem; max-width:
|
| 45 |
#results_gallery .gallery-item { padding: 0.25rem !important; background-color: #374151; border: 1px solid #374151 !important; }
|
| 46 |
#bottom_controls { max-width: 500px; margin: 2.5rem auto 1rem auto; }
|
| 47 |
-
#bottom_controls .gr-accordion > .gr-block-label { text-align: center !important; display: block !important; }
|
| 48 |
"""
|
| 49 |
with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue"), css=css, title="Pneumonia Detection AI") as demo:
|
| 50 |
|
|
@@ -68,22 +69,22 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
|
|
| 68 |
with gr.Row():
|
| 69 |
submit_analysis_btn = gr.Button("Analyze Images", variant="primary")
|
| 70 |
cancel_btn = gr.Button("Cancel", variant="stop")
|
| 71 |
-
|
| 72 |
-
# --- "About" Section (RESTORED) ---
|
| 73 |
with gr.Column(elem_id="bottom_controls"):
|
| 74 |
with gr.Accordion("About this Tool", open=False):
|
| 75 |
gr.Markdown(
|
| 76 |
"""
|
| 77 |
-
### MLOps-Powered Pneumonia Detection
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
-
**Disclaimer:** This tool is for demonstration and educational purposes only and is **not a substitute for professional medical advice.**
|
| 81 |
-
|
| 82 |
---
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
"""
|
| 88 |
)
|
| 89 |
with gr.Row():
|
|
@@ -95,37 +96,27 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
|
|
| 95 |
with gr.Row():
|
| 96 |
back_to_main_btn_hist = gr.Button("⬅️ Back to Main App")
|
| 97 |
refresh_history_btn = gr.Button("Refresh History")
|
|
|
|
|
|
|
| 98 |
history_df = gr.DataFrame(headers=["Name", "Age", "Prediction", "Confidence", "Date"], row_count=10, interactive=False)
|
| 99 |
|
| 100 |
with gr.Column(visible=False) as samples_page:
|
| 101 |
gr.Markdown("# 🖼️ Sample Image Library", elem_classes="app_title")
|
| 102 |
-
gr.Markdown("
|
| 103 |
back_to_main_btn_samp = gr.Button("⬅️ Back to Main App")
|
| 104 |
-
|
| 105 |
with gr.Row():
|
| 106 |
with gr.Column():
|
| 107 |
gr.Markdown("### Normal Cases")
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
value=NORMAL_SAMPLES,
|
| 111 |
-
label="Normal X-Rays",
|
| 112 |
-
columns=5,
|
| 113 |
-
object_fit="contain",
|
| 114 |
-
height="auto"
|
| 115 |
-
)
|
| 116 |
-
|
| 117 |
with gr.Column():
|
| 118 |
gr.Markdown("### Pneumonia Cases")
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
columns=5,
|
| 124 |
-
object_fit="contain",
|
| 125 |
-
height="auto"
|
| 126 |
-
)
|
| 127 |
|
| 128 |
-
#
|
| 129 |
def show_patient_info(files): return gr.update(visible=True) if files else gr.update(visible=False)
|
| 130 |
image_input.upload(fn=show_patient_info, inputs=image_input, outputs=patient_info_modal)
|
| 131 |
async def submit_and_hide_modal(name, age, files):
|
|
@@ -134,8 +125,10 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
|
|
| 134 |
cancel_btn.click(lambda: (gr.update(visible=False), None), None, [patient_info_modal, image_input])
|
| 135 |
start_over_btn.click(fn=None, js="() => { window.location.reload(); }")
|
| 136 |
|
|
|
|
| 137 |
all_pages = [main_app, history_page, samples_page]
|
| 138 |
-
async def show_history_page_and_refresh():
|
|
|
|
| 139 |
def show_samples_page(): return [gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)]
|
| 140 |
def show_main_page(): return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
|
| 141 |
|
|
@@ -144,7 +137,17 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
|
|
| 144 |
back_to_main_btn_hist.click(fn=show_main_page, outputs=all_pages)
|
| 145 |
back_to_main_btn_samp.click(fn=show_main_page, outputs=all_pages)
|
| 146 |
|
|
|
|
| 147 |
refresh_history_btn.click(fn=refresh_history_table, outputs=history_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
demo.load(fn=refresh_history_table, outputs=history_df)
|
| 149 |
|
| 150 |
# --- Launch the App ---
|
|
|
|
| 1 |
+
# app.py (The Definitive Final Version)
|
| 2 |
+
|
| 3 |
import gradio as gr
|
| 4 |
from pathlib import Path
|
| 5 |
import asyncio
|
| 6 |
|
| 7 |
# Import backend components
|
| 8 |
from app.prediction import PredictionPipeline
|
| 9 |
+
from app.database import add_patient_record, get_all_records, clear_all_records # Import the new function
|
| 10 |
|
| 11 |
# --- Initialization ---
|
| 12 |
prediction_pipeline = PredictionPipeline()
|
|
|
|
| 37 |
# --- Gradio UI Definition ---
|
| 38 |
css = """
|
| 39 |
/* --- Professional Dark Theme & Fonts --- */
|
| 40 |
+
:root { --primary-hue: 220 !important; --secondary-hue: 210 !important; --neutral-hue: 210 !important; --body-background-fill: #111827 !important; --block-background-fill: #1F2337 !important; --block-border-width: 1px !important; --border-color-accent: #374151 !important; --background-fill-secondary: #1F2937 !important;}
|
| 41 |
+
/* --- Header & Title Styling (THE FIX) --- */
|
| 42 |
#app_header { text-align: center; max-width: 900px; margin: 0 auto; }
|
| 43 |
+
#app_title { font-size: 3rem !important; font-weight: 800 !important; color: #FFFFFF !important; padding-top: 1rem; }
|
| 44 |
+
#app_subtitle { font-size: 1.25rem !important; color: #9CA3AF !important; margin-bottom: 2rem; }
|
| 45 |
/* --- Layout and Spacing --- */
|
| 46 |
+
#main_container { gap: 2rem; max-width: 700px; margin: 0 auto; } /* Made it slightly narrower */
|
| 47 |
#results_gallery .gallery-item { padding: 0.25rem !important; background-color: #374151; border: 1px solid #374151 !important; }
|
| 48 |
#bottom_controls { max-width: 500px; margin: 2.5rem auto 1rem auto; }
|
|
|
|
| 49 |
"""
|
| 50 |
with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue"), css=css, title="Pneumonia Detection AI") as demo:
|
| 51 |
|
|
|
|
| 69 |
with gr.Row():
|
| 70 |
submit_analysis_btn = gr.Button("Analyze Images", variant="primary")
|
| 71 |
cancel_btn = gr.Button("Cancel", variant="stop")
|
|
|
|
|
|
|
| 72 |
with gr.Column(elem_id="bottom_controls"):
|
| 73 |
with gr.Accordion("About this Tool", open=False):
|
| 74 |
gr.Markdown(
|
| 75 |
"""
|
| 76 |
+
### 🩺 MLOps-Powered Pneumonia Detection
|
| 77 |
+
|
| 78 |
+
This project showcases a complete **end-to-end MLOps pipeline** for medical image classification.
|
| 79 |
+
It leverages a cutting-edge **Vision Transformer (ViT)** model, fine-tuned on publicly available chest X-ray datasets, to classify images into **Normal** or **Pneumonia** cases.
|
| 80 |
+
|
| 81 |
+
⚠️ **Disclaimer:** This application is intended **solely for educational and demonstration purposes**. It is **not a medical diagnostic tool** and must not be used as a substitute for professional medical advice.
|
| 82 |
|
|
|
|
|
|
|
| 83 |
---
|
| 84 |
+
|
| 85 |
+
### 👥 Project Team
|
| 86 |
+
- **Alyyan Ahmed** — ML Engineer & Developer
|
| 87 |
+
- **Munim Akbar** — ML Engineer & Developer
|
| 88 |
"""
|
| 89 |
)
|
| 90 |
with gr.Row():
|
|
|
|
| 96 |
with gr.Row():
|
| 97 |
back_to_main_btn_hist = gr.Button("⬅️ Back to Main App")
|
| 98 |
refresh_history_btn = gr.Button("Refresh History")
|
| 99 |
+
# --- NEW: Clear History Button ---
|
| 100 |
+
clear_history_btn = gr.Button("⚠️ Clear All History", variant="stop")
|
| 101 |
history_df = gr.DataFrame(headers=["Name", "Age", "Prediction", "Confidence", "Date"], row_count=10, interactive=False)
|
| 102 |
|
| 103 |
with gr.Column(visible=False) as samples_page:
|
| 104 |
gr.Markdown("# 🖼️ Sample Image Library", elem_classes="app_title")
|
| 105 |
+
gr.Markdown("You can download these sample images to test the tool on the main page.")
|
| 106 |
back_to_main_btn_samp = gr.Button("⬅️ Back to Main App")
|
|
|
|
| 107 |
with gr.Row():
|
| 108 |
with gr.Column():
|
| 109 |
gr.Markdown("### Normal Cases")
|
| 110 |
+
for img_path in NORMAL_SAMPLES:
|
| 111 |
+
gr.File(value=img_path, label=Path(img_path).name, interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
with gr.Column():
|
| 113 |
gr.Markdown("### Pneumonia Cases")
|
| 114 |
+
for img_path in PNEUMONIA_SAMPLES:
|
| 115 |
+
gr.File(value=img_path, label=Path(img_path).name, interactive=False)
|
| 116 |
+
|
| 117 |
+
# --- Event Handling Logic ---
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
+
# ... (main page handlers are correct)
|
| 120 |
def show_patient_info(files): return gr.update(visible=True) if files else gr.update(visible=False)
|
| 121 |
image_input.upload(fn=show_patient_info, inputs=image_input, outputs=patient_info_modal)
|
| 122 |
async def submit_and_hide_modal(name, age, files):
|
|
|
|
| 125 |
cancel_btn.click(lambda: (gr.update(visible=False), None), None, [patient_info_modal, image_input])
|
| 126 |
start_over_btn.click(fn=None, js="() => { window.location.reload(); }")
|
| 127 |
|
| 128 |
+
# --- Page Navigation (correct) ---
|
| 129 |
all_pages = [main_app, history_page, samples_page]
|
| 130 |
+
async def show_history_page_and_refresh():
|
| 131 |
+
records_update = await refresh_history_table(); return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), records_update]
|
| 132 |
def show_samples_page(): return [gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)]
|
| 133 |
def show_main_page(): return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
|
| 134 |
|
|
|
|
| 137 |
back_to_main_btn_hist.click(fn=show_main_page, outputs=all_pages)
|
| 138 |
back_to_main_btn_samp.click(fn=show_main_page, outputs=all_pages)
|
| 139 |
|
| 140 |
+
# --- History Page Logic ---
|
| 141 |
refresh_history_btn.click(fn=refresh_history_table, outputs=history_df)
|
| 142 |
+
|
| 143 |
+
# --- NEW: Clear History Logic ---
|
| 144 |
+
async def clear_history_and_refresh():
|
| 145 |
+
deleted_count = await clear_all_records()
|
| 146 |
+
gr.Info(f"Successfully deleted {deleted_count} records.")
|
| 147 |
+
# After deleting, immediately refresh the table to show it's empty
|
| 148 |
+
return await refresh_history_table()
|
| 149 |
+
clear_history_btn.click(fn=clear_history_and_refresh, outputs=history_df)
|
| 150 |
+
|
| 151 |
demo.load(fn=refresh_history_table, outputs=history_df)
|
| 152 |
|
| 153 |
# --- Launch the App ---
|