Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ EHRBASE_PASSWORD=
# CORS origins - for API service (JSON array)
CORS_ORIGINS=["http://localhost:5173"]

# Terminology server (Snowstorm Lite) - for API service
TERMINOLOGY_SERVER_URL=http://localhost:8081/fhir
TERMINOLOGY_SERVER_TIMEOUT=30
TERMINOLOGY_CACHE_TTL=300
TERMINOLOGY_VALIDATION_MODE=warn
TERMINOLOGY_ADMIN_PASSWORD=admin
Comment on lines +15 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reorder terminology env keys to satisfy dotenv-linter.

The linter warns about ordering; if enforced, this will break CI. Consider reordering to the expected sequence.

🔧 Proposed reorder
-TERMINOLOGY_SERVER_URL=http://localhost:8081/fhir
-TERMINOLOGY_SERVER_TIMEOUT=30
-TERMINOLOGY_CACHE_TTL=300
-TERMINOLOGY_VALIDATION_MODE=warn
-TERMINOLOGY_ADMIN_PASSWORD=admin
+TERMINOLOGY_ADMIN_PASSWORD=admin
+TERMINOLOGY_CACHE_TTL=300
+TERMINOLOGY_SERVER_TIMEOUT=30
+TERMINOLOGY_SERVER_URL=http://localhost:8081/fhir
+TERMINOLOGY_VALIDATION_MODE=warn
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Terminology server (Snowstorm Lite) - for API service
TERMINOLOGY_SERVER_URL=http://localhost:8081/fhir
TERMINOLOGY_SERVER_TIMEOUT=30
TERMINOLOGY_CACHE_TTL=300
TERMINOLOGY_VALIDATION_MODE=warn
TERMINOLOGY_ADMIN_PASSWORD=admin
# Terminology server (Snowstorm Lite) - for API service
TERMINOLOGY_ADMIN_PASSWORD=admin
TERMINOLOGY_CACHE_TTL=300
TERMINOLOGY_SERVER_TIMEOUT=30
TERMINOLOGY_SERVER_URL=http://localhost:8081/fhir
TERMINOLOGY_VALIDATION_MODE=warn
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 17-17: [UnorderedKey] The TERMINOLOGY_SERVER_TIMEOUT key should go before the TERMINOLOGY_SERVER_URL key

(UnorderedKey)


[warning] 18-18: [UnorderedKey] The TERMINOLOGY_CACHE_TTL key should go before the TERMINOLOGY_SERVER_TIMEOUT key

(UnorderedKey)


[warning] 20-20: [UnorderedKey] The TERMINOLOGY_ADMIN_PASSWORD key should go before the TERMINOLOGY_CACHE_TTL key

(UnorderedKey)

🤖 Prompt for AI Agents
In @.env.example around lines 15 - 20, Reorder the TERMINOLOGY_* environment
variables in .env.example to match dotenv-linter's expected sequence;
specifically, place the keys in alphabetical order: TERMINOLOGY_ADMIN_PASSWORD,
TERMINOLOGY_CACHE_TTL, TERMINOLOGY_SERVER_TIMEOUT, TERMINOLOGY_SERVER_URL,
TERMINOLOGY_VALIDATION_MODE so the linter no longer flags ordering issues.


# Frontend - for web service
VITE_API_URL=http://localhost:8000

Expand Down
12 changes: 12 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,14 @@ See [ADR-0005](./docs/adr/0005-synthetic-data-generation.md) for implementation
# Check EHRBase status (wait 30-60s after docker compose up)
curl http://localhost:8080/ehrbase/rest/status

# Check Terminology Server status
curl http://localhost:8081/fhir/metadata

# View logs
docker compose logs -f ehrbase
docker compose logs -f ehrbase-db
docker compose logs -f app-db
docker compose logs -f terminology-server

# Rebuild specific service
docker compose up -d --build ehrbase
Expand Down Expand Up @@ -158,6 +162,11 @@ Each domain module (e.g., `api/src/patients/`) follows this structure:
- `templates.py`: Template management utilities
- `queries.py`: AQL query builders and executors

### Terminology Integration (`api/src/terminology/`)
- `client.py`: Async FHIR terminology client with TTL cache (singleton: `terminology_client`)
- `router.py`: REST endpoints for lookup, validate, expand, search, subsumes
- `schemas.py`: Pydantic models for terminology responses

### Frontend Structure (`web/src/`)
- `pages/`: Vue components for routes
- `stores/`: Pinia state management
Expand All @@ -174,6 +183,7 @@ EHRBase takes 30-60 seconds to become available after `docker compose up`. Alway
- Frontend dev server: `http://localhost:5173`
- Backend API: `http://localhost:8000`
- EHRBase REST API: `http://localhost:8080/ehrbase/rest`
- Terminology Server (FHIR): `http://localhost:8081/fhir`
- App PostgreSQL: `localhost:5454`
- EHRBase PostgreSQL: `localhost:5433`

