File size: 10,156 Bytes
2306d9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
"""
database.py
-----------
SQLite database for Radiology AI Report System.
Stores all generated reports with patient info, findings, and metadata.

Tables:
  patients β€” patient demographics
  reports  β€” generated radiology reports
  images   β€” uploaded image metadata
"""

import json
import logging
import sqlite3
from datetime import datetime
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

DB_PATH = Path("data/radiology.db")


def get_connection() -> sqlite3.Connection:
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(str(DB_PATH), check_same_thread=False)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    return conn


def init_db():
    """Create all tables if they don't exist."""
    conn = get_connection()
    try:
        conn.executescript("""
            CREATE TABLE IF NOT EXISTS patients (
                id              INTEGER PRIMARY KEY AUTOINCREMENT,
                patient_name    TEXT    NOT NULL,
                age             INTEGER NOT NULL,
                gender          TEXT    NOT NULL,
                created_at      TEXT    DEFAULT (datetime('now')),
                updated_at      TEXT    DEFAULT (datetime('now'))
            );

            CREATE TABLE IF NOT EXISTS reports (
                id                   INTEGER PRIMARY KEY AUTOINCREMENT,
                report_uuid          TEXT    NOT NULL UNIQUE,
                patient_id           INTEGER NOT NULL REFERENCES patients(id),
                patient_name         TEXT    NOT NULL,
                age                  INTEGER NOT NULL,
                gender               TEXT    NOT NULL,
                symptoms             TEXT,
                clinical_history     TEXT,
                modality             TEXT,
                body_region          TEXT,
                view                 TEXT,
                examination_details  TEXT,
                clinical_information TEXT,
                findings             TEXT,
                impression           TEXT,
                recommendations      TEXT,
                confidence_score     REAL,
                generation_time_sec  REAL,
                image_caption        TEXT,
                validation_json      TEXT,
                medical_coding_json  TEXT,
                created_at           TEXT    DEFAULT (datetime('now'))
            );

            CREATE INDEX IF NOT EXISTS idx_reports_uuid
                ON reports(report_uuid);
            CREATE INDEX IF NOT EXISTS idx_reports_patient
                ON reports(patient_id);
            CREATE INDEX IF NOT EXISTS idx_reports_created
                ON reports(created_at);
            CREATE INDEX IF NOT EXISTS idx_patients_name
                ON patients(patient_name);
        """)
        conn.commit()
        logger.info(f"Database initialised at {DB_PATH}")
    finally:
        conn.close()


# ═══════════════════════════════════════════════════════════════════════════
# Patient operations
# ═══════════════════════════════════════════════════════════════════════════

def create_or_get_patient(name: str, age: int, gender: str) -> int:
    """
    Creates a new patient record or returns existing ID
    if same name+age+gender exists.
    """
    conn = get_connection()
    try:
        row = conn.execute(
            "SELECT id FROM patients WHERE patient_name=? AND age=? AND gender=?",
            (name.strip(), age, gender)
        ).fetchone()
        if row:
            return row["id"]
        cur = conn.execute(
            "INSERT INTO patients (patient_name, age, gender) VALUES (?,?,?)",
            (name.strip(), age, gender)
        )
        conn.commit()
        return cur.lastrowid
    finally:
        conn.close()


def search_patients(query: str) -> list:
    """Search patients by name."""
    conn = get_connection()
    try:
        rows = conn.execute(
            "SELECT DISTINCT patient_name, age, gender, COUNT(*) as report_count "
            "FROM reports WHERE patient_name LIKE ? "
            "GROUP BY patient_name, age, gender "
            "ORDER BY created_at DESC LIMIT 20",
            (f"%{query}%",)
        ).fetchall()
        return [dict(r) for r in rows]
    finally:
        conn.close()


# ═══════════════════════════════════════════════════════════════════════════
# Report operations
# ═══════════════════════════════════════════════════════════════════════════

