medbillcodes-api / app /sqlite_store.py
medbillcodes-deploy
Deploy cloud pilot API
5cceba0
Raw
History Blame Contribute Delete
7.53 kB
"""SQLite FTS5 fee-code store for the no-OpenSearch pilot backend."""
from __future__ import annotations
import json
import logging
import re
import sqlite3
from pathlib import Path
from .config import settings
logger = logging.getLogger(__name__)
_FTS_SAFE = re.compile(r"[^A-Za-z0-9]+")
def db_path() -> Path:
return Path(settings.sqlite_index_path)
def connect() -> sqlite3.Connection:
path = db_path()
path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(path), check_same_thread=False)
conn.row_factory = sqlite3.Row
return conn
def ensure_schema(conn: sqlite3.Connection | None = None) -> None:
own = conn is None
conn = conn or connect()
try:
conn.executescript(
"""
CREATE TABLE IF NOT EXISTS fee_codes (
billing_code TEXT PRIMARY KEY,
description_text TEXT NOT NULL DEFAULT '',
rules_and_constraints TEXT NOT NULL DEFAULT '',
parent_section TEXT NOT NULL DEFAULT '',
base_fee_cad REAL NOT NULL DEFAULT 0,
reference TEXT,
differentiators_json TEXT,
fee_components_json TEXT,
effective_date TEXT,
termination_date TEXT,
in_current_schedule INTEGER NOT NULL DEFAULT 0,
has_description INTEGER NOT NULL DEFAULT 0
);
CREATE VIRTUAL TABLE IF NOT EXISTS fee_codes_fts USING fts5(
billing_code,
description_text,
rules_and_constraints,
parent_section,
tokenize = 'porter'
);
"""
)
conn.commit()
finally:
if own:
conn.close()
def _row_to_doc(row: sqlite3.Row | dict) -> dict:
data = dict(row)
diffs = None
fees = None
if data.get("differentiators_json"):
try:
diffs = json.loads(data["differentiators_json"])
except json.JSONDecodeError:
diffs = None
if data.get("fee_components_json"):
try:
fees = json.loads(data["fee_components_json"])
except json.JSONDecodeError:
fees = None
return {
"billing_code": data["billing_code"],
"description_text": data.get("description_text") or "",
"rules_and_constraints": data.get("rules_and_constraints") or "",
"parent_section": data.get("parent_section") or "",
"base_fee_cad": float(data.get("base_fee_cad") or 0),
"reference": data.get("reference"),
"differentiators": diffs,
"fee_components": fees,
"effective_date": data.get("effective_date"),
"termination_date": data.get("termination_date"),
"in_current_schedule": bool(data.get("in_current_schedule")),
"has_description": bool(data.get("has_description")),
"score": data.get("score"),
}
def replace_all(docs: list[dict]) -> int:
"""Rebuild the SQLite index from schedule documents."""
conn = connect()
try:
conn.execute("DROP TABLE IF EXISTS fee_codes_fts")
conn.execute("DROP TABLE IF EXISTS fee_codes")
ensure_schema(conn)
rows = []
fts_rows = []
for doc in docs:
rows.append(
(
doc["billing_code"],
doc.get("description_text") or "",
doc.get("rules_and_constraints") or "",
doc.get("parent_section") or "",
float(doc.get("base_fee_cad") or 0),
doc.get("reference"),
json.dumps(doc.get("differentiators"))
if doc.get("differentiators")
else None,
json.dumps(doc.get("fee_components"))
if doc.get("fee_components")
else None,
doc.get("effective_date"),
doc.get("termination_date"),
1 if doc.get("in_current_schedule") else 0,
1 if doc.get("has_description") else 0,
)
)
fts_rows.append(
(
doc["billing_code"],
doc.get("description_text") or "",
doc.get("rules_and_constraints") or "",
doc.get("parent_section") or "",
)
)
conn.executemany(
"""
INSERT INTO fee_codes (
billing_code, description_text, rules_and_constraints, parent_section,
base_fee_cad, reference, differentiators_json, fee_components_json,
effective_date, termination_date, in_current_schedule, has_description
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
rows,
)
conn.executemany(
"""
INSERT INTO fee_codes_fts (
billing_code, description_text, rules_and_constraints, parent_section
) VALUES (?, ?, ?, ?)
""",
fts_rows,
)
conn.commit()
n = conn.execute("SELECT COUNT(*) FROM fee_codes").fetchone()[0]
logger.info("SQLite fee index rebuilt with %d codes at %s", n, db_path())
return int(n)
finally:
conn.close()
def count_codes() -> int:
if not db_path().exists():
return 0
conn = connect()
try:
ensure_schema(conn)
return int(conn.execute("SELECT COUNT(*) FROM fee_codes").fetchone()[0])
finally:
conn.close()
def get_code(code: str) -> dict | None:
if not code:
return None
conn = connect()
try:
ensure_schema(conn)
row = conn.execute(
"SELECT *, NULL AS score FROM fee_codes WHERE billing_code = ?",
(code.upper(),),
).fetchone()
return _row_to_doc(row) if row else None
finally:
conn.close()
def all_current_docs() -> list[dict]:
conn = connect()
try:
ensure_schema(conn)
rows = conn.execute(
"SELECT *, NULL AS score FROM fee_codes WHERE in_current_schedule = 1"
).fetchall()
return [_row_to_doc(r) for r in rows]
finally:
conn.close()
def _fts_query(text: str) -> str:
tokens = [t for t in _FTS_SAFE.split(text.upper()) if len(t) >= 2]
tokens = tokens[:24]
if not tokens:
return "code OR visit OR assessment"
return " OR ".join(tokens)
def fts_search(query_text: str, *, top_k: int = 40) -> list[dict]:
"""Lexical search over the fee schedule (FTS5 BM25 rank)."""
conn = connect()
try:
ensure_schema(conn)
q = _fts_query(query_text)
rows = conn.execute(
"""
SELECT c.*, bm25(fee_codes_fts) AS score
FROM fee_codes_fts
JOIN fee_codes c ON c.billing_code = fee_codes_fts.billing_code
WHERE fee_codes_fts MATCH ?
AND c.in_current_schedule = 1
ORDER BY score
LIMIT ?
""",
(q, top_k),
).fetchall()
docs = []
for r in rows:
doc = _row_to_doc(r)
raw = doc.get("score")
doc["score"] = float(-raw) if raw is not None else 0.0
docs.append(doc)
return docs
except sqlite3.OperationalError as exc:
logger.warning("FTS search failed (%s); returning empty", exc)
return []
finally:
conn.close()