# ========================= # ONE CELL: SmartCare HMS (Student Edition) # Patients + Doctors (with timing) + Appointments (search doctor by time + select) + Billing + Groq AI + Login # ========================= import os import sqlite3 from datetime import datetime, date import gradio as gr from groq import Groq # -------------------- (OPTIONAL) SET GROQ KEY HERE -------------------- # Paste your key (recommended) OR set it in a separate cell: # os.environ["GROQ_API_KEY"] = "PASTE_YOUR_KEY" # --------------------------------------------------------------------- # os.environ["GROQ_API_KEY"] = "PASTE_YOUR_GROQ_KEY_HERE" # -------------------- DB -------------------- DB_PATH = "hms.db" def get_conn(): return sqlite3.connect(DB_PATH, check_same_thread=False) def _now(): return datetime.now().strftime("%Y-%m-%d %H:%M:%S") def not_empty(s): return s is not None and str(s).strip() != "" def is_int(x): try: int(str(x).strip()) return True except: return False def is_float(x): try: float(str(x).strip()) return True except: return False def parse_date_yyyy_mm_dd(s): try: return datetime.strptime(str(s).strip(), "%Y-%m-%d") except: return None def parse_time_hh_mm(s): try: return datetime.strptime(str(s).strip(), "%H:%M") except: return None def init_db(): conn = get_conn() cur = conn.cursor() # patients cur.execute(""" CREATE TABLE IF NOT EXISTS patients ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, gender TEXT NOT NULL, phone TEXT NOT NULL, address TEXT, created_at TEXT NOT NULL )""") # doctors cur.execute(""" CREATE TABLE IF NOT EXISTS doctors ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, specialization TEXT NOT NULL, phone TEXT NOT NULL, available_from TEXT NOT NULL DEFAULT '09:00', available_to TEXT NOT NULL DEFAULT '17:00', created_at TEXT NOT NULL )""") # appointments cur.execute(""" CREATE TABLE IF NOT EXISTS appointments ( id INTEGER PRIMARY KEY AUTOINCREMENT, patient_id INTEGER NOT NULL, doctor_id INTEGER NOT NULL, appt_date TEXT NOT NULL, appt_time TEXT NOT NULL, reason TEXT, status TEXT NOT NULL DEFAULT 'BOOKED', created_at TEXT NOT NULL )""") # bills cur.execute(""" CREATE TABLE IF NOT EXISTS bills ( id INTEGER PRIMARY KEY AUTOINCREMENT, patient_id INTEGER NOT NULL, services TEXT NOT NULL, total_amount REAL NOT NULL, created_at TEXT NOT NULL )""") conn.commit() conn.close() # -------------------- DELETE FUNCTIONS -------------------- def delete_patient(pid): if not is_int(pid): return "❌ Patient ID must be a number." pid = int(pid) conn = get_conn() cur = conn.cursor() # check exists cur.execute("SELECT id FROM patients WHERE id=?", (pid,)) if cur.fetchone() is None: conn.close() return "⚠️ Patient not found." # also delete related appointments & bills cur.execute("DELETE FROM appointments WHERE patient_id=?", (pid,)) cur.execute("DELETE FROM bills WHERE patient_id=?", (pid,)) cur.execute("DELETE FROM patients WHERE id=?", (pid,)) conn.commit() conn.close() return "πŸ—‘οΈ Patient Deleted Successfully" def delete_doctor(did): if not is_int(did): return "❌ Doctor ID must be a number." did = int(did) conn = get_conn() cur = conn.cursor() cur.execute("SELECT id FROM doctors WHERE id=?", (did,)) if cur.fetchone() is None: conn.close() return "⚠️ Doctor not found." # delete related appointments cur.execute("DELETE FROM appointments WHERE doctor_id=?", (did,)) cur.execute("DELETE FROM doctors WHERE id=?", (did,)) conn.commit() conn.close() return "πŸ—‘οΈ Doctor Deleted Successfully" def delete_appointment(aid): if not is_int(aid): return "❌ Appointment ID must be a number." aid = int(aid) conn = get_conn() cur = conn.cursor() cur.execute("DELETE FROM appointments WHERE id=?", (aid,)) conn.commit() deleted = cur.rowcount conn.close() return "πŸ—‘οΈ Appointment Deleted" if deleted else "⚠️ Appointment not found." def delete_bill(bid): if not is_int(bid): return "❌ Bill ID must be a number." bid = int(bid) conn = get_conn() cur = conn.cursor() cur.execute("DELETE FROM bills WHERE id=?", (bid,)) conn.commit() deleted = cur.rowcount conn.close() return "πŸ—‘οΈ Bill Deleted" if deleted else "⚠️ Bill not found." init_db() # -------------------- Patients -------------------- def add_patient(name, age, gender, phone, address): if not not_empty(name): return "❌ Name is required." if not is_int(age): return "❌ Age must be a number." age = int(age) if age <= 0 or age > 120: return "❌ Age must be between 1 and 120." if gender not in ["Male", "Female", "Other"]: return "❌ Select gender." if not not_empty(phone): return "❌ Phone required." conn = get_conn() cur = conn.cursor() cur.execute("""INSERT INTO patients (name, age, gender, phone, address, created_at) VALUES (?, ?, ?, ?, ?, ?)""", (name.strip(), age, gender, phone.strip(), (address or "").strip(), _now())) conn.commit() conn.close() return "βœ… Patient Added πŸŽ‰" def list_patients(): conn = get_conn() cur = conn.cursor() cur.execute("SELECT id, name, age, gender, phone, address, created_at FROM patients ORDER BY id DESC") rows = cur.fetchall() conn.close() return rows # -------------------- Doctors (WITH TIMING) -------------------- def add_doctor(name, specialization, phone, available_from, available_to): if not not_empty(name): return "❌ Doctor name required." if not not_empty(specialization): return "❌ Specialization required." if not not_empty(phone): return "❌ Phone required." t1 = parse_time_hh_mm(available_from) t2 = parse_time_hh_mm(available_to) if t1 is None or t2 is None: return "❌ Timing must be HH:MM (e.g., 09:00 to 17:00)." if t1 >= t2: return "❌ 'From' time must be earlier than 'To' time." conn = get_conn() cur = conn.cursor() cur.execute("""INSERT INTO doctors (name, specialization, phone, available_from, available_to, created_at) VALUES (?, ?, ?, ?, ?, ?)""", (name.strip(), specialization.strip(), phone.strip(), available_from.strip(), available_to.strip(), _now())) conn.commit() conn.close() return "βœ… Doctor Added 🩺" def list_doctors(): conn = get_conn() cur = conn.cursor() cur.execute("SELECT id, name, specialization, phone, available_from, available_to, created_at FROM doctors ORDER BY id DESC") rows = cur.fetchall() conn.close() return rows def search_doctors_by_time(time_str): """ Returns: - table rows of doctors available at time_str - dropdown choices [(label, value), ...] - status msg """ t = parse_time_hh_mm(time_str) if t is None: return [], gr.update(choices=[], value=None), "❌ Time format must be HH:MM (e.g., 14:30)." # HH:MM lexicographic compare works if zero-padded ts = time_str.strip() conn = get_conn() cur = conn.cursor() cur.execute(""" SELECT id, name, specialization, phone, available_from, available_to FROM doctors WHERE available_from <= ? AND available_to >= ? ORDER BY id DESC """, (ts, ts)) rows = cur.fetchall() conn.close() choices = [] for r in rows: did, name, spec, phone, f, to = r label = f"{did} - {name} ({spec}) [{f}-{to}]" choices.append((label, str(did))) msg = f"βœ… {len(rows)} doctor(s) found for {ts}." if rows else f"⚠️ No doctor available at {ts}." return rows, gr.update(choices=choices, value=(choices[0][1] if choices else None)), msg # -------------------- Appointments (SEARCH + SELECT DOCTOR + BOOK) -------------------- def book_appointment_admin(patient_id, selected_doctor_id, appt_date, appt_time, reason): # patient id if not is_int(patient_id): return "❌ Patient ID must be a number." # doctor dropdown value is string id if not not_empty(selected_doctor_id) or not is_int(selected_doctor_id): return "❌ Please select a doctor from the search list." pid = int(patient_id) did = int(selected_doctor_id) # validate date/time formats d = parse_date_yyyy_mm_dd(appt_date) if d is None: return "❌ Date format must be YYYY-MM-DD (e.g., 2026-02-19)." t = parse_time_hh_mm(appt_time) if t is None: return "❌ Time format must be HH:MM (e.g., 14:30)." if not not_empty(reason): return "❌ Reason is required (short text)." conn = get_conn() cur = conn.cursor() # patient exists? cur.execute("SELECT id FROM patients WHERE id=?", (pid,)) if cur.fetchone() is None: conn.close() return "⚠️ Patient not found." # doctor exists + timing check cur.execute("SELECT available_from, available_to FROM doctors WHERE id=?", (did,)) row = cur.fetchone() if row is None: conn.close() return "⚠️ Doctor not found." doc_from, doc_to = row[0], row[1] # ensure appointment time inside availability if not (doc_from <= appt_time.strip() <= doc_to): conn.close() return f"⚠️ Doctor available time is {doc_from} to {doc_to}. Choose a time within this range." # conflict check: same doctor/date/time and still booked cur.execute("""SELECT id FROM appointments WHERE doctor_id=? AND appt_date=? AND appt_time=? AND status='BOOKED'""", (did, appt_date.strip(), appt_time.strip())) if cur.fetchone() is not None: conn.close() return "⚠️ Slot already booked for this doctor. Choose another time." cur.execute("""INSERT INTO appointments (patient_id, doctor_id, appt_date, appt_time, reason, status, created_at) VALUES (?, ?, ?, ?, ?, 'BOOKED', ?)""", (pid, did, appt_date.strip(), appt_time.strip(), reason.strip(), _now())) conn.commit() conn.close() return "βœ… Appointment Booked πŸ“…" def list_appointments(): conn = get_conn() cur = conn.cursor() cur.execute(""" SELECT a.id, a.patient_id, p.name, a.doctor_id, d.name, a.appt_date, a.appt_time, a.status, a.reason FROM appointments a JOIN patients p ON p.id = a.patient_id JOIN doctors d ON d.id = a.doctor_id ORDER BY a.id DESC """) rows = cur.fetchall() conn.close() return rows def cancel_appointment(appt_id): if not is_int(appt_id): return "❌ Appointment ID must be a number." aid = int(appt_id) conn = get_conn() cur = conn.cursor() cur.execute("UPDATE appointments SET status='CANCELLED' WHERE id=?", (aid,)) conn.commit() updated = cur.rowcount conn.close() return "βœ… Cancelled 🧾" if updated else "⚠️ Appointment not found." # -------------------- Billing -------------------- def create_bill(patient_id, services_csv, amounts_csv): if not is_int(patient_id): return "❌ Patient ID must be a number." pid = int(patient_id) if not not_empty(services_csv): return "❌ Services required." if not not_empty(amounts_csv): return "❌ Amounts required." services = [s.strip() for s in services_csv.split(",") if s.strip()] amounts_raw = [a.strip() for a in amounts_csv.split(",") if a.strip()] if len(services) == 0: return "❌ Add at least 1 service." if len(services) != len(amounts_raw): return "❌ Services count must match amounts count." amounts = [] for a in amounts_raw: if not is_float(a): return f"❌ Amount '{a}' is not a number." val = float(a) if val < 0: return "❌ Amount cannot be negative." amounts.append(val) total = sum(amounts) conn = get_conn() cur = conn.cursor() cur.execute("SELECT id FROM patients WHERE id=?", (pid,)) if cur.fetchone() is None: conn.close() return "⚠️ Patient not found." nice_services = " | ".join([f"{s} (Rs {amt:.0f})" for s, amt in zip(services, amounts)]) cur.execute("""INSERT INTO bills (patient_id, services, total_amount, created_at) VALUES (?, ?, ?, ?)""", (pid, nice_services, total, _now())) conn.commit() conn.close() return f"βœ… Bill Created πŸ’Έ Total: Rs {total:.0f}" def list_bills(): conn = get_conn() cur = conn.cursor() cur.execute(""" SELECT b.id, b.patient_id, p.name, b.services, b.total_amount, b.created_at FROM bills b JOIN patients p ON p.id = b.patient_id ORDER BY b.id DESC """ ) rows = cur.fetchall() conn.close() return rows # -------------------- Reports -------------------- def reports_summary(): return { "Total Patients": len(list_patients()), "Total Doctors": len(list_doctors()), "Total Appointments": len(list_appointments()), "Total Bills": len(list_bills()), } # -------------------- Groq (SAFE + EXACT STYLE) -------------------- def groq_assistant(task_type, input_text): if not not_empty(input_text): return "⚠️ Please enter some text first." if not os.environ.get("GROQ_API_KEY"): return "⚠️ GROQ_API_KEY not set. Paste your key: os.environ['GROQ_API_KEY']='...'." system_note = ( "You are a helpful hospital assistant for an undergraduate demo app. " "Do NOT provide medical diagnosis. Provide general educational info only. " "Always add: 'This is not medical advice. Please consult a qualified doctor.'" ) prompt_map = { "Explain Diagnosis (Simple)": f"Explain this in simple, patient-friendly words:\n\n{input_text}", "Summarize Visit Notes": f"Summarize these visit notes into short bullet points:\n\n{input_text}", "Prescription Instructions": f"Rewrite these prescription instructions in clear steps:\n\n{input_text}", "Generate Discharge Advice": f"Write generic discharge advice (educational) for:\n\n{input_text}", } user_prompt = prompt_map.get(task_type, input_text) try: client = Groq(api_key=os.environ.get("GROQ_API_KEY")) chat_completion = client.chat.completions.create( messages=[ {"role": "system", "content": system_note}, {"role": "user", "content": user_prompt}, ], model="llama-3.3-70b-versatile", ) return chat_completion.choices[0].message.content except Exception as e: return f"❌ Groq API Error: {e}" # -------------------- Login (Demo) -------------------- PINS = {"Admin": "1234", "Receptionist": "1111", "Doctor": "2222"} def login(role, pin): if role not in PINS: return False, "❌ Invalid role." if str(pin).strip() != PINS[role]: return False, "❌ Wrong PIN." return True, f"βœ… Logged in as {role} πŸŽ‰" # -------------------- UI (GenZ + HCI) -------------------- CSS = """ :root { --radius: 18px; } .gradio-container { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial; } h1,h2,h3 { letter-spacing: -0.5px; } """ with gr.Blocks(css=CSS, title="SmartCare HMS (Student Edition)") as demo: auth_state = gr.State({"logged_in": False, "role": None}) gr.Markdown("# πŸ₯ SmartCare HMS (Student Edition)\nGen-Z clean UI ✨ + HCI-friendly flows βœ…") # LOGIN PANEL with gr.Row(): role_in = gr.Dropdown(["Admin", "Receptionist", "Doctor"], value="Admin", label="Role") pin_in = gr.Textbox(label="PIN", placeholder="Enter PIN", type="password") btn_login = gr.Button("Login πŸ”", variant="primary") login_msg = gr.Textbox(label="Status", interactive=False) app_wrap = gr.Column(visible=False) def do_login(role, pin, state): ok, msg = login(role, pin) state = dict(state) state["logged_in"] = ok state["role"] = role if ok else None return state, msg, gr.update(visible=ok) btn_login.click(do_login, inputs=[role_in, pin_in, auth_state], outputs=[auth_state, login_msg, app_wrap]) # APP (Hidden until login) with app_wrap: with gr.Tab("✨ Dashboard"): btn_refresh = gr.Button("πŸ”„ Refresh Stats", variant="primary") stats = gr.JSON(label="Hospital Snapshot") btn_refresh.click(fn=reports_summary, outputs=stats) with gr.Tab("πŸ§‘β€πŸ€β€πŸ§‘ Patients"): with gr.Row(): p_name = gr.Textbox(label="Name", placeholder="e.g., Ali Khan") p_age = gr.Textbox(label="Age", placeholder="e.g., 21") p_gender = gr.Dropdown(["Male","Female","Other"], value="Male", label="Gender") with gr.Row(): p_phone = gr.Textbox(label="Phone", placeholder="e.g., 03xx-xxxxxxx") p_addr = gr.Textbox(label="Address", placeholder="e.g., Lahore (optional)") btn_add_p = gr.Button("βž• Add Patient", variant="primary") p_msg = gr.Textbox(label="Status", interactive=False) btn_add_p.click(add_patient, [p_name, p_age, p_gender, p_phone, p_addr], p_msg) btn_list_p = gr.Button("πŸ“‹ Show Patients") p_table = gr.Dataframe(interactive=False, headers=["ID","Name","Age","Gender","Phone","Address","Created At"]) btn_list_p.click(list_patients, outputs=p_table) with gr.Row(): del_p_id = gr.Textbox(label="Delete Patient ID", placeholder="e.g., 1") btn_del_p = gr.Button("πŸ—‘οΈ Delete Patient", variant="stop") del_p_msg = gr.Textbox(label="Delete Status", interactive=False) btn_del_p.click(delete_patient, del_p_id, del_p_msg) with gr.Tab("🩺 Doctors"): gr.Markdown("### Add Doctor (with availability timing)") with gr.Row(): d_name = gr.Textbox(label="Doctor Name", placeholder="e.g., Dr. Sara") d_spec = gr.Textbox(label="Specialization", placeholder="e.g., Cardiologist") with gr.Row(): d_phone = gr.Textbox(label="Phone", placeholder="e.g., 03xx-xxxxxxx") d_from = gr.Textbox(label="Available From (HH:MM)", value="09:00") d_to = gr.Textbox(label="Available To (HH:MM)", value="17:00") btn_add_d = gr.Button("βž• Add Doctor", variant="primary") d_msg = gr.Textbox(label="Status", interactive=False) btn_add_d.click(add_doctor, [d_name, d_spec, d_phone, d_from, d_to], d_msg) btn_list_d = gr.Button("πŸ“‹ Show Doctors") d_table = gr.Dataframe(interactive=False, headers=["ID","Name","Specialization","Phone","From","To","Created At"]) btn_list_d.click(list_doctors, outputs=d_table) with gr.Row(): del_d_id = gr.Textbox(label="Delete Doctor ID", placeholder="e.g., 1") btn_del_d = gr.Button("πŸ—‘οΈ Delete Doctor", variant="stop") del_d_msg = gr.Textbox(label="Delete Status", interactive=False) btn_del_d.click(delete_doctor, del_d_id, del_d_msg) with gr.Tab("πŸ“… Appointments"): gr.Markdown("### 1) Search Doctor by Time β†’ 2) Select Doctor β†’ 3) Add Patient ID + Date + Time + Reason β†’ Book") # search by time with gr.Row(): search_time = gr.Textbox(label="Search Time (HH:MM)", placeholder="e.g., 14:30") btn_search_doc = gr.Button("πŸ”Ž Search Doctors", variant="primary") doc_search_msg = gr.Textbox(label="Search Status", interactive=False) doc_table = gr.Dataframe( interactive=False, headers=["ID","Name","Specialization","Phone","From","To"] ) doc_select = gr.Dropdown( choices=[], label="Select Doctor from Results" ) btn_search_doc.click( fn=search_doctors_by_time, inputs=[search_time], outputs=[doc_table, doc_select, doc_search_msg] ) # booking with gr.Row(): ap_pid = gr.Textbox(label="Patient ID", placeholder="e.g., 1") ap_date = gr.Textbox(label="Date (YYYY-MM-DD)", value=str(date.today())) ap_time = gr.Textbox(label="Time (HH:MM)", placeholder="e.g., 14:30") ap_reason = gr.Textbox(label="Reason (required)", placeholder="e.g., fever follow-up / routine checkup") btn_book = gr.Button("βœ… Book Appointment", variant="primary") ap_msg = gr.Textbox(label="Booking Status", interactive=False) btn_book.click( fn=book_appointment_admin, inputs=[ap_pid, doc_select, ap_date, ap_time, ap_reason], outputs=ap_msg ) # list appointments btn_list_ap = gr.Button("πŸ“‹ Show Appointments") ap_table = gr.Dataframe(interactive=False, headers=["Appt ID","Patient ID","Patient Name","Doctor ID","Doctor Name","Date","Time","Status","Reason"]) btn_list_ap.click(list_appointments, outputs=ap_table) # cancel with gr.Row(): ap_cancel_id = gr.Textbox(label="Cancel Appointment ID", placeholder="e.g., 3") btn_cancel = gr.Button("🧾 Cancel", variant="stop") cancel_msg = gr.Textbox(label="Cancel Status", interactive=False) btn_cancel.click(cancel_appointment, ap_cancel_id, cancel_msg) with gr.Row(): del_ap_id = gr.Textbox(label="Delete Appointment ID", placeholder="e.g., 2") btn_del_ap = gr.Button("πŸ—‘οΈ Delete Appointment", variant="stop") del_ap_msg = gr.Textbox(label="Delete Status", interactive=False) btn_del_ap.click(delete_appointment, del_ap_id, del_ap_msg) with gr.Tab("πŸ’Έ Billing"): b_pid = gr.Textbox(label="Patient ID", placeholder="e.g., 1") b_services = gr.Textbox(label="Services (comma-separated)", placeholder="Consultation, X-Ray, Medicine") b_amounts = gr.Textbox(label="Amounts (comma-separated)", placeholder="1000, 1500, 800") btn_bill = gr.Button("🧾 Generate Bill", variant="primary") b_msg = gr.Textbox(label="Status", interactive=False) btn_bill.click(create_bill, [b_pid, b_services, b_amounts], b_msg) btn_list_b = gr.Button("πŸ“‹ Show Bills") b_table = gr.Dataframe(interactive=False, headers=["Bill ID","Patient ID","Patient Name","Services","Total","Created At"]) btn_list_b.click(list_bills, outputs=b_table) with gr.Row(): del_b_id = gr.Textbox(label="Delete Bill ID", placeholder="e.g., 1") btn_del_b = gr.Button("πŸ—‘οΈ Delete Bill", variant="stop") del_b_msg = gr.Textbox(label="Delete Status", interactive=False) btn_del_b.click(delete_bill, del_b_id, del_b_msg) with gr.Tab("πŸ€– AI Assistant"): task = gr.Dropdown( ["Explain Diagnosis (Simple)", "Summarize Visit Notes", "Prescription Instructions", "Generate Discharge Advice"], value="Summarize Visit Notes", label="Task" ) ai_input = gr.Textbox(label="Input Text", lines=6, placeholder="Paste notes here…") btn_ai = gr.Button("⚑ Generate", variant="primary") ai_output = gr.Textbox(label="AI Output", lines=10) btn_ai.click(groq_assistant, [task, ai_input], ai_output) demo.launch(share=True)