feat(backend): add GET /api/{remittances,providers,activity}
This commit is contained in:
@@ -450,4 +450,75 @@ def list_claims(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/remittances")
|
||||||
|
def list_remittances(
|
||||||
|
batch_id: str | None = Query(None),
|
||||||
|
payer: str | None = Query(None),
|
||||||
|
claim_id: str | None = Query(None),
|
||||||
|
date_from: str | None = Query(None),
|
||||||
|
date_to: str | None = Query(None),
|
||||||
|
sort: str | None = Query(None),
|
||||||
|
order: str = Query("desc"),
|
||||||
|
limit: int = Query(100, ge=1, le=1000),
|
||||||
|
offset: int = Query(0, ge=0),
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
common = dict(
|
||||||
|
batch_id=batch_id,
|
||||||
|
payer=payer,
|
||||||
|
claim_id=claim_id,
|
||||||
|
date_from=date_from,
|
||||||
|
date_to=date_to,
|
||||||
|
)
|
||||||
|
items = list(store.iter_remittances(
|
||||||
|
sort=sort, order=order, limit=limit, offset=offset, **common,
|
||||||
|
))
|
||||||
|
total = len(list(store.iter_remittances(**common)))
|
||||||
|
return {
|
||||||
|
"items": items,
|
||||||
|
"total": total,
|
||||||
|
"returned": len(items),
|
||||||
|
"has_more": total > offset + len(items),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/providers")
|
||||||
|
def list_providers(
|
||||||
|
npi: str | None = Query(None),
|
||||||
|
state: str | None = Query(None),
|
||||||
|
limit: int = Query(100, ge=1, le=1000),
|
||||||
|
offset: int = Query(0, ge=0),
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
items = store.distinct_providers()
|
||||||
|
if npi is not None:
|
||||||
|
items = [p for p in items if p["npi"] == npi]
|
||||||
|
if state is not None:
|
||||||
|
items = [p for p in items if p.get("state") == state]
|
||||||
|
paged = items[offset:offset + limit]
|
||||||
|
return {
|
||||||
|
"items": paged,
|
||||||
|
"total": len(items),
|
||||||
|
"returned": len(paged),
|
||||||
|
"has_more": len(items) > offset + len(paged),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/activity")
|
||||||
|
def list_activity(
|
||||||
|
kind: str | None = Query(None),
|
||||||
|
since: str | None = Query(None),
|
||||||
|
limit: int = Query(200, ge=1, le=500),
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
events = store.recent_activity(limit=limit)
|
||||||
|
if kind is not None:
|
||||||
|
events = [e for e in events if e["kind"] == kind]
|
||||||
|
if since is not None:
|
||||||
|
events = [e for e in events if e["timestamp"] >= since]
|
||||||
|
return {
|
||||||
|
"items": events,
|
||||||
|
"total": len(events),
|
||||||
|
"returned": len(events),
|
||||||
|
"has_more": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["app"]
|
__all__ = ["app"]
|
||||||
|
|||||||
@@ -125,3 +125,62 @@ def test_claims_pagination_limit_offset(seeded_store):
|
|||||||
body1 = resp1.json()
|
body1 = resp1.json()
|
||||||
assert body1["returned"] == 1
|
assert body1["returned"] == 1
|
||||||
assert body1["has_more"] is True
|
assert body1["has_more"] is True
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# /api/remittances
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_remittances_returns_empty_list_when_no_parses(client: TestClient):
|
||||||
|
resp = client.get("/api/remittances")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert body["items"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_remittances_filters_by_batch_id(seeded_store):
|
||||||
|
"""No 835 fixture parsed yet — but the endpoint should return 200 with empty."""
|
||||||
|
resp = seeded_store.get("/api/remittances?batch_id=nope")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.json()["items"] == []
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# /api/providers
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_providers_empty_when_no_parses(client: TestClient):
|
||||||
|
resp = client.get("/api/providers")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert body["items"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_providers_returns_one_per_distinct_npi(seeded_store):
|
||||||
|
resp = seeded_store.get("/api/providers")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert len(body["items"]) == 1
|
||||||
|
assert body["items"][0]["npi"] == "1881068062"
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# /api/activity
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_activity_empty_when_no_parses(client: TestClient):
|
||||||
|
resp = client.get("/api/activity")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert body["items"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_activity_returns_one_event_per_claim(seeded_store):
|
||||||
|
resp = seeded_store.get("/api/activity")
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert len(body["items"]) == 2 # co_medicaid_837p.txt has 2 claims
|
||||||
|
assert all(e["kind"] == "claim_submitted" for e in body["items"])
|
||||||
|
|||||||
Reference in New Issue
Block a user