The Data Import Facility provides a web-based and CLI-driven system for migrating patient data from legacy systems into client VPS databases. It supports importing four standard CSV sheets in dependency order.
URL: https://{tenant}.gp.veripath.co.uk/import/
Access control: Only users with is_staff=True or is_superuser=True can access the import dashboard. Unauthenticated users are redirected to Keycloak login.
| Column | Required | Description |
|---|---|---|
LegacyPatientID |
Yes | Unique patient ID from legacy system (used to link across sheets) |
First Name |
Yes | Patient first name |
Last Name |
Yes | Patient last name |
Date of Birth |
Yes | Format: YYYY-MM-DD |
NHS Number |
No | 10-digit NHS number |
Email Address |
No | Used as username if provided |
Mobile Phone |
No | Contact mobile number |
Gender |
No | Values: MALE, FEMALE, UNKNOWN, OTHER, NOT_STATED |
Street Address |
No | Address line 1 |
City |
No | City/town |
Postcode |
No | UK postcode |
Emergency Contact Name |
No | Emergency contact full name |
Emergency Contact Phone |
No | Emergency contact phone number |
Role |
No | Defaults to PATIENT. Valid roles: PATIENT, CLINICIAN, RECEPTIONIST, PRACTICE_MANAGER, etc. |
Command: python manage.py import_patients --file patients.csv --tenant {slug}
| Column | Required | Description |
|---|---|---|
PatientID |
Yes | Must match a LegacyPatientID from Patient Demographics (or NHS number or email) |
Name |
Yes | Allergen or condition name |
Type |
Yes | ALLERGY or MEDICAL CONDITION |
OnsetDate |
Yes | Format: YYYY-MM-DD |
Severity |
No | MILD, MODERATE, SEVERE, or UNSPECIFIED (default) |
Status |
No | ACTIVE (default) or RESOLVED |
ClinicalCode |
No | SNOMED-CT or ICD-10 code (numeric) |
Command: python manage.py import_clinical_background --file allergies.csv --tenant {slug}
| Column | Required | Description |
|---|---|---|
PatientID |
Yes | Must match a LegacyPatientID from Patient Demographics |
Consultation Date |
Yes | Format: YYYY-MM-DD HH:MM or YYYY-MM-DD |
Clinical Note Text |
Yes | Raw narrative text (encrypted at rest) |
Practitioner ID |
No | Legacy ID or email of the clinician who wrote the note |
Note Type |
No | GP NOTE, CONSULTATION, TRIAGE, REFERRAL, DENTAL PROGRESS, OTHER |
Diagnosis |
No | Diagnosis or reason for visit (encrypted at rest) |
ClinicalCode |
No | SNOMED code |
Command: python manage.py import_clinical_notes --file notes.csv --tenant {slug}
| Column | Required | Description |
|---|---|---|
PatientID |
Yes | Must match a LegacyPatientID from Patient Demographics |
Drug Name |
Yes | Generic or brand name |
Frequency / Instructions |
Yes | Dosage instructions (e.g. "Take one tablet twice daily") |
Prescription Date |
Yes | Format: YYYY-MM-DD |
Dosage / Strength |
No | e.g. "500mg" — appended to drug name if provided |
Status |
No | ACTIVE, ACUTE, REPEAT, DISPENSED, DISCONTINUED, CANCELLED, EXPIRED |
Quantity Authorised |
No | Number of units (e.g. 30 tablets) |
Practitioner ID |
No | Legacy ID or email of the prescribing clinician |
Command: python manage.py import_medications --file medications.csv --tenant {slug}
Sheets must be imported in order due to foreign key dependencies:
LegacyPatientID/import/)maple-surgery)# Dry-run (validate CSV without importing):
python manage.py import_patients --file patients.csv --tenant maple-surgery --dry-run
# Live import:
python manage.py import_patients --file patients.csv --tenant maple-surgery
# All sheets support:
# --file / -f CSV file path (required)
# --tenant / -t Tenant subdomain slug
# --dry-run Validate without writing
# --encoding File encoding (auto-detected)
When sheets reference patients by PatientID, the system attempts to match in this order:
legacy_id — the LegacyPatientID from the Demographics sheetnhs_number — encrypted NHS numberemail — email addressThis allows cross-sheet linking without requiring the legacy ID format to be a specific type.
SectorDatabaseRouter now enabled in settings.py to route tenant data to the correct database/import/ — import dashboard works even on subdomains not yet registered as tenantsUser → NGINX → Django
├── TenantMiddleware (exempts /import/)
├── SectorDatabaseRouter
└── ImportDashboardView
├── upload CSV → temp storage
├── call_command (dry-run)
└── Preview → Confirm → call_command (live)
tmp_imports/ (gitignored)call_command() to invoke the same CLI import engine| File | Purpose |
|---|---|
core/import_base.py |
Shared import infrastructure (CSV parsing, validation, dry-run, summary) |
core/views.py |
ImportDashboardView — web UI for uploading and confirming imports |
core/urls.py |
URL config for /import/ |
users/management/commands/import_patients.py |
Patient demographics importer |
clinical_data/management/commands/import_clinical_background.py |
Allergies & conditions importer |
clinical_data/management/commands/import_clinical_notes.py |
Medical notes importer |
clinical_data/management/commands/import_medications.py |
Medications importer |
templates/core/import_dashboard.html |
Web UI template |
tenancy/middleware.py |
Added /import/ to TENANT_EXEMPT_PATHS |
tenancy/routers.py |
Added lazy dynamic DB alias loader |
users/models.py |
Added legacy_id, emergency_contact_name, emergency_contact_phone |
clinical_data/models.py |
Added onset_date, status to conditions/allergies; note_type, diagnosis to notes; quantity to prescriptions |
dsp_clinic/settings.py |
Enabled DATABASE_ROUTERS, added .gp.veripath.co.uk to ALLOWED_HOSTS |
requirements.txt |
Added chardet for CSV encoding detection |