Skip to main content

Scripts Reference

All deployment and operational scripts included in the repository.

Overview

ScriptLocationPurposeRun From
deploy-prod.shscripts/Automated production deployment with rollbackServer
validate-deployment.shscripts/Pre-deployment validation checksServer
push-snomed.shscripts/Upload SNOMED RF2 to server and run importMac (local)
import_snomed_rf2.pybackend/scripts/SNOMED RF2 database importServer (via Docker)
verify-port-config.shscripts/Check port configuration consistencyLocal/Server
validate_and_stamp_department_schedules.pybackend/scripts/Post-ingest: validate + stamp department schedulesServer (via Docker)
seed_ontology_aliases.pybackend/scripts/Post-ingest: seed specialty-noun → department aliasesServer (via Docker)
seed_snomed_domain_mapping.pybackend/scripts/Post-ingest: seed SNOMED → clinical-domain mapServer (via Docker)

Post-Ingest Finalization Scripts

Run once after a full crawl + taxonomy extraction (see Data Seeding → Step 6). All three are idempotent and read DATABASE_URL from the zol-app container env. They are operator steps, not part of the ingest pipeline — skipping them leaves the doctor-schedule tools, spoken-specialty resolution, and SNOMED domain guards silently non-functional on a fresh install.

ScriptWhat breaks if skippedRe-run when
validate_and_stamp_department_schedules.pyEvery "which doctors consult on {day}?" query hedges (no schedule is trusted)After any re-ingest of … – Raadplegingen pages
seed_ontology_aliases.pySpoken specialty nouns ("longarts") don't resolve to a departmentAfter the approved DEPARTMENT_SPECIALTY_NOUNS table changes
seed_snomed_domain_mapping.pySNOMED domain plausibility guards have no mappingAfter a SNOMED import / when the ~38-row map changes
docker exec zol-app python -m scripts.validate_and_stamp_department_schedules
docker exec zol-app python -m scripts.seed_ontology_aliases
docker exec zol-app python -m scripts.seed_snomed_domain_mapping

Not these on a fresh install: backfill_consultation_schedule.py and backfill_department_schedule.py are one-shot historical migrations for pre-existing corpora; new docs get those fields extracted at ingest time.


deploy-prod.sh

Automated production deployment with health checks and rollback.

./scripts/deploy-prod.sh

What It Does

  1. Pre-deployment checks — Docker, compose, env file, disk space
  2. Backup — PostgreSQL dump + volume snapshot
  3. Pull images — Download latest Docker images
  4. Migrations — Run Alembic database migrations
  5. Deploydocker compose up -d with --remove-orphans
  6. Health checks — Wait for backend and frontend health endpoints
  7. Smoke tests — API endpoint, frontend, auth endpoint
  8. Cleanup — Prune unused images, remove old backups (>30 days)

Failure Handling

If any step fails, the script triggers automatic rollback:

  • Stops new containers
  • Logs the failure

Configuration

VariableDefaultDescription
COMPOSE_FILEdocker-compose.prod.ymlCompose file to use
ENV_FILE.env.prodEnvironment file
BACKUP_DIRbackups/Backup destination

validate-deployment.sh

Pre-deployment validation without actually deploying.

./scripts/validate-deployment.sh

Checks Performed

  • Docker Engine installed and running
  • Docker Compose installed
  • docker-compose.prod.yml YAML syntax valid
  • Required Dockerfiles exist
  • nginx configuration exists
  • Deployment script is executable
  • .env.prod.example template exists
  • CI/CD workflow exists
  • Backend and frontend directory structure

Exit Codes

CodeMeaning
0All checks passed
1One or more checks failed

push-snomed.sh

Upload SNOMED CT RF2 from Mac to server and run import.

./scripts/push-snomed.sh user@server-ip [OPTIONS]

Arguments

ArgumentRequiredDescription
user@server-ipYesSSH target (e.g., deploy@10.0.0.5)

Options

FlagDescription
--dry-runShow what would be done without executing
--helpShow usage information

What It Does

  1. Find — Auto-detects ~/Downloads/SnomedCT_ManagedServiceBE_PRODUCTION_*
  2. Compresstar -czf (~2 GB → ~400 MB), reuses existing tarball
  3. Uploadscp to /opt/zol-rag/snomed/ on the server
  4. Importdocker exec zol-app python -m scripts.import_snomed_rf2 inside the container
  5. Verify — Queries row counts from SNOMED tables

Prerequisites

  • SNOMED RF2 folder in ~/Downloads/
  • SSH key-based access to the server
  • The zol-app container running on the server

Examples

# Full import
./scripts/push-snomed.sh deploy@10.0.0.5

# Preview only
./scripts/push-snomed.sh deploy@10.0.0.5 --dry-run

import_snomed_rf2.py

SNOMED CT RF2 to PostgreSQL import script.

# Run via Docker (on server)
docker exec zol-app python -m scripts.import_snomed_rf2 \
--rf2-dir /path/to/Snapshot [--dry-run] [--db-url URL]

Arguments

FlagRequiredDefaultDescription
--rf2-dirYesPath to RF2 Snapshot directory
--dry-runNofalseParse and show statistics without writing to DB
--db-urlNoDATABASE_URL envPostgreSQL connection URL

Import Phases

  1. Parse RF2 files — Dutch descriptions, English descriptions (for FSN), relationships, language refset
  2. Build records — Concept records with FSN, preferred Dutch term, semantic tag
  3. Compute transitive closure — IS-A hierarchy (topological sort + BFS)
  4. Insert into PostgreSQL — Bulk COPY into app.snomed_* tables

Tables Modified

TableContent
app.snomed_conceptsConcept ID, FSN, preferred Dutch term, semantic tag
app.snomed_descriptionsDutch descriptions with acceptability
app.snomed_relationshipsAll active relationships (IS-A, FINDING_SITE, etc.)
app.snomed_transitive_closurePre-computed IS-A ancestry (ancestor, descendant, depth)

All tables are truncated before import (fresh load, not incremental).

Expected Output

Phase 1: Parsing RF2 files...
Dutch descriptions: ~150,000
English descriptions: ~350,000
Relationships: ~900,000
Phase 2: Building concept records...
Concepts: ~350,000
Phase 3: Computing transitive closure...
Closure rows: ~5,000,000
Phase 4: Loading into PostgreSQL...
Total time: ~5-15 minutes

verify-port-config.sh

Check port configuration consistency across the project.

./scripts/verify-port-config.sh

Verifies that port numbers in Docker compose files, environment variables, and application configuration are consistent with each other.


Operational Commands Quick Reference

These are not scripts but commonly used Docker commands for operations:

Health Check

curl -s http://localhost:80/health | python3 -m json.tool

View Logs

docker logs zol-app --tail 100 -f

Restart Application

APP_IMAGE=zol-rag-app:${GIT_SHA} \
docker compose -f docker/docker-compose.app.yml --env-file .env.prod up -d

Database Shell

docker exec -it zol-postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}

Container Status

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"