Spaces:
Sleeping
Sleeping
| from flask import Flask, request, jsonify, render_template_string | |
| import sqlite3 | |
| import datetime | |
| app = Flask(__name__) | |
| DB_NAME = "hospital.db" | |
| def init_db(): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS patients | |
| (bed_id INTEGER PRIMARY KEY, name TEXT, age INTEGER, gender TEXT, disease TEXT)''') | |
| c.execute('''CREATE TABLE IF NOT EXISTS vitals | |
| (id INTEGER PRIMARY KEY AUTOINCREMENT, bed_id INTEGER, bpm REAL, spo2 REAL, temp REAL, ecg TEXT, timestamp TEXT)''') | |
| conn.commit() | |
| conn.close() | |
| init_db() | |
| # --- MAIN DASHBOARD TEMPLATE --- | |
| HTML_TEMPLATE = ''' | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Hospital Ward Control Dashboard</title> | |
| <style> | |
| body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; padding: 20px; } | |
| .container { max-width: 1000px; margin: 0 auto; background: white; padding: 25px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } | |
| table { width: 100%; border-collapse: collapse; margin-top: 15px; margin-bottom: 30px; } | |
| th, td { border: 1px solid #ddd; padding: 12px; text-align: left; } | |
| th { background-color: #3498db; color: white; } | |
| input, select { padding: 10px; border: 1px solid #ccc; border-radius: 4px; width: 140px; } | |
| button { background-color: #2ecc71; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; } | |
| .btn-view { background-color: #f39c12; color: white; padding: 6px 12px; text-decoration: none; border-radius: 4px; font-weight: bold; font-size: 14px; } | |
| .btn-remove { color: red; font-weight: bold; text-decoration: none; margin-left: 15px; font-size: 14px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h2>🏥 Autonomous Medical Assistant - Ward Dashboard</h2> | |
| <h3>Assign Patient to Bed</h3> | |
| <form action="/add_patient" method="POST" style="margin-bottom: 30px;"> | |
| Bed No: <input type="number" name="bed_id" min="1" style="width:60px;" required> | |
| Name: <input type="text" name="name" required> | |
| Age: <input type="number" name="age" style="width:60px;" required> | |
| Gen: <select name="gender" style="width:60px;"><option>M</option><option>F</option></select> | |
| Disease: <input type="text" name="disease" required> | |
| <button type="submit">Assign Patient</button> | |
| </form> | |
| <h3>Active Patients in Ward</h3> | |
| <table> | |
| <tr><th>Bed No</th><th>Patient Name</th><th>Age</th><th>Gender</th><th>Diagnosed Disease</th><th>Actions</th></tr> | |
| {% for p in patients %} | |
| <tr> | |
| <td><b>Bed {{ p[0] }}</b></td><td>{{ p[1] }}</td><td>{{ p[2] }}</td><td>{{ p[3] }}</td><td>{{ p[4] }}</td> | |
| <td> | |
| <a class="btn-view" href="/report/{{ p[0] }}">View Report</a> | |
| <a class="btn-remove" href="/remove/{{ p[0] }}">Discharge</a> | |
| </td> | |
| </tr> | |
| {% endfor %} | |
| {% if not patients %} | |
| <tr><td colspan="6" style="text-align: center; color: #7f8c8d;">No active patients in the ward.</td></tr> | |
| {% endif %} | |
| </table> | |
| </div> | |
| </body> | |
| </html> | |
| ''' | |
| # --- PATIENT REPORT TEMPLATE (NOW WITH CHART.JS FOR ECG) --- | |
| REPORT_TEMPLATE = ''' | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Patient Report - Bed {{ bed_id }}</title> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; padding: 20px; } | |
| .container { max-width: 900px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } | |
| .header-card { background: #34495e; color: white; padding: 20px; border-radius: 8px; margin-bottom: 25px; } | |
| .header-card h2 { margin: 0 0 10px 0; color: #ecf0f1; } | |
| .header-card p { margin: 5px 0; font-size: 16px; color: #bdc3c7; } | |
| .header-card span { color: #fff; font-weight: bold; } | |
| table { width: 100%; border-collapse: collapse; margin-top: 15px; } | |
| th, td { border: 1px solid #ddd; padding: 12px; text-align: center; } | |
| th { background-color: #2c3e50; color: white; } | |
| tr:nth-child(even) { background-color: #f9f9f9; } | |
| .btn-back { display: inline-block; background-color: #3498db; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin-bottom: 20px; font-weight: bold; } | |
| .btn-graph { background-color: #e74c3c; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-weight: bold; } | |
| .btn-graph:hover { background-color: #c0392b; } | |
| .graph-row { display: none; background-color: #fff !important; } | |
| .canvas-container { width: 100%; height: 250px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <a href="/" class="btn-back">⬅ Back to Dashboard</a> | |
| <div class="header-card"> | |
| <h2>Patient Portfolio - Bed {{ bed_id }}</h2> | |
| <p>Name: <span>{{ patient[1] }}</span> | Age: <span>{{ patient[2] }}</span> | Gender: <span>{{ patient[3] }}</span></p> | |
| <p>Diagnosis: <span>{{ patient[4] }}</span></p> | |
| </div> | |
| <h3>Vitals History</h3> | |
| <table> | |
| <tr><th>Timestamp</th><th>Heart Rate</th><th>SpO2 Level</th><th>Temperature</th><th>ECG Trace</th></tr> | |
| {% for v in vitals %} | |
| <tr> | |
| <td>{{ v[6] }}</td> | |
| <td><b>{% if v[2] > 0 %}{{ v[2] }} BPM{% else %}None{% endif %}</b></td> | |
| <td><b>{% if v[3] > 0 %}{{ v[3] }} %{% else %}None{% endif %}</b></td> | |
| <td><b>{% if v[4] > 0 %}{{ v[4] }} °C{% else %}None{% endif %}</b></td> | |
| <td> | |
| {% if v[5] == '[0]' or v[5] == '0' %} | |
| <span style="color:#7f8c8d;">Not Recorded</span> | |
| {% else %} | |
| <button class="btn-graph" onclick="toggleGraph('ecgRow{{ v[0] }}', 'ecgCanvas{{ v[0] }}', {{ v[5] }})">View Graph</button> | |
| {% endif %} | |
| </td> | |
| </tr> | |
| {% if v[5] != '[0]' and v[5] != '0' %} | |
| <tr id="ecgRow{{ v[0] }}" class="graph-row"> | |
| <td colspan="5"> | |
| <div class="canvas-container"> | |
| <canvas id="ecgCanvas{{ v[0] }}"></canvas> | |
| </div> | |
| </td> | |
| </tr> | |
| {% endif %} | |
| {% endfor %} | |
| {% if not vitals %} | |
| <tr><td colspan="5" style="text-align: center; color: #7f8c8d;">No vitals recorded yet.</td></tr> | |
| {% endif %} | |
| </table> | |
| </div> | |
| <script> | |
| // Object to store chart instances so we don't recreate them on every click | |
| const charts = {}; | |
| function toggleGraph(rowId, canvasId, ecgData) { | |
| const row = document.getElementById(rowId); | |
| const canvas = document.getElementById(canvasId); | |
| if (row.style.display === "none" || row.style.display === "") { | |
| row.style.display = "table-row"; | |
| // Draw chart only if it hasn't been drawn yet | |
| if (!charts[canvasId]) { | |
| const ctx = canvas.getContext('2d'); | |
| // Generate X-axis labels (just numbers from 1 to 500) | |
| const labels = Array.from({length: ecgData.length}, (_, i) => i + 1); | |
| charts[canvasId] = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| label: 'ECG Signal (20s Trace)', | |
| data: ecgData, | |
| borderColor: '#2ecc71', | |
| borderWidth: 1.5, | |
| pointRadius: 0, // Hides the dots to make it look like a smooth medical wave | |
| fill: false, | |
| tension: 0.1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: true } | |
| }, | |
| scales: { | |
| x: { display: false }, // Hide numbers on X axis | |
| y: { | |
| suggestedMin: 0, | |
| suggestedMax: 4095, | |
| title: { display: true, text: 'ADC Value (0-4095)' } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| } else { | |
| row.style.display = "none"; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| ''' | |
| def index(): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("SELECT * FROM patients ORDER BY bed_id ASC") | |
| patients = c.fetchall() | |
| conn.close() | |
| return render_template_string(HTML_TEMPLATE, patients=patients) | |
| def view_report(bed_id): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("SELECT * FROM patients WHERE bed_id=?", (bed_id,)) | |
| patient = c.fetchone() | |
| if not patient: | |
| conn.close() | |
| return "Patient not found. They might have been discharged.", 404 | |
| c.execute("SELECT * FROM vitals WHERE bed_id=? ORDER BY timestamp DESC", (bed_id,)) | |
| vitals = c.fetchall() | |
| conn.close() | |
| return render_template_string(REPORT_TEMPLATE, patient=patient, vitals=vitals, bed_id=bed_id) | |
| def add_patient(): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("REPLACE INTO patients (bed_id, name, age, gender, disease) VALUES (?, ?, ?, ?, ?)", | |
| (request.form['bed_id'], request.form['name'][:15], request.form['age'], request.form['gender'], request.form['disease'][:15])) | |
| conn.commit() | |
| conn.close() | |
| return index() | |
| def remove_patient(bed_id): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("DELETE FROM patients WHERE bed_id=?", (bed_id,)) | |
| c.execute("DELETE FROM vitals WHERE bed_id=?", (bed_id,)) | |
| conn.commit() | |
| conn.close() | |
| return index() | |
| def get_status(): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("SELECT COUNT(*) FROM patients") | |
| count = c.fetchone()[0] | |
| conn.close() | |
| return jsonify({"total_patients": count}) | |
| def get_patient(bed_id): | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("SELECT name, age, gender, disease FROM patients WHERE bed_id=?", (bed_id,)) | |
| row = c.fetchone() | |
| conn.close() | |
| if row: return jsonify({"found": True, "name": row[0], "age": row[1], "gender": row[2], "disease": row[3]}) | |
| return jsonify({"found": False}) | |
| def save_vitals(): | |
| data = request.json | |
| timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| conn = sqlite3.connect(DB_NAME) | |
| c = conn.cursor() | |
| c.execute("INSERT INTO vitals (bed_id, bpm, spo2, temp, ecg, timestamp) VALUES (?, ?, ?, ?, ?, ?)", | |
| (data['bed_id'], data['bpm'], data['spo2'], data['temp'], str(data['ecg']), timestamp)) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({"status": "success"}) | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860) |