{"repo": "allanotieno254/-breast-cancer-ultrasound-classifier-enhanced", "files": {"check.py": {"1c4d3a54fba097ec96fea00114fa72485d8630a7": {"t": 1785745427.473821, "n": 13174, "h": "53da1fdb056298b3", "c": "# # app.py (merged, schema-migrating version — fixes \"no such column\" errors)\n# import streamlit as st\n# from PIL import Image\n# from img_classification import (\n# teachable_machine_classification,\n# grad_cam_for_pil,\n# is_image_blurry,\n# prepare_result_record,\n# insert_record,\n# get_model_metadata,\n# )\n# import base64\n# import os\n# import requests\n# from dotenv import load_dotenv\n# from io import BytesIO\n# import json\n# import matplotlib.pyplot as plt\n# import tempfile\n# import sqlite3\n# import pandas as pd\n\n# load_dotenv()\n\n# DEEPSEEK_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\n# DEEPSEEK_MODEL = os.getenv(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n# DEEPSEEK_BASE = os.getenv(\"DEEPSEEK_BASE_URL\", \"https://api.deepseek.com/v1\")\n\n# st.set_page_config(page_title=\"Breast Cancer Classifier\", layout=\"wide\")\n\n# DB_PATH = os.path.join(os.path.dirname(__file__), \"predictions.db\")\n\n# # --------------------------------------------------------------------\n# # DATABASE MIGRATION / ENSURE SCHEMA\n# # --------------------------------------------------------------------\n# REQUIRED_COLUMNS = {\n# \"id\": \"INTEGER PRIMARY KEY AUTOINCREMENT\",\n# \"timestamp\": \"TEXT\",\n# \"patient_name\": \"TEXT\",\n# \"patient_age\": \"INTEGER\",\n# \"patient_gender\": \"TEXT\",\n# \"notes\": \"TEXT\",\n# \"image_paths\": \"TEXT\",\n# \"prediction\": \"TEXT\",\n# \"probs\": \"TEXT\",\n# \"explanation\": \"TEXT\",\n# \"model_metadata\": \"TEXT\",\n# \"psno\": \"TEXT\"\n# }\n\n# def ensure_table_and_columns():\n# \"\"\"\n# Ensures `predictions` table exists and contains all REQUIRED_COLUMNS.\n# If table exists but misses columns, add them via ALTER TABLE.\n# \"\"\"\n# conn = sqlite3.connect(DB_PATH)\n# c = conn.cursor()\n\n# # If table doesn't exist, create with full schema\n# c.execute(\"SELECT name FROM sqlite_master WHERE type='table' AND name='predictions'\")\n# if not c.fetchone():\n# # build create statement using REQUIRED_COLUMNS in a deterministic order\n# cols_sql = \",\\n \".join([f\"{col} {REQUIRED_COLUMNS[col]}\" for col in REQUIRED_COLUMNS])\n# create_sql = f\"CREATE TABLE predictions (\\n {cols_sql}\\n)\"\n# c.execute(create_sql)\n# conn.commit()\n# conn.close()\n# return\n\n# # Table exists: inspect columns\n# c.execute(\"PRAGMA table_info(predictions)\")\n# existing = [row[1] for row in c.fetchall()] # row format: (cid, name, type, notnull, dflt_value, pk)\n# # Add any missing columns\n# for col, coldef in REQUIRED_COLUMNS.items():\n# if col not in existing:\n# try:\n# # For PK we skip altering since it's only for creation; but if id absent, we still add a column id (non-PK)\n# if col == \"id\":\n# # If id missing, add a plain INTEGER column (can't make it PK on alter)\n# c.execute(f\"ALTER TABLE predictions ADD COLUMN {col} INTEGER\")\n# else:\n# c.execute(f\"ALTER TABLE predictions ADD COLUMN {col} {coldef}\")\n# except Exception:\n# # best-effort: ignore failures (e.g., column already added by race condition)\n# pass\n\n# conn.commit()\n# conn.close()\n\n# # ensure schema before anything\n# ensure_table_and_columns()\n\n# # --------------------------------------------------------------------\n# # SIDEBAR PATIENT INFO (includes PSNO)\n# # --------------------------------------------------------------------\n# st.sidebar.header(\"Patient Details\")\n# with st.sidebar.form(\"patient_form\"):\n# psno = st.text_input(\"Patient PSNO (unique ID)\")\n# name = st.text_input(\"Patient name\")\n# age = st.number_input(\"Age\", min_value=0, max_value=130, value=40)\n# gender = st.selectbox(\"Gender\", [\"Female\", \"Male\", \"Other\"])\n# symptoms = st.text_area(\"Clinical notes (symptoms)\")\n# submit_patient = st.form_submit_button(\"Save patient info\")\n\n# if submit_patient:\n# if not psno:\n# st.sidebar.error(\"PSNO is required.\")\n# else:\n# st.sidebar.success(\"Patient info saved (temporary)\")\n\n# # --------------------------------------------------------------------\n# # MODEL METADATA (unchanged)\n# # --------------------------------------------------------------------\n# st.sidebar.header(\"Model Info\")\n# meta = get_model_metadata()\n# st.sidebar.write(f\"Model file: {os.path.basename(meta['model_path'])}\")\n# st.sidebar.write(f\"Input size: {meta['input_size']}\")\n# st.sidebar.write(f\"Labels: {meta['labels']}\")\n# st.sidebar.write(f\"Loaded layers: {len(meta['keras_model_layers'])} layers\")\n\n# # --------------------------------------------------------------------\n# # MAIN: Upload, classify, grad-cam, LLM, insert\n# # --------------------------------------------------------------------\n# st.title(\"🩺 Breast Cancer Ultrasound Classifier — Enhanced\")\n# st.markdown(\"Upload ultrasound images for automated analysis, Grad-CAM, explanation, and DB saving.\")\n\n# uploaded_files = st.file_uploader(\"Upload image(s)\", accept_multiple_files=True, type=[\"jpg\",\"jpeg\",\"png\"])\n# exp_mode = st.radio(\"Explanation mode\", (\"Doctor\", \"Patient (plain language)\"))\n\n# if uploaded_files:\n# results = []\n# temp_dir = tempfile.mkdtemp(prefix=\"bcapp2_\")\n\n# for uploaded in uploaded_files:\n# try:\n# image = Image.open(uploaded).convert(\"RGB\")\n# except Exception:\n# st.error(f\"Could not open {uploaded.name}\")\n# continue\n\n# img_path = os.path.join(temp_dir, uploaded.name)\n# image.save(img_path)\n\n# blurry, lap_var = is_image_blurry(image)\n# if blurry:\n# st.warning(f\"{uploaded.name} appears blurry (Laplacian variance={lap_var:.1f}).\")\n\n# pred_idx, probs = teachable_machine_classification(image)\n# class_names = [\"Malignant\", \"Benign\"]\n# pred_label = class_names[pred_idx]\n\n# # GRAD-CAM\n# try:\n# overlay, heatmap = grad_cam_for_pil(image)\n# grad_path = os.path.join(temp_dir, f\"gradcam_{uploaded.name}\")\n# overlay.save(grad_path)\n# except Exception:\n# grad_path = None\n\n# # LLM (keep behavior from app.py2)\n# explanation = \"LLM disabled (no API key configured).\"\n# if DEEPSEEK_API_KEY:\n# headers = {\"Authorization\": f\"Bearer {DEEPSEEK_API_KEY}\", \"Content-Type\": \"application/json\"}\n# payload = {\n# \"model\": DEEPSEEK_MODEL,\n# \"messages\": [\n# {\"role\": \"system\", \"content\": \"You are a medical assistant.\"},\n# {\"role\": \"user\", \"content\": (\n# f\"A breast ultrasound image was analyzed by a machine learning model.\\n\"\n# f\"Prediction: {pred_label}\\n\"\n# f\"Probabilities: Malignant {probs[0]*100:.2f}%, Benign {probs[1]*100:.2f}%.\\n\"\n# f\"Patient name: {name}\\nAge: {age}\\nGender: {gender}\\nNotes: {symptoms}\\n\"\n# f\"Mode: {exp_mode}\"\n# )}\n# ],\n# \"max_tokens\": 700,\n# \"temperature\": 0.2\n# }\n# try:\n# r = requests.post(f\"{DEEPSEEK_BASE}/chat/completions\", headers=headers, json=payload, timeout=30)\n# if r.status_code == 200:\n# data = r.json()\n# explanation = data[\"choices\"][0][\"message\"][\"content\"].strip()\n# else:\n# explanation = f\"DeepSeek error: {r.status_code} {r.text}\"\n# except Exception as e:\n# explanation = f\"DeepSeek API call failed: {e}\"\n\n# # Recommendation logic (same)\n# recommendation = \"\"\n# if probs[0] >= 0.70:\n# recommendation = \"High suspicion for malignancy — urgent follow-up required.\"\n# elif probs[1] >= 0.90:\n# recommendation = \"High confidence benign — routine follow-up.\"\n# elif max(probs) < 0.55:\n# recommendation = \"Low confidence — further imaging advised.\"\n\n# # Prepare record using original prepare_result_record (keeps compatibility)\n# patient_info = {\"name\": name, \"age\": age, \"gender\": gender, \"notes\": symptoms}\n# try:\n# rec = prepare_result_record(patient_info, [img_path], pred_label, probs, explanation)\n# # rec has fields: timestamp, patient_name, patient_age, patient_gender, notes, image_paths,\n# # prediction, probs, explanation, model_metadata\n# # Add psno into record so insert_record will store it in the DB's psno column (if insert_record supports it)\n# rec[\"psno\"] = psno\n# # Use the original insert_record() from img_classification.py\n# insert_record(rec)\n# except Exception as e:\n# st.error(f\"DB insert error: {e}\")\n\n# # collect for display\n# results.append({\n# \"filename\": uploaded.name,\n# \"image_path\": img_path,\n# \"gradcam_path\": grad_path,\n# \"prediction\": pred_label,\n# \"probs\": [float(probs[0]), float(probs[1])],\n# \"explanation\": explanation,\n# \"recommendation\": recommendation\n# })\n\n# # Display results\n# st.markdown(\"### Results\")\n# for r in results:\n# st.markdown(f\"**{r['filename']}** — Prediction: **{r['prediction']}**\")\n# st.write(f\"Malignant: {r['probs'][0]*100:.2f}% | Benign: {r['probs'][1]*100:.2f}%\")\n# col1, col2 = st.columns(2)\n# with col1:\n# st.image(r[\"image_path\"], caption=\"Original\")\n# with col2:\n# if r[\"gradcam_path\"]:\n# st.image(r[\"gradcam_path\"], caption=\"Grad-CAM\")\n# st.info(r[\"recommendation\"])\n# with st.expander(\"Explanation\"):\n# st.write(r[\"explanation\"])\n# fig, ax = plt.subplots(figsize=(4,1.2))\n# ax.barh([\"Malignant\",\"Benign\"], [r['probs'][0]*100, r['probs'][1]*100])\n# ax.set_xlim(0,100)\n# st.pyplot(fig)\n# st.markdown(\"---\")\n\n# # --------------------------------------------------------------------\n# # DATABASE MANAGEMENT UI (replaces PDF export)\n# # --------------------------------------------------------------------\n# st.header(\"📦 Database Management\")\n\n# # Download CSV\n# if st.button(\"Download database as CSV\"):\n# ensure_table_and_columns = None # placeholder to avoid linter confusion (we used ensure_table_and_columns earlier)\n# # ensure schema and read\n# ensure_table_and_columns = None\n# # Re-run the ensure (safe to call)\n# ensure_table_and_columns = lambda: ensure_table_and_columns # no-op\n# # We'll just make sure the DB exists and then read\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# conn.close()\n# df = pd.DataFrame()\n# conn.close()\n\n# if df.empty:\n# st.info(\"Database is empty.\")\n# else:\n# csv = df.to_csv(index=False).encode()\n# b64 = base64.b64encode(csv).decode()\n# st.markdown(f'Download CSV', unsafe_allow_html=True)\n\n# # Download Excel\n# if st.button(\"Download database as Excel\"):\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# conn.close()\n# df = pd.DataFrame()\n# conn.close()\n\n# if df.empty:\n# st.info(\"Database is empty.\")\n# else:\n# excel_path = os.path.join(tempfile.gettempdir(), \"predictions.xlsx\")\n# df.to_excel(excel_path, index=False)\n# with open(excel_path, \"rb\") as f:\n# excel_bytes = f.read()\n# b64 = base64.b64encode(excel_bytes).decode()\n# st.markdown(f'Download Excel', unsafe_allow_html=True)\n\n# # Clear DB\n# if st.button(\"Clear entire database\"):\n# if os.path.exists(DB_PATH):\n# try:\n# os.remove(DB_PATH)\n# except Exception as e:\n# st.error(f\"Could not remove DB file: {e}\")\n# # Recreate table with required columns so UI continues to work\n# ensure_table_and_columns()\n# st.success(\"Database cleared.\")\n# else:\n# st.info(\"Database already empty.\")\n\n# # Show DB table\n# if st.checkbox(\"Show database records\"):\n# # Ensure schema present and then show up to 100 rows\n# ensure_table_and_columns()\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions ORDER BY id DESC LIMIT 100\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# df = pd.DataFrame()\n# conn.close()\n# if df.empty:\n# st.info(\"No records found.\")\n# else:\n# st.dataframe(df)\n"}, "1d0c1d3868a9d2b3a27a87820ecde96a40d1da53": {"t": 1785753084.4074812, "n": 13174, "h": "53da1fdb056298b3", "c": "# # app.py (merged, schema-migrating version — fixes \"no such column\" errors)\n# import streamlit as st\n# from PIL import Image\n# from img_classification import (\n# teachable_machine_classification,\n# grad_cam_for_pil,\n# is_image_blurry,\n# prepare_result_record,\n# insert_record,\n# get_model_metadata,\n# )\n# import base64\n# import os\n# import requests\n# from dotenv import load_dotenv\n# from io import BytesIO\n# import json\n# import matplotlib.pyplot as plt\n# import tempfile\n# import sqlite3\n# import pandas as pd\n\n# load_dotenv()\n\n# DEEPSEEK_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\n# DEEPSEEK_MODEL = os.getenv(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n# DEEPSEEK_BASE = os.getenv(\"DEEPSEEK_BASE_URL\", \"https://api.deepseek.com/v1\")\n\n# st.set_page_config(page_title=\"Breast Cancer Classifier\", layout=\"wide\")\n\n# DB_PATH = os.path.join(os.path.dirname(__file__), \"predictions.db\")\n\n# # --------------------------------------------------------------------\n# # DATABASE MIGRATION / ENSURE SCHEMA\n# # --------------------------------------------------------------------\n# REQUIRED_COLUMNS = {\n# \"id\": \"INTEGER PRIMARY KEY AUTOINCREMENT\",\n# \"timestamp\": \"TEXT\",\n# \"patient_name\": \"TEXT\",\n# \"patient_age\": \"INTEGER\",\n# \"patient_gender\": \"TEXT\",\n# \"notes\": \"TEXT\",\n# \"image_paths\": \"TEXT\",\n# \"prediction\": \"TEXT\",\n# \"probs\": \"TEXT\",\n# \"explanation\": \"TEXT\",\n# \"model_metadata\": \"TEXT\",\n# \"psno\": \"TEXT\"\n# }\n\n# def ensure_table_and_columns():\n# \"\"\"\n# Ensures `predictions` table exists and contains all REQUIRED_COLUMNS.\n# If table exists but misses columns, add them via ALTER TABLE.\n# \"\"\"\n# conn = sqlite3.connect(DB_PATH)\n# c = conn.cursor()\n\n# # If table doesn't exist, create with full schema\n# c.execute(\"SELECT name FROM sqlite_master WHERE type='table' AND name='predictions'\")\n# if not c.fetchone():\n# # build create statement using REQUIRED_COLUMNS in a deterministic order\n# cols_sql = \",\\n \".join([f\"{col} {REQUIRED_COLUMNS[col]}\" for col in REQUIRED_COLUMNS])\n# create_sql = f\"CREATE TABLE predictions (\\n {cols_sql}\\n)\"\n# c.execute(create_sql)\n# conn.commit()\n# conn.close()\n# return\n\n# # Table exists: inspect columns\n# c.execute(\"PRAGMA table_info(predictions)\")\n# existing = [row[1] for row in c.fetchall()] # row format: (cid, name, type, notnull, dflt_value, pk)\n# # Add any missing columns\n# for col, coldef in REQUIRED_COLUMNS.items():\n# if col not in existing:\n# try:\n# # For PK we skip altering since it's only for creation; but if id absent, we still add a column id (non-PK)\n# if col == \"id\":\n# # If id missing, add a plain INTEGER column (can't make it PK on alter)\n# c.execute(f\"ALTER TABLE predictions ADD COLUMN {col} INTEGER\")\n# else:\n# c.execute(f\"ALTER TABLE predictions ADD COLUMN {col} {coldef}\")\n# except Exception:\n# # best-effort: ignore failures (e.g., column already added by race condition)\n# pass\n\n# conn.commit()\n# conn.close()\n\n# # ensure schema before anything\n# ensure_table_and_columns()\n\n# # --------------------------------------------------------------------\n# # SIDEBAR PATIENT INFO (includes PSNO)\n# # --------------------------------------------------------------------\n# st.sidebar.header(\"Patient Details\")\n# with st.sidebar.form(\"patient_form\"):\n# psno = st.text_input(\"Patient PSNO (unique ID)\")\n# name = st.text_input(\"Patient name\")\n# age = st.number_input(\"Age\", min_value=0, max_value=130, value=40)\n# gender = st.selectbox(\"Gender\", [\"Female\", \"Male\", \"Other\"])\n# symptoms = st.text_area(\"Clinical notes (symptoms)\")\n# submit_patient = st.form_submit_button(\"Save patient info\")\n\n# if submit_patient:\n# if not psno:\n# st.sidebar.error(\"PSNO is required.\")\n# else:\n# st.sidebar.success(\"Patient info saved (temporary)\")\n\n# # --------------------------------------------------------------------\n# # MODEL METADATA (unchanged)\n# # --------------------------------------------------------------------\n# st.sidebar.header(\"Model Info\")\n# meta = get_model_metadata()\n# st.sidebar.write(f\"Model file: {os.path.basename(meta['model_path'])}\")\n# st.sidebar.write(f\"Input size: {meta['input_size']}\")\n# st.sidebar.write(f\"Labels: {meta['labels']}\")\n# st.sidebar.write(f\"Loaded layers: {len(meta['keras_model_layers'])} layers\")\n\n# # --------------------------------------------------------------------\n# # MAIN: Upload, classify, grad-cam, LLM, insert\n# # --------------------------------------------------------------------\n# st.title(\"🩺 Breast Cancer Ultrasound Classifier — Enhanced\")\n# st.markdown(\"Upload ultrasound images for automated analysis, Grad-CAM, explanation, and DB saving.\")\n\n# uploaded_files = st.file_uploader(\"Upload image(s)\", accept_multiple_files=True, type=[\"jpg\",\"jpeg\",\"png\"])\n# exp_mode = st.radio(\"Explanation mode\", (\"Doctor\", \"Patient (plain language)\"))\n\n# if uploaded_files:\n# results = []\n# temp_dir = tempfile.mkdtemp(prefix=\"bcapp2_\")\n\n# for uploaded in uploaded_files:\n# try:\n# image = Image.open(uploaded).convert(\"RGB\")\n# except Exception:\n# st.error(f\"Could not open {uploaded.name}\")\n# continue\n\n# img_path = os.path.join(temp_dir, uploaded.name)\n# image.save(img_path)\n\n# blurry, lap_var = is_image_blurry(image)\n# if blurry:\n# st.warning(f\"{uploaded.name} appears blurry (Laplacian variance={lap_var:.1f}).\")\n\n# pred_idx, probs = teachable_machine_classification(image)\n# class_names = [\"Malignant\", \"Benign\"]\n# pred_label = class_names[pred_idx]\n\n# # GRAD-CAM\n# try:\n# overlay, heatmap = grad_cam_for_pil(image)\n# grad_path = os.path.join(temp_dir, f\"gradcam_{uploaded.name}\")\n# overlay.save(grad_path)\n# except Exception:\n# grad_path = None\n\n# # LLM (keep behavior from app.py2)\n# explanation = \"LLM disabled (no API key configured).\"\n# if DEEPSEEK_API_KEY:\n# headers = {\"Authorization\": f\"Bearer {DEEPSEEK_API_KEY}\", \"Content-Type\": \"application/json\"}\n# payload = {\n# \"model\": DEEPSEEK_MODEL,\n# \"messages\": [\n# {\"role\": \"system\", \"content\": \"You are a medical assistant.\"},\n# {\"role\": \"user\", \"content\": (\n# f\"A breast ultrasound image was analyzed by a machine learning model.\\n\"\n# f\"Prediction: {pred_label}\\n\"\n# f\"Probabilities: Malignant {probs[0]*100:.2f}%, Benign {probs[1]*100:.2f}%.\\n\"\n# f\"Patient name: {name}\\nAge: {age}\\nGender: {gender}\\nNotes: {symptoms}\\n\"\n# f\"Mode: {exp_mode}\"\n# )}\n# ],\n# \"max_tokens\": 700,\n# \"temperature\": 0.2\n# }\n# try:\n# r = requests.post(f\"{DEEPSEEK_BASE}/chat/completions\", headers=headers, json=payload, timeout=30)\n# if r.status_code == 200:\n# data = r.json()\n# explanation = data[\"choices\"][0][\"message\"][\"content\"].strip()\n# else:\n# explanation = f\"DeepSeek error: {r.status_code} {r.text}\"\n# except Exception as e:\n# explanation = f\"DeepSeek API call failed: {e}\"\n\n# # Recommendation logic (same)\n# recommendation = \"\"\n# if probs[0] >= 0.70:\n# recommendation = \"High suspicion for malignancy — urgent follow-up required.\"\n# elif probs[1] >= 0.90:\n# recommendation = \"High confidence benign — routine follow-up.\"\n# elif max(probs) < 0.55:\n# recommendation = \"Low confidence — further imaging advised.\"\n\n# # Prepare record using original prepare_result_record (keeps compatibility)\n# patient_info = {\"name\": name, \"age\": age, \"gender\": gender, \"notes\": symptoms}\n# try:\n# rec = prepare_result_record(patient_info, [img_path], pred_label, probs, explanation)\n# # rec has fields: timestamp, patient_name, patient_age, patient_gender, notes, image_paths,\n# # prediction, probs, explanation, model_metadata\n# # Add psno into record so insert_record will store it in the DB's psno column (if insert_record supports it)\n# rec[\"psno\"] = psno\n# # Use the original insert_record() from img_classification.py\n# insert_record(rec)\n# except Exception as e:\n# st.error(f\"DB insert error: {e}\")\n\n# # collect for display\n# results.append({\n# \"filename\": uploaded.name,\n# \"image_path\": img_path,\n# \"gradcam_path\": grad_path,\n# \"prediction\": pred_label,\n# \"probs\": [float(probs[0]), float(probs[1])],\n# \"explanation\": explanation,\n# \"recommendation\": recommendation\n# })\n\n# # Display results\n# st.markdown(\"### Results\")\n# for r in results:\n# st.markdown(f\"**{r['filename']}** — Prediction: **{r['prediction']}**\")\n# st.write(f\"Malignant: {r['probs'][0]*100:.2f}% | Benign: {r['probs'][1]*100:.2f}%\")\n# col1, col2 = st.columns(2)\n# with col1:\n# st.image(r[\"image_path\"], caption=\"Original\")\n# with col2:\n# if r[\"gradcam_path\"]:\n# st.image(r[\"gradcam_path\"], caption=\"Grad-CAM\")\n# st.info(r[\"recommendation\"])\n# with st.expander(\"Explanation\"):\n# st.write(r[\"explanation\"])\n# fig, ax = plt.subplots(figsize=(4,1.2))\n# ax.barh([\"Malignant\",\"Benign\"], [r['probs'][0]*100, r['probs'][1]*100])\n# ax.set_xlim(0,100)\n# st.pyplot(fig)\n# st.markdown(\"---\")\n\n# # --------------------------------------------------------------------\n# # DATABASE MANAGEMENT UI (replaces PDF export)\n# # --------------------------------------------------------------------\n# st.header(\"📦 Database Management\")\n\n# # Download CSV\n# if st.button(\"Download database as CSV\"):\n# ensure_table_and_columns = None # placeholder to avoid linter confusion (we used ensure_table_and_columns earlier)\n# # ensure schema and read\n# ensure_table_and_columns = None\n# # Re-run the ensure (safe to call)\n# ensure_table_and_columns = lambda: ensure_table_and_columns # no-op\n# # We'll just make sure the DB exists and then read\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# conn.close()\n# df = pd.DataFrame()\n# conn.close()\n\n# if df.empty:\n# st.info(\"Database is empty.\")\n# else:\n# csv = df.to_csv(index=False).encode()\n# b64 = base64.b64encode(csv).decode()\n# st.markdown(f'Download CSV', unsafe_allow_html=True)\n\n# # Download Excel\n# if st.button(\"Download database as Excel\"):\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# conn.close()\n# df = pd.DataFrame()\n# conn.close()\n\n# if df.empty:\n# st.info(\"Database is empty.\")\n# else:\n# excel_path = os.path.join(tempfile.gettempdir(), \"predictions.xlsx\")\n# df.to_excel(excel_path, index=False)\n# with open(excel_path, \"rb\") as f:\n# excel_bytes = f.read()\n# b64 = base64.b64encode(excel_bytes).decode()\n# st.markdown(f'Download Excel', unsafe_allow_html=True)\n\n# # Clear DB\n# if st.button(\"Clear entire database\"):\n# if os.path.exists(DB_PATH):\n# try:\n# os.remove(DB_PATH)\n# except Exception as e:\n# st.error(f\"Could not remove DB file: {e}\")\n# # Recreate table with required columns so UI continues to work\n# ensure_table_and_columns()\n# st.success(\"Database cleared.\")\n# else:\n# st.info(\"Database already empty.\")\n\n# # Show DB table\n# if st.checkbox(\"Show database records\"):\n# # Ensure schema present and then show up to 100 rows\n# ensure_table_and_columns()\n# conn = sqlite3.connect(DB_PATH)\n# try:\n# df = pd.read_sql_query(\"SELECT * FROM predictions ORDER BY id DESC LIMIT 100\", conn)\n# except Exception as e:\n# st.error(f\"Failed to read DB: {e}\")\n# df = pd.DataFrame()\n# conn.close()\n# if df.empty:\n# st.info(\"No records found.\")\n# else:\n# st.dataframe(df)\n"}}}}