Expand All @@ -183,6 +193,8 @@ Copy `.env.example` to `.env` (locally) and configure:
- `EHRBASE_URL`: EHRBase REST API endpoint
- `CORS_ORIGINS`: JSON array of allowed origins
- `VITE_API_URL`: Frontend API base URL
- `TERMINOLOGY_SERVER_URL`: FHIR terminology server endpoint
- `TERMINOLOGY_ADMIN_PASSWORD`: Snowstorm Lite admin password

### Coding Standards
- **Python**: Type hints required everywhere (enforced by mypy config), all functions must be `async`
Expand Down
7 changes: 7 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Personal learning project exploring modern clinical data architecture.
- **Frontend**: Vue 3 + TypeScript + Vite + shadcn-vue + Tailwind + Pinia
- **Backend**: FastAPI + Python 3.11+ + Pydantic
- **Clinical Data**: EHRBase (openEHR repository)
- **Terminology**: Snowstorm Lite (FHIR terminology server for SNOMED CT)
- **App Database**: PostgreSQL via Prisma (prisma-client-py)
- **Deployment**: Railway

Expand Down Expand Up @@ -44,3 +45,9 @@ Personal learning project exploring modern clinical data architecture.
- `GET /api/patients/{id}` - Get patient by ID
- `GET /api/patients/mrn/{mrn}` - Get patient by MRN
- `PATCH /api/patients/{id}` - Update patient
- `GET /api/terminology/lookup` - Look up a terminology code
- `GET /api/terminology/validate` - Validate a code
- `GET /api/terminology/expand` - Expand a value set
- `GET /api/terminology/search` - Search for codes by term
- `GET /api/terminology/subsumes` - Test subsumption relationship
- `GET /api/terminology/health` - Terminology server health check
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Deploy your own instance with one click using the Railway template above. Railwa
| Frontend | Vue 3 + TypeScript + Vite + shadcn-vue + Tailwind + Pinia |
| Backend | FastAPI + Python 3.11+ + Pydantic |
| Clinical Data | EHRBase (openEHR repository) |
| Terminology | Snowstorm Lite (FHIR terminology server) |
| App Database | PostgreSQL via Prisma (prisma-client-py) |
| Infrastructure | Docker Compose |
| Deployment | Railway |
Expand Down Expand Up @@ -65,7 +66,7 @@ Verify all services are running:
```bash
# Check Docker containers
docker compose ps
# Expected: app-db, ehrbase-db, ehrbase all "Up" and healthy
# Expected: app-db, ehrbase-db, ehrbase, terminology-server all "Up" and healthy

# Check EHRBase (wait 30-60s after docker compose up)
curl http://localhost:8080/ehrbase/rest/status
Expand All @@ -88,6 +89,7 @@ open http://localhost:5173
- **API Docs (ReDoc)**: http://localhost:8000/redoc
- **OpenAPI Schema**: http://localhost:8000/openapi.json
- **EHRBase**: http://localhost:8080/ehrbase/rest
- **Terminology Server (FHIR)**: http://localhost:8081/fhir
- **App Database**: localhost:5454 (PostgreSQL)
- **EHRBase Database**: localhost:5433 (PostgreSQL)

Expand Down
2 changes: 2 additions & 0 deletions api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies = [
"aiofiles>=23.2.0",
"faker>=22.0.0",
"oehrpy>=0.1.0",
"cachetools>=5.3.0",
]

[project.optional-dependencies]
Expand All @@ -24,6 +25,7 @@ dev = [
"ruff>=0.1.0",
"mypy>=1.8.0",
"types-aiofiles>=23.2.0",
"types-cachetools>=5.3.0",
]

[build-system]
Expand Down
6 changes: 6 additions & 0 deletions api/src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class Settings(BaseSettings):
ehrbase_timeout: float = 30.0
ehrbase_verify_ssl: bool = True

# Terminology server (Snowstorm Lite)
terminology_server_url: str = "http://localhost:8081/fhir"
terminology_server_timeout: float = 30.0
terminology_cache_ttl: int = 300
terminology_validation_mode: str = "warn" # strict | warn | off

class Config:
env_file = ".env"
extra = "ignore" # Ignore extra env vars not in Settings
Expand Down
4 changes: 4 additions & 0 deletions api/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from src.encounters.router import router as encounters_router
from src.observations.router import router as observations_router
from src.patients.router import router as patients_router
from src.terminology.client import terminology_client
from src.terminology.router import router as terminology_router

# Configure logging
logging.basicConfig(
Expand Down Expand Up @@ -47,6 +49,7 @@ async def lifespan(app: FastAPI):
if prisma.is_connected():
await prisma.disconnect()
await ehrbase_client.close()
await terminology_client.close()


app = FastAPI(
Expand All @@ -67,6 +70,7 @@ async def lifespan(app: FastAPI):
app.include_router(patients_router, prefix="/api/patients", tags=["patients"])
app.include_router(encounters_router, prefix="/api/encounters", tags=["encounters"])
app.include_router(observations_router, prefix="/api/observations", tags=["observations"])
app.include_router(terminology_router, prefix="/api/terminology", tags=["terminology"])


@app.get("/health")
Expand Down
Empty file added api/src/terminology/__init__.py
Empty file.
Loading