def save_report(payload: dict, patient_name: str) -> int:
    """Save a complete report payload to the database. Returns row ID."""
    patient = payload.get("patient", {})
    imaging = payload.get("imaging", {})
    report  = payload.get("report",  {})
    valid   = payload.get("validation", {})
    coding  = payload.get("medical_coding", {})

    age    = patient.get("age", 0)
    gender = patient.get("gender", "Unknown")

    patient_id = create_or_get_patient(patient_name, age, gender)

    conn = get_connection()
    try:
        cur = conn.execute("""
            INSERT INTO reports (
                report_uuid, patient_id, patient_name, age, gender,
                symptoms, clinical_history,
                modality, body_region, view,
                examination_details, clinical_information,
                findings, impression, recommendations,
                confidence_score, generation_time_sec, image_caption,
                validation_json, medical_coding_json
            ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """, (
            payload.get("report_id", ""),
            patient_id,
            patient_name.strip(),
            age,
            gender,
            patient.get("symptoms", ""),
            patient.get("clinical_history", ""),
            imaging.get("modality", ""),
            imaging.get("body_region", ""),
            imaging.get("view", ""),
            report.get("examination_details", ""),
            report.get("clinical_information", ""),
            report.get("findings", ""),
            report.get("impression", ""),
            report.get("recommendations", ""),
            report.get("confidence_score", 0.0),
            report.get("generation_time_seconds", 0.0),
            payload.get("image_caption", ""),
            json.dumps(valid),
            json.dumps(coding),
        ))
        conn.commit()
        logger.info(f"Report saved β€” DB row: {cur.lastrowid} | patient: {patient_name}")
        return cur.lastrowid
    finally:
        conn.close()


def get_all_reports(limit: int = 50, offset: int = 0) -> list:
    """Get all reports ordered by newest first."""
    conn = get_connection()
    try:
        rows = conn.execute("""
            SELECT report_uuid, patient_name, age, gender,
                   modality, body_region, confidence_score,
                   created_at
            FROM reports
            ORDER BY created_at DESC
            LIMIT ? OFFSET ?
        """, (limit, offset)).fetchall()
        return [dict(r) for r in rows]
    finally:
        conn.close()


def get_report_by_uuid(uuid: str) -> Optional[dict]:
    """Get full report by UUID."""
    conn = get_connection()
    try:
        row = conn.execute(
            "SELECT * FROM reports WHERE report_uuid=?", (uuid,)
        ).fetchone()
        if not row:
            return None
        result = dict(row)
        # Parse JSON fields
        for field in ("validation_json", "medical_coding_json"):
            try:
                result[field] = json.loads(result[field] or "{}")
            except Exception:
                result[field] = {}
        return result
    finally:
        conn.close()


def get_reports_by_patient(patient_name: str) -> list:
    """Get all reports for a specific patient."""
    conn = get_connection()
    try:
        rows = conn.execute("""
            SELECT report_uuid, patient_name, age, gender,
                   modality, body_region, confidence_score,
                   findings, impression, created_at
            FROM reports
            WHERE patient_name LIKE ?
            ORDER BY created_at DESC
        """, (f"%{patient_name}%",)).fetchall()
        return [dict(r) for r in rows]
    finally:
        conn.close()


def get_db_stats() -> dict:
    """Get database statistics."""
    conn = get_connection()
    try:
        total_reports   = conn.execute("SELECT COUNT(*) FROM reports").fetchone()[0]
        total_patients  = conn.execute("SELECT COUNT(DISTINCT patient_name) FROM reports").fetchone()[0]
        avg_confidence  = conn.execute("SELECT AVG(confidence_score) FROM reports").fetchone()[0]
        modality_counts = conn.execute(
            "SELECT modality, COUNT(*) as cnt FROM reports GROUP BY modality ORDER BY cnt DESC"
        ).fetchall()
        return {
            "total_reports":   total_reports,
            "total_patients":  total_patients,
            "avg_confidence":  round(avg_confidence or 0, 2),
            "modality_counts": {r["modality"]: r["cnt"] for r in modality_counts},
        }
    finally:
        conn.close()


def delete_report(uuid: str) -> bool:
    """Delete a report by UUID."""
    conn = get_connection()
    try:
        conn.execute("DELETE FROM reports WHERE report_uuid=?", (uuid,))
        conn.commit()
        return True
    except Exception as e:
        logger.error(f"Delete failed: {e}")
        return False
    finally:
        conn.close()


# Auto-initialise on import
init_db()