Skip to content

stillwell/MedPharm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MedPharm ERP — Enlightec Ltd.

MedPharm ERP

MedPharm ERP — Medical & Pharmaceutical Enterprise Resource Planning System. PyQt6 desktop app, Flask patient portal, Windows app, macOS app, iOS app, Android app, and SQLAlchemy database with 75+ medications (default) and ALL pharmaceutical, medicine, and herbal/vitamins database updates. Copyright (C) 2026 Enlightec Ltd. (www.enlightec.com)

Medical & Pharmaceutical Enterprise Resource Planning System

A multi-platform ERP system for medical practices and pharmacies, featuring native clients for Android, iOS, macOS, and Windows, a PyQt6 desktop application for clinical staff, a Flask web portal for patients, and a cloud REST API backend. Built on a unified SQLAlchemy database with 75+ real-world medications, 30+ symptoms, 25+ conditions, drug interaction checking, insurance claims, prescription management, billing, and analytics.

Enlightec Ltd. — Creativity in Productivity

Developed by Robert Andrew Stillwell at Enlightec Ltd.Creativity in Productivity.



What's new in 1.7.6

  • ngrok integration end-to-end. ./install.sh --ngrok-login opens the ngrok dashboard in your browser, prompts for the auth token with hidden input, and persists it in both ~/.config/ngrok/ngrok.yml and ~/.config/medpharm/ngrok.env (mode 0600) so every launcher script picks it up automatically. New start_ngrok.sh boots the tunnel, polls the local inspector for the public URL, and writes data/ngrok_public_url.txt, data/ngrok_client_config.json, and data/ngrok_qr.png. docker-compose.hub.yml ships an opt-in ngrok profile (./start_docker_hub.sh ngrok) that brings the API + ngrok container up together.
  • One-tap client onboarding. Android and iOS Login screens have a Scan QR button that auto-fills the Server URL from the QR code generated by the server (Google Code Scanner on Android, AVFoundation on iOS — no extra runtime permissions on Android). macOS and Windows have a Paste-from-clipboard button. A shared QRConfigParser accepts a bare URL, a bare ngrok host, or the full client-config JSON.
  • PDF rendering fixes. Volume III (Developer Manual) was emitting a navy backdrop on every body page (text was unreadable) and a blank page before each part divider. Both are fixed in 1.7.6. All three manuals are now produced as PDF 1.5 with Fast Web View linearization, which is what GitHub's pdf.js viewer streams — earlier builds frequently appeared as a blank box in the in-browser preview.
  • Qt About dialog now shows the correct version (was hard-coded to v1.1).
  • ./update.sh — new standalone updater that polls GitHub for new commits, optionally backs up the database, and fast-forwards the local working tree.
  • Volume III Developer Manual added in this release line as the engineer-facing companion to Volume I (Technical Reference) and Volume II (Service Manual).

Rolling updates since 1.7.6 (revisions A–E)

The 1.7.6 product line carries a series of operator- and engineer-facing improvements published as manual revisions A through E without re-tagging the underlying release. Service Manual is at 1.7.6-E, Developer Manual at 1.7.6-D, Volume I tracks the headline v1.7.6 cover stamp.

  • Click-medication → MedlinePlus in every client. iOS / macOS / Android rows are tappable, the Qt desktop has a "🔎 MedlinePlus" button in the medication detail panel, the patient web portal hyperlinks the brand name. URL helper centralised so the data source is one swap.
  • iOS / macOS builds from a non-Mac host. .github/workflows/{ios,macos}-build.yml drive xcodebuild on a macos-14 runner; the Cirrus CI fallback at .cirrus.yml provides free macOS-on-M1 minutes when GitHub Actions is billing-blocked. Helpers ios/build_via_actions.sh, ios/build_via_cirrus.sh, and ios/swift_lint.sh (offline Foundation-only compile-check on the Linux Swift toolchain).
  • Auto-update with database backup. ./update.sh gains --auto, --install-schedule[=PERIOD], --uninstall-schedule, --show-schedule for unattended runs via systemd --user timer (preferred) or crontab (fallback). Concurrency lock at .update.lock.d/, dirty-tree refusal in --auto, DB-backup-before-pull guarantee.
  • Native systemd services via ./install-services.sh. Migrates the tree to /opt/medpharm, creates the medpharm system user (UID 1000, /usr/sbin/nologin), installs sandboxed medpharm-api.service + medpharm-web.service units. Aligned with the Docker image and k8s deployment so all three deployment paths converge on the same security model (runAsNonRoot, capabilities.drop=[ALL], seccompProfile: RuntimeDefault).
  • Medications catalogue bulk loaders. database/load_fda_data.py ingests the FDA NDC Directory + NIH DSLD (~360k Rx + OTC + dietary-supplement entries). database/load_dailymed_spl.py enriches existing rows with prescribing information (indications, contraindications, side effects, dosage, warnings) extracted from HL7 V3 SPL XML by LOINC section code. Idempotent, streaming, resumable. Schema gains data_source + 7 other columns and three indexes; search_medications and /medications/search paginate. Full operator guide: docs/MEDICATIONS_DATABASE.md.
  • Code-health pass. datetime.utcnow() (deprecated in Python 3.12, removed in 3.14) replaced across 35 call sites with a centralised _naive_utc_now() helper. Schema migration is now dialect-aware (functional lower(col) indexes on SQLite + Postgres, plain indexes on MySQL/other). SAWarning noise from functional-index reflection silenced. Android MedicationsResponse carries optional pagination metadata so the new "Showing N of M — refine your search" footer can fire when the catalogue overflows the page.
  • In-app user guides. Every long-lived client now ships its own bundled User Guide. The Qt desktop opens qt_app/resources/help.html from Help → User Guide (F1); the Flask Patient Portal renders web/templates/help.html at GET /help and links it from the right-hand user dropdown alongside Profile and Logout. Both documents are themed to match their host UI (dark navy + teal), use a sticky table of contents, and cover every screen, action, role/permission boundary, and troubleshooting recipe a user is likely to need.
  • HIPAA documentation expansion. Eight new policy + playbook documents under docs/ — Notice of Privacy Practices, Risk Analysis, Contingency Plan, Sanctions Policy, Workforce Training, Minimum Necessary, Patient Rights, Data Retention — paired with a substantially expanded docs/HIPAA_COMPLIANCE.md hub that maps every Security Rule standard to its implementation or its policy doc and ships a deployment checklist. New runtime helpers in security/: deidentify.py (Safe Harbor 18-identifier removal per § 164.514(b)(2)), rate_limit.py (sliding-window rate limiter), log_redaction.py (PHI redaction logging filter). encryption.py now refuses to boot in production without the cryptography package and MEDPHARM_FIELD_KEY; sessions.py adds an absolute-session-lifetime cap and an opt-in strict CSP (MEDPHARM_STRICT_CSP=1).

Table of Contents


Features

Qt6 Desktop Application (Clinical Staff)

  • Dashboard — Real-time KPIs, today's appointments, recent activity with 60-second auto-refresh
  • Patient Management — Master-detail view with demographics, vitals, allergies, diagnoses, and 7-tab detail panel
  • Prescriptions — Create prescriptions with automatic drug-drug interaction checking across 24 known interaction pairs
  • Medication Database — Searchable catalog of 75+ FDA-referenced medications with NDC codes, scheduling, and pricing
  • Appointments — Calendar-based scheduling with status management (scheduled, checked-in, in-progress, completed, cancelled)
  • Medical Records — Patient-filtered clinical records with type-based filtering
  • Billing — Invoice management, payment recording, insurance claim submission and processing, and revenue summary
  • Symptoms & Conditions — Searchable medical reference database with 30+ symptoms and 25+ conditions, body system filtering, and emergency indicators
  • Analytics — Four interactive matplotlib charts: revenue trends, patient demographics, top medications, and provider workload

Cloud REST API (Mobile & Desktop Clients)

  • JWT Authentication — HMAC-SHA256 token-based auth with access/refresh tokens, patient and staff login flows
  • Patient Self-Service — Dashboard, prescriptions, billing, records, appointments, medications, profile, notifications
  • Staff Endpoints — Dashboard, patient management, prescriptions, appointments, analytics (revenue, demographics, top meds)
  • Insurance Claims — Submit claims, view claim status, staff claim processing with approved/denied/paid workflow
  • Medical Reference — Searchable symptoms and conditions database with body system and category filtering
  • Medication Search — Full medication catalog search by name, drug class, or schedule
  • CORS-enabled — Configurable origins for cross-platform client access
  • Production-ready — Gunicorn support, environment variable configuration, health check endpoint
  • Configurable client endpoint — Every mobile and desktop client exposes a Server URL field on the login screen (with a "Reset to default" control) so operators can point the app at any compatible MedPharm API host. Default is https://medpharm-erp.enlightec.com:8080/api/v1. Overrides persist across launches: UserDefaults (iOS / macOS), SharedPreferences (Android), %APPDATA%\MedPharm\settings.json (Windows).

Android Application (Kotlin)

  • MVVM Architecture — ViewModel + LiveData + Repository pattern with Retrofit/OkHttp networking
  • Patient Portal — Dashboard, prescriptions with refill requests, billing with payments, appointments, medical records
  • Insurance Claims — File claims against invoices using on-file insurance records
  • Medication Browser — Searchable medication reference database
  • Security — EncryptedSharedPreferences for token storage, OkHttp auth interceptor for automatic token injection
  • Material Design — Material 3 components, dark theme, swipe-to-refresh, bottom navigation

iOS Application (SwiftUI)

  • Native SwiftUI — Async/await networking, NavigationStack, searchable modifiers
  • Patient Portal — Dashboard with KPI cards, prescriptions with refill, billing with payment sheet, appointments, records
  • Insurance Claims — File claims with insurance picker and submit via REST API
  • Keychain Storage — Secure token persistence via iOS Keychain Services
  • Tab Navigation — Dashboard, Prescriptions, Billing, Appointments, and More (records, medications, symptoms, profile)

macOS Application (SwiftUI)

  • NavigationSplitView — Native macOS sidebar navigation with master-detail layout
  • Full Feature Parity — Dashboard, prescriptions, billing, appointments, records, medications, insurance claims, profile
  • SwiftUI Table — Native macOS table components for prescriptions and medications
  • Shared Codebase — Shares Models and APIClient with the iOS application

Windows Desktop Application (.NET 8 / WPF)

  • MVVM with CommunityToolkit — Clean architecture with HttpClient, Newtonsoft.Json, DPAPI token encryption
  • Sidebar Navigation — Dashboard, prescriptions, billing, appointments, insurance claims
  • Payment & Claims — Pay invoices and file insurance claims with provider selection
  • DPAPI Security — Windows Data Protection API for encrypted token storage

Flask Web Portal (Patients)

  • Secure Registration — 4-factor patient identity verification (name, date of birth, SSN last 4, insurance ID)
  • Prescription Viewer — Active, past, and detailed prescription views with refill request capability
  • Bill Pay — View invoices and submit payments with credit card, debit card, or ACH
  • Medical Records — Browse personal records filtered by type
  • Appointments — View upcoming and past appointment history
  • Medication Info — Current medications with dosage, frequency, and drug information
  • Profile Management — Update contact info, insurance details, and allergy records
  • In-Portal User Guide — A 17-section walkthrough of every screen, action, and notification, accessible from the user dropdown (top-right, next to Profile and Logout)

Database & Backend

  • 23 SQLAlchemy ORM models with 18 Python enums and full relationship mapping
  • 75+ real medications seeded with NDC codes, drug classes, schedules, and pricing
  • 24 drug-drug interactions with severity levels and clinical descriptions
  • 30+ symptoms with body system classification and emergency indicators
  • 25+ conditions with ICD-10 codes, categories, and prevalence data
  • Insurance claims workflow — SUBMITTED → IN_REVIEW → APPROVED → PAID / DENIED with automatic payment creation
  • PBKDF2-SHA256 password hashing via Werkzeug
  • Role-based access control — Doctor, Psychiatrist, Pharmacist, Admin, and Patient roles
  • Audit logging for compliance tracking

Architecture

┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   Android    │ │     iOS      │ │    macOS     │ │   Windows    │
│   (Kotlin)   │ │  (SwiftUI)   │ │  (SwiftUI)   │ │  (.NET/WPF)  │
│   Material 3 │ │  Async/Await │ │  NavSplit    │ │  MVVM/DPAPI  │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
       │                │                │                │
       └────────────────┴────────┬───────┴────────────────┘
                                 ▼
                    ┌────────────────────────┐
                    │   Flask REST API       │
                    │   /api/v1/*            │
                    │   JWT + CORS           │
                    │   (run_cloud.py)       │
                    └───────────┬────────────┘
                                │
┌─────────────────────┐         │         ┌─────────────────────┐
│   PyQt6 Desktop     │         │         │   Flask Web Portal   │
│   (Clinical Staff)  │         │         │   (Patients)         │
└────────┬────────────┘         │         └──────────┬───────────┘
         │                      │                    │
         └──────────────┬───────┴────────────────────┘
                        ▼
              ┌─────────────────────┐
              │   DatabaseManager   │
              │   (SQLAlchemy 2.0)  │
              └──────────┬──────────┘
                         ▼
              ┌─────────────────────┐
              │   SQLite Database   │
              │   23 Models         │
              │   75+ Medications   │
              │   30+ Symptoms      │
              │   25+ Conditions    │
              │   24 Interactions   │
              └─────────────────────┘

Project Structure

MedPharm/
├── __init__.py
├── run_qt.py                  # Desktop application launcher
├── run_web.py                 # Web portal launcher
├── run_cloud.py               # Cloud API server launcher
├── install.sh                 # Automated installer (cross-platform)
├── uninstall.sh               # Companion uninstaller — reverses install.sh
├── start_desktop.sh           # Desktop quick-launch script
├── start_web.sh               # Web portal quick-launch script
├── start_cloud.sh             # Cloud API quick-launch script
├── generate_docs.sh           # PDF documentation generator script
├── requirements.txt           # Qt desktop + web portal dependencies
├── requirements-cloud.txt     # Cloud API server dependencies
├── Dockerfile                 # Docker image (API only, Ubuntu 24.04)
├── docker-compose.yml         # Docker Compose — API (build from source)
├── docker-compose.hub.yml     # Docker Compose — API (pull from Docker Hub)
├── start_docker_hub.sh        # Interactive launcher for Docker Hub images
├── .dockerignore              # Docker build exclusions
├── LICENSE                    # GNU General Public License v3.0
│
├── .github/workflows/
│   └── docker-publish.yml     # CI/CD: build & push to Docker Hub
│
├── server/                    # ── Docker Server Package ───────
│   ├── Dockerfile             # Full stack (Ubuntu 24.04 LTS)
│   ├── docker-compose.yml     # Full stack — build from source
│   ├── docker-compose.hub.yml # Full stack — pull from Docker Hub
│   ├── entrypoint.sh          # DB init & process bootstrap
│   ├── healthcheck.sh         # Docker health check script
│   ├── supervisord.conf       # Process manager config
│   ├── requirements-server.txt# Combined server dependencies
│   ├── .env.example           # Environment variable template
│   ├── .dockerignore          # Server build exclusions
│   └── nginx/
│       └── medpharm.conf      # Nginx reverse proxy config
│
├── database/
│   ├── models.py              # 23 SQLAlchemy ORM models & 18 enums
│   ├── db_manager.py          # DatabaseManager — all CRUD & business logic
│   ├── seed_data.py           # 55 medications, 24 interactions, sample data
│   └── seed_expanded.py       # 30+ symptoms, 25+ conditions, 20+ additional meds
│
├── api/
│   ├── app.py                 # Cloud API Flask application factory
│   ├── auth.py                # JWT HMAC-SHA256 authentication
│   └── routes.py              # REST API endpoints (/api/v1/*)
│
├── qt_app/
│   ├── main_window.py         # QMainWindow with sidebar navigation
│   ├── styles.py              # Dark theme QSS stylesheet (teal accents)
│   ├── dialogs/
│   │   └── login_dialog.py    # Staff authentication dialog
│   └── widgets/
│       ├── dashboard_widget.py
│       ├── patient_widget.py
│       ├── prescription_widget.py
│       ├── medication_widget.py
│       ├── appointment_widget.py
│       ├── records_widget.py
│       ├── billing_widget.py  # Includes insurance claim submission
│       ├── symptoms_widget.py # Symptoms & conditions reference browser
│       └── analytics_widget.py
│
├── web/
│   ├── app.py                 # Flask application factory
│   ├── routes.py              # All route handlers & API endpoints
│   ├── static/
│   │   ├── css/style.css      # Dark theme portal stylesheet
│   │   └── js/app.js          # Client-side interactions
│   └── templates/             # 14 Jinja2 templates
│
├── android/                   # Android app (Kotlin, MVVM, Retrofit)
│   └── app/src/main/
│       ├── java/com/enlightec/medpharm/
│       │   ├── data/          # API service, repository, models
│       │   ├── ui/            # Fragments, ViewModels, Adapters
│       │   └── util/          # AuthInterceptor, Resource wrapper
│       └── res/               # Layouts, drawables, strings, themes
│
├── ios/                       # iOS app (SwiftUI, async/await)
│   ├── MedPharm/MedPharm/
│   │   ├── Models/            # Codable data models
│   │   ├── Services/          # APIClient, AuthManager, KeychainHelper
│   │   └── Views/             # SwiftUI views by feature
│   ├── Package.swift          # SPM facade for swift-on-Linux compile-check
│   ├── swift_lint.sh          # Local Foundation-only build (no Mac needed)
│   ├── build_via_actions.sh   # Trigger GitHub Actions build from any platform
│   └── build_via_cirrus.sh    # Trigger Cirrus CI fallback (no GitHub billing)
│
├── macos/                     # macOS app (SwiftUI, NavigationSplitView)
│   ├── MedPharm/MedPharm/
│   │   ├── Models/            # Shared with iOS
│   │   ├── Services/          # APIClient, AuthManager
│   │   └── Views/             # macOS-optimized views
│   ├── Package.swift          # SPM facade for swift-on-Linux compile-check
│   ├── swift_lint.sh          # Local Foundation-only build (no Mac needed)
│   ├── build_via_actions.sh   # Trigger GitHub Actions build from any platform
│   └── build_via_cirrus.sh    # Trigger Cirrus CI fallback (no GitHub billing)
│
├── windows/                   # Windows app (.NET 8, WPF)
│   └── MedPharm/
│       ├── Models/            # API data models
│       ├── Services/          # ApiClient, TokenStore (DPAPI)
│       └── Views/             # XAML pages and dialogs
│
└── docs/
    ├── generate_pdf.py                    # Technical reference PDF generator (ReportLab)
    ├── MedPharm_ERP_Documentation.pdf     # Technical reference (Volume I)
    ├── generate_service_manual.py         # Service manual PDF generator (ReportLab)
    ├── MedPharm_ERP_Service_Manual.pdf    # Service manual (Volume II)
    ├── generate_developer_manual.py       # Developer manual PDF generator (ReportLab)
    └── MedPharm_ERP_Developer_Manual.pdf  # Developer manual (Volume III)

Requirements

Server / Desktop (Python)

  • Python 3.10+
  • Operating System: Ubuntu/Debian, Fedora/RHEL, macOS, Arch Linux, or Windows (WSL)
  • Display server required for the Qt desktop application (X11 or Wayland)

Python Dependencies

Package Version Purpose
PyQt6 >= 6.6.0 Desktop GUI framework
Flask >= 3.0.0 Web portal + Cloud API
flask-cors >= 4.0.0 CORS support for REST API
SQLAlchemy >= 2.0.0 ORM and database management
Werkzeug >= 3.0.0 Password hashing & WSGI
matplotlib >= 3.8.0 Analytics charts
numpy >= 1.26.0 Numerical support for charts
gunicorn >= 21.2.0 Production WSGI server
ReportLab >= 4.0 PDF documentation generation

Mobile / Desktop Clients

Platform Language Min Version Key Dependencies
Android Kotlin API 26+ Retrofit, OkHttp, Coroutines, EncryptedSharedPreferences
iOS Swift iOS 16+ SwiftUI, async/await, Keychain Services
macOS Swift macOS 13+ SwiftUI, NavigationSplitView
Windows C# .NET 8 WPF, Newtonsoft.Json, CommunityToolkit.Mvvm, DPAPI

Installation

Automated Install (Recommended)

The install script handles OS detection, Python version checking, virtual environment creation, dependency installation, database initialization, and integration tests.

git clone https://github.com/stillwell/MedPharm.git
cd MedPharm
chmod +x install.sh
./install.sh

The installer will:

  1. Detect your operating system and install system-level dependencies
  2. Verify Python 3.10+ is available
  3. Create a virtual environment in venv/
  4. Install all Python packages including flask-cors for cloud API support
  5. Initialize the SQLite database and seed it with sample data
  6. Seed expanded reference data (symptoms, conditions, additional medications)
  7. Run integration tests to verify everything works
  8. Preserve the tracked launcher scripts (start_web.sh, start_desktop.sh, start_cloud.sh, generate_docs.sh) — fallback templates with full GPL headers are only written when a launcher is missing from the working tree

Running as system services

For server deployments, the API and patient web portal can run as systemd services that start on boot, restart on failure, and run under a dedicated medpharm system user. The native systemd setup uses the same /opt/medpharm install path and the same medpharm user as the published Docker images, so the security model is identical whether you ship via Docker, Kubernetes, or native processes.

# Migrate this checkout to /opt/medpharm, create the medpharm user, build a
# venv, and install + start medpharm-api.service + medpharm-web.service.
sudo ./install-services.sh install

# Subcommands
sudo ./install-services.sh status              # systemctl status for both units
sudo ./install-services.sh logs --follow       # tail journald for both
sudo ./install-services.sh restart             # restart both
sudo ./install-services.sh start --api-only    # selective start
sudo ./install-services.sh update-units        # re-render after editing the templates
sudo ./install-services.sh uninstall           # stop, disable, remove unit files
sudo ./install-services.sh uninstall --purge   # also remove /opt/medpharm + the user
                                               # (the DB is archived to /var/backups/medpharm/ first)
sudo ./install-services.sh --help              # full flag reference

What gets installed. Two unit files at /etc/systemd/system/:

  • medpharm-api.service — gunicorn binding :8080, running run_cloud:app. Ordered After=network-online.target.
  • medpharm-web.service — gunicorn binding :5000, running run_web:app. Ordered After=medpharm-api.service so first-paint requests see a populated schema.

Both units enable systemd sandboxing (NoNewPrivileges, ProtectSystem=strict, ProtectHome=read-only with explicit ReadWritePaths, MemoryDenyWriteExecute, RestrictNamespaces, SystemCallFilter=@system-service) so a compromise of the API or web portal can't pivot to the rest of the host. The unit templates live at systemd/medpharm-api.service.template and systemd/medpharm-web.service.template — read them to see exactly what is enabled, and edit + update-units if you need to relax anything for your environment.

Operator overrides go in /etc/medpharm/medpharm.env (system-wide) or ${INSTALL_DIR}/.env (per-install). Any of MEDPHARM_JWT_SECRET, MEDPHARM_FIELD_KEY, MEDPHARM_TLS_MODE, MEDPHARM_CORS_ORIGINS, etc. — see the unit templates for the full list of Environment= defaults.

Useful flags on install:

Flag Effect
--user=NAME Service user (default medpharm)
--no-create-user Don't create the user; assume it already exists
--prefix=PATH Install dir (default /opt/medpharm)
--api-port=N, --web-port=N, --workers=N Override defaults
--api-only, --web-only Install only one service
--from-current (default) rsync this checkout into the prefix
--clone-fresh git clone from the upstream repo into the prefix instead
--no-start Install + enable but don't start (staged rollouts)
--force Overwrite an existing prefix (the existing tree is archived to ${PREFIX}.bak.<timestamp> first)
--user-mode Install as systemctl --user units (no sudo, no boot start unless you also loginctl enable-linger)

Choosing a deployment mode. All three published deployment paths converge on /opt/medpharm + the medpharm user (UID 1000, no login shell):

Mode Best for Service supervision
install-services.sh install Bare-metal / VM hosts that already have systemd systemd unit, journald, systemctl restart
docker compose -f docker-compose.hub.yml up -d Single-host containerised deploy Docker daemon
k8s/medpharm-k8s.sh deploy Multi-node / cluster deploys kubelet, with the deployment's runAsNonRoot: true, dropped capabilities, seccompProfile: RuntimeDefault

If you also want automatic source-tree updates on a recurring schedule, layer ./update.sh --install-schedule=daily on top — that timer runs as the operator who installed it (not the medpharm service user) so it can git pull and trigger a systemctl restart medpharm-api medpharm-web as needed.

Loading the medications catalogue

The shipped seed data covers ~80 demo medications. To populate a realistic catalogue from public US government sources, two loaders compose:

# Phase 1: FDA NDC Directory + NIH DSLD — names, codes, manufacturer, dose form,
# route, DEA schedule for ~360k Rx + OTC + dietary-supplement products. ~5 min.
python3 database/load_fda_data.py all

# Phase 3: DailyMed SPL labels — indications, contraindications, side effects,
# warnings, dosage extracted from the HL7 V3 SPL XML the FDA publishes.
# Incremental enrichment via the NLM REST API:
python3 database/load_dailymed_spl.py fetch --top 500

# OR offline parse of an FDA bulk SPL extract:
python3 database/load_dailymed_spl.py parse --from-dir /path/to/extracted/spls

Both loaders are idempotent, resumable, streaming (never load all rows in memory), and non-destructive — they preserve the original hand-curated seed data (rows with data_source='seed') and only update rows they previously inserted.

The 1.7.6-E schema adds three indexes (data_source, lower(brand_name), lower(generic_name)), and search_medications / /medications/search are now paginated so the patient-facing UIs continue to perform well at 300k+ rows.

Full operator guide with subcommand tables, performance notes, and a "how much should I load" sizing matrix: docs/MEDICATIONS_DATABASE.md.

Automatic updates

Once installed, the working tree can keep itself current with the upstream master branch on GitHub. Every update path backs up the SQLite database to data/backups/medpharm-YYYYMMDD-HHMMSS.db before touching the working tree, so a botched deploy can always be rolled back to the prior commit + the matching snapshot.

./update.sh                              # Interactive: check, prompt, backup, fast-forward
./update.sh --check-only                 # Just report whether new commits exist
./update.sh --auto                       # Fully unattended: silent if up-to-date,
                                         # one stdout line if it pulled, stderr on failure
./update.sh --install-schedule=daily     # Recurring auto-update (systemd timer or cron)
./update.sh --show-schedule              # What will run, and when
./update.sh --uninstall-schedule         # Remove the recurring job
./update.sh --help                       # Full flag reference

Schedule mechanics. --install-schedule[=PERIOD] prefers a systemd --user timer (sandboxed via ProtectSystem=strict / ProtectHome=read-only / ReadWritePaths={SCRIPT_DIR}, persistent across reboots, catches up on missed runs after wake) and falls back to a crontab entry when systemd-user isn't available (containers, headless boxes, BSDs). PERIOD accepts hourly, daily, weekly (default), monthly, or a literal OnCalendar=... (systemd) / cron=... (cron) expression. Both flavours stamp the install with a recognisable marker so --uninstall-schedule removes exactly the entry this script created and never touches anything else in your crontab.

Safety guarantees.

  • A directory-based PID lock at .update.lock.d/ keeps a manual run and a scheduled run from colliding on the git index. Stale locks (PID gone) are stolen automatically.
  • --auto refuses to update a dirty working tree (no unsupervised auto-stash); it logs the dirty files and exits non-zero so cron / journalctl surface the problem.
  • DB backup uses sqlite3 .backup (online, consistent) when available; falls back to cp of the file plus any WAL/SHM sidecars.
  • The DB path is auto-detected in this priority order: $MEDPHARM_DB_PATH env var → medpharm_erp.db (project root, the run_cloud.py default) → data/medpharm_erp.db (Docker volume default) → data/medpharm.db (legacy).

After any update, restart any running MedPharm services (./start_cloud.sh, ./start_web.sh, ./start_desktop.sh, the Docker stack) to pick up the new code.

Uninstallation

The repository ships with uninstall.sh, which reverses every artifact install.sh creates. Tracked source files (launcher scripts, .env.example, docs, Dockerfiles) are never touched.

./uninstall.sh                             # Interactive — remove venv, DB, log, __pycache__
./uninstall.sh --force                     # Non-interactive (for CI / scripting)
./uninstall.sh --keep-data                 # Preserve data/medpharm.db
./uninstall.sh --docker --docker-volumes   # Tear down API-only container + persistent volume
./uninstall.sh --all --force               # Scorched earth: containers, volumes, images, env, no prompts
./uninstall.sh --help                      # Full flag reference

Docker teardown is opt-in — nothing Docker-related runs unless you pass --docker, --docker-server, --docker-volumes, --docker-images, or --all. See docs/INSTALLATION.md for the full flag matrix.

Kubernetes (GKE, EKS, Linode, bare-metal)

Manifests live under k8s/ as a kustomize base with cloud-specific overlays. Use the wrapper script for the shortest path:

./k8s/medpharm-k8s.sh install --hostname=erp.example.com     # auto-detects GKE/EKS/generic
./k8s/medpharm-k8s.sh status
./k8s/medpharm-k8s.sh deploy --tag=1.7.6                     # rolling upgrade
./k8s/medpharm-k8s.sh backup pre-upgrade.db                  # SQLite hot backup
./k8s/medpharm-k8s.sh --help                                 # full command reference

Or go through kubectl directly:

kubectl create namespace medpharm
kubectl -n medpharm create secret generic medpharm-secrets \
  --from-literal=MEDPHARM_JWT_SECRET="$(openssl rand -base64 48)" \
  --from-literal=MEDPHARM_SECRET_KEY="$(openssl rand -base64 48)"
kubectl apply -k k8s/overlays/gcp        # or aws / generic

Full walkthrough (GKE ManagedCertificate, EKS ALB Controller, ingress-nginx + cert-manager) is in docs/KUBERNETES.md.

To reinstall after uninstalling:

./install.sh --fresh

Manual Install

git clone https://github.com/stillwell/MedPharm.git
cd MedPharm

# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt
pip install -r requirements-cloud.txt
pip install reportlab

# Initialize the database
python3 -c "
import sys, os
sys.path.insert(0, os.getcwd())
from database.db_manager import DatabaseManager
from database.seed_data import seed_database
from database.seed_expanded import seed_expanded_data
db = DatabaseManager('medpharm_erp.db')
db.init_db()
seed_database(db)
seed_expanded_data(db)
print('Database initialized with sample data.')
"

Docker Deployment (Ubuntu 24.04 LTS)

Both Docker images run on Ubuntu Server 24.04 LTS and are published to Docker Hub under the enlightec namespace. You can either pull pre-built images (fastest, recommended) or build from source using the included Dockerfiles.

Option A — Pull Pre-built Images from Docker Hub (Recommended)

No local build required. Pull the images and start a container in under a minute.

One-line install (uses the included installer):

./install.sh --docker                     # API only (port 8080)
./install.sh --docker-server              # Full stack: Nginx + API + Patient Portal (port 80)
./install.sh --docker --docker-server     # Both at once (API on 8080 + Full stack on 80)

Both --docker and --docker-server can be combined in a single invocation. When combined, the full-stack container's direct API host port is auto-remapped from 8080 to 8081 to avoid clashing with the API-only container that already owns 8080. The full-stack Nginx entry point remains on port 80, and the direct Patient Portal port remains on 5000.

Interactive quick-launch helper:

./start_docker_hub.sh           # Menu — choose API only, full stack, pull, or stop
./start_docker_hub.sh api       # Start API-only container
./start_docker_hub.sh server    # Start full stack
./start_docker_hub.sh pull      # Pull both images without starting
./start_docker_hub.sh stop      # Stop all MedPharm containers

Manual docker compose (API only) — uses docker-compose.hub.yml:

docker compose -f docker-compose.hub.yml pull
docker compose -f docker-compose.hub.yml up -d
curl -k https://localhost:8080/api/v1/health        # TLS on (self-signed by default)

Manual docker compose (Full stack) — uses server/docker-compose.hub.yml:

cd server
cp .env.example .env                              # Edit secrets before production
docker compose -f docker-compose.hub.yml pull
docker compose -f docker-compose.hub.yml up -d
curl -k https://localhost/api/v1/health             # TLS via Nginx (self-signed by default)

Pull without docker compose:

docker pull enlightec/medpharm-server:latest    # Full stack
docker pull enlightec/medpharm-api:latest       # API only

# Pin to a specific release
docker pull enlightec/medpharm-api:1.7.6

Run directly with docker run:

# API only (TLS on port 8080 — self-signed cert auto-generated)
docker run -d --name medpharm-api \
  -p 8080:8080 \
  -v medpharm-data:/data \
  -v medpharm-tls:/etc/ssl/medpharm \
  -e MEDPHARM_JWT_SECRET=your-secret-here \
  -e MEDPHARM_SECRET_KEY=your-other-secret \
  enlightec/medpharm-api:latest

# Full stack (Nginx TLS on 443, HTTP on 80 redirects to 443)
docker run -d --name medpharm-server \
  -p 80:80 -p 443:443 -p 8080:8080 -p 5000:5000 \
  -v medpharm-data:/data \
  -v medpharm-logs:/var/log/medpharm \
  -v medpharm-tls:/etc/ssl/medpharm \
  -e MEDPHARM_JWT_SECRET=your-secret-here \
  -e MEDPHARM_SECRET_KEY=your-other-secret \
  enlightec/medpharm-server:latest

TLS by default — both images terminate TLS out of the box. On first boot they auto-generate a self-signed cert into the medpharm-tls volume (or /etc/ssl/medpharm on a host bind-mount). For production, drop a CA-issued fullchain.pem + privkey.pem into that volume and set MEDPHARM_TLS_MODE=require. To fall back to plaintext (never in production), set MEDPHARM_TLS_MODE=disable.

Option B — Build from Source

If you want to build the images locally (e.g., to make source modifications):

Full Server Stack (Nginx + Cloud API + Web Portal):

cd server
cp .env.example .env      # Edit secrets before production use
docker compose up -d       # Build from server/Dockerfile and start
docker compose logs -f     # View logs

API Only (lightweight, no Nginx):

docker compose up -d       # Build from root Dockerfile, starts API on port 8080

Services available (either option):

Endpoint Description
https://localhost/api/v1/health REST API via Nginx TLS (full stack only)
https://localhost/portal/ Web Portal via Nginx TLS (full stack only)
http://localhost/ 301 → https://localhost/ (full stack only)
https://localhost:8080/ API direct access (gunicorn TLS)
http://localhost:5000/ Web Portal direct access — plaintext, localhost-only (full stack only)

Option C — Combined Deployment (API + Full Stack Simultaneously)

You can run the API-only image and the full-stack image on the same host in a single installer invocation:

./install.sh --docker --docker-server

Both sets of flags after the same ./install.sh command are parsed together. The installer auto-remaps the full-stack container's direct API host port from 80808081 so it does not collide with the API-only container that owns 8080.

Resulting endpoints when both deployments are active:

Endpoint Container Description
https://localhost:8080/api/v1 enlightec/medpharm-api Standalone REST API (gunicorn TLS)
https://localhost/ enlightec/medpharm-server Full-stack Nginx TLS entry point
https://localhost/api/v1/health enlightec/medpharm-server Full-stack API via Nginx TLS
https://localhost/portal/ enlightec/medpharm-server Patient Portal via Nginx TLS
http://localhost:8081/ enlightec/medpharm-server Full-stack API direct — plaintext (remapped from 8080)
http://localhost:5000/ enlightec/medpharm-server Patient Portal direct — plaintext

Stop both deployments:

docker compose -f docker-compose.hub.yml down
docker compose -f server/docker-compose.hub.yml down

CI/CD Pipeline (GitHub Actions)

Docker images are automatically built and pushed to Docker Hub on every tagged release. The pipeline:

  1. Validates source code — Syntax checks all Python modules, tests database initialization, verifies API health check and web portal login page
  2. Builds & pushes two images to Docker Hub:
    • enlightec/medpharm-server:<version> — Full stack (Nginx + API + Web Portal)
    • enlightec/medpharm-api:<version> — API only

To trigger a release:

git tag v1.7.6
git push origin v1.7.6   # Triggers the pipeline

Required GitHub Secrets (set in Settings > Secrets and variables > Actions):

Secret Description
DOCKERHUB_USERNAME Docker Hub username (e.g., enlightec)
DOCKERHUB_TOKEN Docker Hub access token (create one here)

Building Mobile / Desktop Clients

Android:

cd android
./gradlew assembleDebug

iOS / macOS (on a Mac): Open ios/MedPharm/MedPharm.xcodeproj or macos/MedPharm/MedPharm.xcodeproj in Xcode and build.

iOS / macOS from Linux or Windows: Apple's toolchain is macOS-only, so xcodebuild cannot run natively on Linux. Three options cover engineers without a Mac:

# Path 1a — GitHub Actions (.github/workflows/{ios,macos}-build.yml)
gh auth login
./ios/build_via_actions.sh              # → MedPharm-iOS-Simulator.app.zip + unsigned device archive
./macos/build_via_actions.sh            # → MedPharm-macOS.app.zip (unsigned)

# Path 1b — Cirrus CI fallback (.cirrus.yml). Free macOS-on-M1 minutes for
# public OSS, independent of GitHub billing. Install the Cirrus CI GitHub App
# once at https://github.com/marketplace/cirrus-ci, then:
./ios/build_via_cirrus.sh               # same artifacts as 1a
./macos/build_via_cirrus.sh

# Path 2 — Offline compile-check via Swift on Linux (no network, no Mac)
./ios/swift_lint.sh                     # `swift build` of the Foundation-only subset
./macos/swift_lint.sh                   # (Models/, QRConfigParser) — instant feedback

Full details, signing notes, and limitations: ios/README.md § Build from Linux, macos/README.md § Build from Linux.

Windows:

cd windows/MedPharm
dotnet build
dotnet run

Docker Hub Images

MedPharm ERP publishes two official container images to Docker Hub under the enlightec namespace. Both images are built from the Dockerfiles in this repository and are tagged on every versioned release (v*.*.*).

Published Images

Image Docker Hub Contents Base Exposed Ports
enlightec/medpharm-server:latest hub.docker.com/r/enlightec/medpharm-server Nginx + Cloud REST API + Patient Web Portal (Supervisor-managed) Ubuntu 24.04 LTS 80, 8080, 5000
enlightec/medpharm-api:latest hub.docker.com/r/enlightec/medpharm-api Cloud REST API only (Gunicorn) Ubuntu 24.04 LTS 8080

All images are available at https://hub.docker.com/u/enlightec and can be browsed at https://hub.docker.com/repositories/enlightec.

Supported Tags

Tag Description
latest Most recent release
1.7.6, 1.7.5, 1.7.4, 1.7.3, 1.7.2, 1.6.0, 1.1.1, 1.1.0, 1.0.0 Pinned semantic version tags (published from v*.*.* git tags)

Quick Pull

docker pull enlightec/medpharm-server:latest
docker pull enlightec/medpharm-api:latest

# Pin to a specific release
docker pull enlightec/medpharm-api:1.7.6

Quick Start

The fastest way to run MedPharm without any local Python setup:

git clone https://github.com/stillwell/MedPharm.git
cd MedPharm

# Option 1 — interactive menu
./start_docker_hub.sh

# Option 2 — direct flags
./start_docker_hub.sh api        # API only on port 8080
./start_docker_hub.sh server     # Full stack on port 80

# Option 3 — installer
./install.sh --docker                     # API only
./install.sh --docker-server              # Full stack
./install.sh --docker --docker-server     # Both (API on 8080 + Full stack on 80)
./install.sh --docker --tag=1.7.6         # Pin to a specific release

Compose Files for Docker Hub Images

The repository ships two compose files that reference the Docker Hub images (no build step):

File Purpose
docker-compose.hub.yml API-only deployment (enlightec/medpharm-api)
server/docker-compose.hub.yml Full stack deployment (enlightec/medpharm-server)
# API only
docker compose -f docker-compose.hub.yml up -d

# Full stack
cd server && docker compose -f docker-compose.hub.yml up -d

Persistent Data & Volumes

Both images use named Docker volumes so your data survives container restarts and re-pulls:

Volume Mount Point Purpose
medpharm-data /data SQLite database (medpharm_erp.db)
medpharm-logs /var/log/medpharm Nginx + Supervisor logs (full stack only)

Image Rebuilds

Docker images are automatically rebuilt and published to Docker Hub by the GitHub Actions workflow on every tagged release. See the CI/CD Pipeline section for details.


Quick Start

Start via Docker Hub (Zero-Build)

Skip the Python build entirely and run the official images directly:

./start_docker_hub.sh server     # Full stack at https://localhost (HTTP on :80 redirects to :443)
./start_docker_hub.sh api        # API only at https://localhost:8080

See Docker Hub Images for more options.

Start the Cloud API Server (Required for Mobile/Desktop Clients)

./start_cloud.sh

Or manually:

source venv/bin/activate
python3 run_cloud.py

The API server starts at https://localhost:8080/api/v1 (self-signed cert auto-generated into ./data/tls/ on first run). All Android, iOS, macOS, and Windows clients connect to this endpoint. Set MEDPHARM_TLS_MODE=disable to force plaintext http://localhost:8080 for local dev without certs.

Start the Web Portal (Patient Interface)

./start_web.sh

Or manually:

source venv/bin/activate
python3 run_web.py

Open your browser to http://localhost:5000 and log in with one of the patient accounts listed in Default Credentials. Behind the full server stack this is served as https://localhost/portal/ via the Nginx TLS reverse proxy.

Start the Desktop Application (Clinical Staff)

./start_desktop.sh

Or manually:

source venv/bin/activate
python3 run_qt.py

A login dialog will appear. Use one of the staff credentials listed in Default Credentials.

Custom Port for Web Portal

Pass the port as an argument:

./start_web.sh 8080

Remote Access via ngrok

The clients (Android, iOS, macOS, Windows) all expect the API to be reachable at a public URL. When the server lives behind a home firewall, double-NAT, carrier-grade NAT, or any environment where you cannot get a routable IP, the bundled ngrok integration punches a TLS tunnel from ngrok's edge network to the local API container so the clients can connect from anywhere.

The integration touches three layers:

  1. Server-side install / launcherinstall.sh --ngrok provisions the ngrok agent (apt repo on Debian/Ubuntu, Homebrew on macOS, yum repo on RHEL/Fedora, static binary fallback). start_ngrok.sh boots the tunnel against localhost:8080 and writes the public URL into data/.
  2. Docker Hub variantdocker-compose.hub.yml ships an opt-in ngrok profile that runs the official ngrok/ngrok:latest container alongside the API. ./start_docker_hub.sh ngrok brings both up.
  3. Client-side onboarding — Android and iOS Login screens have a Scan QR button that auto-fills the Server URL from the QR code generated by the server. macOS and Windows have a Paste from clipboard button that does the same from text.

Provision an auth token

Sign up at https://dashboard.ngrok.com (the free tier is sufficient for development; paid tiers add reserved domains). The token is captured once and persisted forever — every launcher script sources it automatically after that. Three equivalent ways to provide it:

A. Browser-based capture (recommended). The installer opens your default browser at the ngrok dashboard, prompts for the token with input hidden (no echo, no scrollback leak), and saves it.

./install.sh --ngrok-login                 # capture only
./install.sh --docker --ngrok --ngrok-login  # full firewalled-server setup

The flow is:

  1. The installer attempts to open https://dashboard.ngrok.com/get-started/your-authtoken via xdg-open (Linux), open (macOS), sensible-browser, the BROWSER env var, or powershell.exe Start-Process (WSL). If none of those work, it prints the URL for you to visit manually.
  2. You log in (or sign up) and copy the token shown in the gray code box.
  3. You return to the terminal, press <Enter>, and paste the token at the prompt. Input is hidden via read -s.
  4. The installer calls ngrok config add-authtoken (writes ~/.config/ngrok/ngrok.yml) and writes ~/.config/medpharm/ngrok.env with chmod 0600 so future invocations of start_ngrok.sh and start_docker_hub.sh ngrok pick the token up automatically.

B. Inline. Skip the browser entirely if you already have the token on the clipboard:

./install.sh --ngrok --ngrok-authtoken=2abc...XYZ

This also writes both ~/.config/ngrok/ngrok.yml and ~/.config/medpharm/ngrok.env.

C. Environment variable. Useful in CI:

NGROK_AUTHTOKEN=2abc...XYZ ./start_ngrok.sh
NGROK_AUTHTOKEN=2abc...XYZ ./start_docker_hub.sh ngrok

The order of precedence at run time is:

  1. Explicit --authtoken= / --ngrok-authtoken= flag
  2. NGROK_AUTHTOKEN already exported in the caller's environment
  3. ~/.config/medpharm/ngrok.env (auto-sourced)
  4. ~/.config/ngrok/ngrok.yml (used by the ngrok agent itself)

To rotate the token (e.g. after regenerating it on the dashboard), just re-run ./install.sh --ngrok-login — the new value overwrites both files.

Start a tunnel — source-installed server

./start_ngrok.sh                      # tunnel localhost:8080, defaults to US edge
./start_ngrok.sh --port=80            # tunnel a different port (e.g. full-stack Nginx)
./start_ngrok.sh --region=eu          # us | eu | ap | au | sa | jp | in
./start_ngrok.sh --domain=med.example.ngrok-free.app
                                      # pin a reserved hostname (paid tiers)
./start_ngrok.sh --stop               # tear down a running tunnel

On success the script prints the public URL and writes three artifacts under data/:

File Purpose
data/ngrok_public_url.txt Plain text — https://<random>.ngrok-free.app
data/ngrok_client_config.json {"api_base_url": "<url>/api/v1", ...} — drop-in for clients
data/ngrok_qr.png QR code of the API base URL — scan from Android/iOS Login screens

The ngrok inspector dashboard (live request log + replay) is always available locally at http://127.0.0.1:4040.

QR generation depends on qrencode (apt install qrencode / brew install qrencode) or the qrcode Python package (already pulled into the venv by the source install). If neither is present the URL and JSON files are still written; the QR step is skipped.

Start a tunnel — Docker Hub variant

NGROK_AUTHTOKEN=2abc...XYZ ./start_docker_hub.sh ngrok

This brings up enlightec/medpharm-api plus the ngrok compose service, polls the in-container ngrok inspector (http://127.0.0.1:4040/api/tunnels) for the public URL, then writes the same data/ngrok_public_url.txt / data/ngrok_client_config.json / data/ngrok_qr.png artifacts. The interactive menu (./start_docker_hub.sh with no arguments) also has a 5) Start ngrok tunnel option.

To stop both containers:

docker compose -f docker-compose.hub.yml --profile ngrok down
# or simply: ./start_docker_hub.sh stop

Configure the clients

Client How to apply the URL
Android Login screen → tap 📷 Scan QR → point the camera at data/ngrok_qr.png displayed on the server's screen. The Server URL field auto-fills and the value is persisted.
iOS Login screen → tap Scan QR → align the QR code in the camera viewfinder. Same auto-fill behaviour.
macOS Copy the contents of data/ngrok_client_config.json (or just the URL line). Login window → Paste.
Windows Same as macOS — Login window → Paste from clipboard.
Qt desktop (clinical staff) Connects to a local SQLite DB and does not consume the REST API directly, so no client-side change is required.

The QRConfigParser in each client accepts any of three payloads:

  1. The bare API base URL — https://abc123.ngrok-free.app/api/v1
  2. The full client-config JSON — {"api_base_url":"https://abc123.ngrok-free.app/api/v1", ...}
  3. A bare ngrok host — https://abc123.ngrok-free.app (the /api/v1 suffix is appended automatically)

Production caveats

  • Free ngrok URLs rotate every time the agent restarts. For a stable URL across reboots, either reserve a paid ngrok domain (--domain=...) or move to a real DNS A-record + a reverse proxy.
  • Free-tier tunnels also expire after a few hours of idle time without an auth token; --ngrok-authtoken= removes that limit.
  • The ngrok inspector at http://127.0.0.1:4040 exposes full request/response bodies for any traffic crossing the tunnel — bind it only to loopback (the compose file does this automatically) and never expose port 4040 to the public Internet.
  • ngrok's edge network is not part of your HIPAA Business Associate Agreement (BAA) unless you separately negotiate one with ngrok, Inc. For PHI-bearing production traffic, prefer a managed reverse proxy / dedicated VPN over ngrok.

Usage Guide

Cloud API — Mobile & Desktop Client Access

The REST API provides JWT-authenticated endpoints for all client platforms:

  • Authentication: POST /api/v1/auth/login/patient, POST /api/v1/auth/login/staff, POST /api/v1/auth/register
  • Patient Endpoints: /api/v1/patient/dashboard, /prescriptions, /billing, /records, /appointments, /profile, /notifications
  • Insurance Claims: POST /api/v1/patient/insurance/claims to submit, GET to list, staff can process via /staff/insurance/claims/<id>/process
  • Reference Data: /api/v1/reference/symptoms, /reference/conditions, /medications/search
  • Staff Endpoints: /api/v1/staff/dashboard, /patients, /appointments, /prescriptions, /analytics/*

All endpoints require a Bearer <token> Authorization header except /health, /auth/*.

Desktop Application — Clinical Staff Workflow

  1. Login — Authenticate with your staff credentials. Access is role-based (Doctor, Psychiatrist, Pharmacist, Admin).

  2. Dashboard — After login, the dashboard shows key performance indicators (active patients, pending prescriptions, today's appointments, monthly revenue), today's schedule, and recent activity. Data refreshes automatically every 60 seconds.

  3. Patient Management — The left panel lists all patients with a search bar. Select a patient to view their full profile in the right panel, which is organized into 7 tabs: Demographics, Vitals, Allergies, Diagnoses, Prescriptions, Billing, and Records. Click "Add Patient" to register a new patient.

  4. Prescriptions — View all prescriptions or create new ones. The prescription dialog lets you select a patient, add multiple medication lines, and set dosage/frequency/duration/quantity for each. When you add medications, the system automatically checks for drug-drug interactions and displays warnings with severity levels. Saving a prescription auto-generates a priced invoice.

  5. Medication Database — Browse and search the full catalog of 75+ medications. Filter by drug class or schedule. Select any medication to view detailed information including NDC code, manufacturer, dosage forms, pricing, and contraindications.

  6. Appointments — A calendar widget on the left lets you pick a date. Appointments for that date appear on the right. Create new appointments or update their status through the workflow: Scheduled → Checked In → In Progress → Completed.

  7. Medical Records — Select a patient and filter records by type (lab results, imaging, clinical notes, etc.).

  8. Billing — View all invoices with status indicators (Pending, Partial, Paid, Overdue). Record payments against invoices. Submit and process insurance claims with approval/denial workflow. Summary cards show total billed, collected, and outstanding amounts.

  9. Symptoms & Conditions — Browse the medical reference database. Search symptoms by name or body system with emergency indicators. Search conditions by name, ICD-10 code, or category. Select any entry to view detailed information including associated conditions, typical medications, and prevalence data.

  10. Analytics — Four chart panels powered by matplotlib: monthly revenue trends, patient age/gender demographics, top prescribed medications, and provider workload distribution.

Web Portal — Patient Workflow

  1. Register — New patients verify their identity using 4 factors: first name, last name, date of birth, and last 4 digits of SSN. These must match an existing patient record in the system. Once verified, create a username and password.

  2. Login — Log in with your portal credentials.

  3. Dashboard — View a summary of active prescriptions, upcoming appointments, and recent invoices at a glance.

  4. Prescriptions — Browse active, past, and all prescriptions. View full details including medications, dosages, refill counts, and costs. Request refills for eligible medications directly from the detail page.

  5. Billing — View all invoices and their payment status. Click "Pay" on any invoice with an outstanding balance to submit a payment via credit card, debit card, or bank account (ACH).

  6. Records — Access your medical records filtered by type.

  7. Appointments — View upcoming and past appointments with date, time, provider, and status.

  8. Medications — See your current medications with full details: dosage, frequency, drug class, manufacturer, and special instructions.

  9. Profile — Update your phone number, email, and address. View insurance information and manage allergy records.

  10. User Guide — Click your name in the top-right corner and select User Guide from the dropdown to open an in-portal walkthrough covering every page, the notification bell, refill requests, online payment, secure messaging, privacy/HIPAA controls, and a troubleshooting checklist. The same guide is also reachable directly at /help.


Documentation

MedPharm ERP ships with three complementary PDF manuals, each generated by a self-contained ReportLab script under docs/. Volume I describes what the software is; Volume II describes what it does under an operator's care; Volume III describes how it was built and how to extend it. They are designed to be read together.

Volume File Generator Audience
I — Technical Reference docs/MedPharm_ERP_Documentation.pdf docs/generate_pdf.py Architects, integrators, external auditors
II — Service Manual docs/MedPharm_ERP_Service_Manual.pdf docs/generate_service_manual.py System Operators, Security Officers, on-call responders
III — Developer Manual docs/MedPharm_ERP_Developer_Manual.pdf docs/generate_developer_manual.py Software engineers, maintainers, contributors

All three PDFs are produced as PDF 1.5, linearized for Fast Web View. They render reliably in Adobe Acrobat Reader, Apple Preview, GitHub's in-browser PDF viewer (pdf.js), Chrome/Edge/Firefox, and on iOS / Android tablets. Each generator script invokes Ghostscript (gs) at the end of its build to perform the linearization pass — if Ghostscript is not on PATH, the unoptimised ReportLab output is left in place and a banner is omitted from the build log; the file is still valid, only not web-streamable.

Note: The 1.7.5 release of Volume III (Developer Manual) emitted a PDF whose cover-page navy backdrop bled into every body page, rendering long stretches of text as dark-blue text on a dark-blue field, and inserted a blank page before each part divider. Both defects were corrected in 1.7.6; if you have an older locally-built copy, regenerate it with python3 docs/generate_developer_manual.py to pick up the fix.

Volume I — Technical Reference

The pre-generated PDF is located at docs/MedPharm_ERP_Documentation.pdf. Regenerate it with:

./generate_docs.sh
# or manually:
source venv/bin/activate && python3 docs/generate_pdf.py

Contents:

  • System architecture overview with component diagrams
  • Complete database schema — all 23 SQLAlchemy models and relationships
  • Multi-platform client architecture (Android, iOS, macOS, Windows)
  • Cloud REST API endpoint reference with authentication details
  • Insurance claims workflow documentation
  • Drug interaction checking algorithm walkthrough
  • Flask web portal route reference
  • Qt desktop application module documentation
  • Security control matrix (JWT auth, HMAC-SHA256, DPAPI, Keychain, password hashing, audit logging)
  • Deployment and configuration guide
  • Links to external documentation (SQLAlchemy, Flask, PyQt6, Retrofit, SwiftUI, WPF)

Volume II — Service Manual

An operations-focused companion to the technical reference, written for the people responsible for keeping a MedPharm deployment healthy in production. Where Volume I describes the system's anatomy (models, routes, code), the service manual describes its life under an operator's care: commissioning, daily routine, security operations, data stewardship, upgrades, fault diagnosis, and disaster recovery.

The manual is approximately 200 pages, organised into nine parts and thirty-three chapters plus six appendices. Each chapter opens with context, proceeds through procedure, and closes with a verification step so that work can be confirmed complete rather than merely attempted. Four signal words — Note, Caution, Danger, and Legend — are used consistently throughout with precise meanings defined in the front matter.

Structure:

Part Title Chapters
I Orientation Platform overview, operator roles, service topology
II Commissioning Pre-install survey, sizing, commissioning procedures, post-commissioning validation
III Daily Operations Daily routine, startup/shutdown, user administration, monitoring, log management
IV Security Operations Security handbook, TLS lifecycle, secret rotation, HIPAA controls, incident response
V Data Stewardship Database administration, backup, restore / point-in-time recovery, retention
VI Maintenance and Change Maintenance calendar, patching, dependency hygiene, change management
VII Fault Diagnosis Troubleshooting methodology, server / client / database / network symptom directories
VIII Business Continuity Failure modes and RTO/RPO, DR runbooks, BCP
IX Appendices Command reference, env var reference, port/protocol reference, directory layout, glossary, revision history

Regenerate the PDF:

source venv/bin/activate
python3 docs/generate_service_manual.py

The generator is deliberately self-contained — it reads no project files at build time, so it compiles cleanly in sterile build environments (CI, container builds, documentation-only forks) and does not drift when subordinate modules are refactored. The output is written to docs/MedPharm_ERP_Service_Manual.pdf.

Note: The service manual is written in the second person, addressing the System Operator directly. It assumes familiarity with a Linux shell, the UNIX file-permission model, and the ability to read simple SQL. No formal DBA credentials are required.

Volume III — Developer Manual

The engineering counterpart to the previous two volumes. Where Volume I describes what the software is and Volume II describes how to operate it, Volume III describes how it was built and how to extend, debug, and evolve it. Audience: software engineers joining the project, returning maintainers, external auditors, and contributors preparing pull requests.

The manual runs to ~100 pages organised into eight parts and thirty-four chapters plus six appendices, with embedded flowcharts, syntax-highlighted source-code samples, hyperlinks, and support email addresses. Code samples are reproduced inline so the manual does not drift when subordinate modules are refactored.

Structure:

Part Title Chapters
I Foundations Project vision, technology stack, architecture, repository layout
II The Data Model DB design philosophy, core / clinical / financial / HIPAA / messaging models
III The Backend DatabaseManager, field encryption, REST API, JWT, CSRF/lockout/MFA, Flask portal
IV The Clients Qt desktop, Android, iOS, macOS, Windows
V Cross-Cutting Concerns Audit, TLS, Docker, Kubernetes
VI Development Workflow Dev environment, testing, branching, releases, debugging
VII Extending MedPharm Recipes for new model / endpoint / module / client platform
VIII Appendices File tour, SQL schema, endpoint catalogue, error codes, glossary, support

Regenerate the PDF:

source venv/bin/activate
python3 docs/generate_developer_manual.py

The output is written to docs/MedPharm_ERP_Developer_Manual.pdf and opens cleanly in Adobe Acrobat Reader, Apple Preview, or any modern browser PDF viewer. Hyperlinks in the manual (mailto: and https://) are clickable.

Tip: A new engineer should expect to spend three to five working days reading Volume III end-to-end while opening the real source files alongside. After the first pass, the table of contents becomes the index. Part VII (Extending MedPharm) is the right starting point if you are about to land a substantial change.

HIPAA Compliance & Privacy Documentation

MedPharm ERP is built and documented for deployment by U.S. HIPAA-covered entities and their business associates. The technical safeguards in code (45 CFR § 164.312) are paired with administrative, physical, and organisational documentation under docs/. The hub is docs/HIPAA_COMPLIANCE.md, which maps every Security Rule standard to its implementation or its policy doc and ships a deployment checklist.

Document What it covers HIPAA reference
HIPAA_COMPLIANCE.md Master control map + deployment checklist 45 CFR § 164.302 et seq.
NOTICE_OF_PRIVACY_PRACTICES.md Patient-facing notice template § 164.520
RISK_ANALYSIS_TEMPLATE.md Annual risk analysis & risk-management plan § 164.308(a)(1)(ii)(A)–(B)
CONTINGENCY_PLAN.md Backup, disaster recovery, emergency-mode operation § 164.308(a)(7)
SANCTIONS_POLICY.md Workforce sanctions framework with violation categories A–D § 164.308(a)(1)(ii)(C)
WORKFORCE_TRAINING.md Onboarding + annual refresh + incident-driven training § 164.308(a)(5)
MINIMUM_NECESSARY.md RBAC + minimum-necessary policy § 164.502(b) / § 164.514(d)
PATIENT_RIGHTS.md Access, amendment, accounting, restriction, complaints — front-desk playbook §§ 164.522–528
DATA_RETENTION_POLICY.md Retention windows, destruction methods, legal-hold workflow § 164.316(b)(2), § 164.530(j)
BREACH_NOTIFICATION.md Five-stage incident workflow + notification timelines §§ 164.400–414
BAA_TEMPLATE.md Business Associate Agreement template § 164.504(e)

The implementation lives under security/. Each module names the regulatory section it implements in its docstring:

Module Standard What it does
security/audit.py § 164.312(b), (c)(1) Hash-chained audit_log; verify_audit_chain() recomputes the chain to detect tampering.
security/phi.py § 164.312(b) @log_phi_access decorator stamps phi_access_log for every PHI read; powers § 164.528 accounting.
security/encryption.py § 164.312(a)(2)(iv), (e)(2)(ii) Fernet FieldCipher for marked PHI columns; refuses to boot in production without cryptography and MEDPHARM_FIELD_KEY.
security/passwords.py § 164.312(d) Complexity, history, rotation. PBKDF2-HMAC-SHA256 via Werkzeug.
security/totp.py § 164.312(d) TOTP MFA.
security/lockout.py § 164.308(a)(5)(ii)(C) Failed-login monitoring & temporary lockout.
security/sessions.py § 164.312(a)(2)(iii) Idle timeout + absolute lifetime cap; HSTS, CSP, SameSite=Lax, security headers.
security/csrf.py § 164.312(c)(2) CSRF tokens on every state-changing portal route.
security/emergency.py § 164.312(a)(2)(ii) Break-glass emergency-access grant with mandatory justification + audit entry.
security/deidentify.py § 164.514(b)(2) Safe Harbor 18-identifier removal + free-text redaction for analytics export.
security/rate_limit.py § 164.308(a)(1)(ii)(B), defence-in-depth Sliding-window per-IP rate limiter for unauthenticated and hot endpoints.
security/log_redaction.py Defence-in-depth around (b) Logging filter that scrubs SSN, phone, email, IP, URL, dates, MRN, ages > 89, and JWTs from log records.

Reminder. Code is one of three pillars. Without the policy and process documents above (and the workforce training and signed BAAs they reference), MedPharm is not HIPAA-compliant in itself — it is a HIPAA-aligned platform that a covered entity can use to be compliant.


Default Credentials

Desktop Application & Cloud API (Staff)

Role Username Password
Doctor dr.carter doctor123
Doctor dr.chen doctor123
Psychiatrist dr.brooks doctor123
Pharmacist pharm.davis pharm123
Admin admin admin123

Web Portal, Mobile & Desktop Clients (Patients)

Patient Username Password
John Smith jsmith_portal patient123
Maria Johnson mjohnson_portal patient123
Emily Williams ewilliams_portal patient123
Sarah Davis sdavis_portal patient123
Linda Martinez lmartinez_portal patient123

Note: These are demo credentials for development and testing. Change all passwords before any production deployment.


Environment Variables

The cloud API server and Docker deployment can be configured via environment variables:

Variable Default Description
MEDPHARM_DB_PATH medpharm_erp.db Path to SQLite database
MEDPHARM_SECRET_KEY Auto-generated Flask secret key
MEDPHARM_JWT_SECRET Dev default JWT signing secret (change in production)
MEDPHARM_TOKEN_EXPIRY 86400 Access token lifetime in seconds (24h)
MEDPHARM_REFRESH_EXPIRY 604800 Refresh token lifetime in seconds (7 days)
MEDPHARM_CORS_ORIGINS * Allowed CORS origins
MEDPHARM_HOST 0.0.0.0 Server bind host
MEDPHARM_PORT 8080 Server bind port
MEDPHARM_DEBUG false Enable debug mode
MEDPHARM_HTTP_PORT 80 Nginx listen port (Docker full stack)
MEDPHARM_API_PORT 8080 Gunicorn API port (Docker full stack)
MEDPHARM_WEB_PORT 5000 Gunicorn Web Portal port (Docker full stack)
MEDPHARM_WORKERS 4 Gunicorn worker processes
MEDPHARM_THREADS 2 Gunicorn threads per worker
MEDPHARM_HTTPS_PORT 443 Nginx TLS listen port (Docker full stack)
MEDPHARM_TLS_MODE auto auto | require | disable — see TLS section below
MEDPHARM_TLS_DIR /etc/ssl/medpharm Directory holding fullchain.pem + privkey.pem
MEDPHARM_TLS_HOSTNAME localhost CN / SAN for self-signed cert generation
MEDPHARM_TLS_DAYS 825 Validity (days) for auto-generated self-signed certs

TLS / HTTPS

Every install path — source Python, docker-compose, docker run, Kubernetes — terminates TLS by default. This satisfies the HIPAA transmission-security rule (§ 164.312(e)(1)) and removes the failure mode where a new deployment silently serves PHI over plain HTTP.

How certs are provisioned

Install path Cert location Auto-generated?
API-only Docker (enlightec/medpharm-api) medpharm-tls volume → /etc/ssl/medpharm Yes (gunicorn entrypoint)
Server stack Docker (enlightec/medpharm-server) medpharm-tls volume → /etc/ssl/medpharm Yes (nginx + gunicorn)
Source Python (./start_cloud.sh) ./data/tls/ Yes (server/nginx/generate-cert.sh)
Kubernetes Secret named medpharm-tls in the medpharm namespace Yes, via ./k8s/medpharm-k8s.sh install

Modes (MEDPHARM_TLS_MODE):

  • auto (default): if no cert is found at MEDPHARM_TLS_DIR, a self-signed RSA-4096 cert is generated on first boot. If one is already present (you mounted a CA-issued cert), it is used as-is.
  • require: the container fails to start unless a cert is mounted. Use this in production to guarantee no fallback path.
  • disable: plaintext HTTP. Local dev only.

Rotating the self-signed cert

# Docker
docker compose -f docker-compose.hub.yml exec api rm /etc/ssl/medpharm/*.pem
docker compose -f docker-compose.hub.yml restart api

# Kubernetes
./k8s/medpharm-k8s.sh rotate-tls

Using a CA-issued cert (production)

Drop fullchain.pem and privkey.pem into the medpharm-tls volume (or the K8s medpharm-tls Secret) and set MEDPHARM_TLS_MODE=require. Never ship production with a self-signed cert — browsers and mobile clients will refuse the connection, and training users to click past warnings is itself a compliance failure.

Clients & self-signed certs (dev only)

  • Android (android/app/src/main/res/xml/network_security_config.xml) — the localhost / 10.0.2.2 domain-config trusts user-installed CAs in addition to the system store. Export the server's fullchain.pem and install it on the device as a user CA to dismiss the self-signed warning.
  • iOS / macOS — add the self-signed cert to the iOS Simulator trust store (drag into the running simulator) or the macOS keychain and mark it as trusted. URLSession otherwise refuses the connection.
  • Windows (.NET)HttpClient uses the Windows certificate store. Import fullchain.pem into Trusted Root Certification Authorities for the current user.

License

This project is licensed under the GNU General Public License v3.0.

Copyright (C) 2026 Enlightec Ltd. Author: Robert Andrew Stillwell Email: Andrew.Stillwell@enlightec.com

See the LICENSE file for the full license text.


Enlightec Ltd.
MedPharm ERP — Built by Enlightec Ltd.
Creativity in Productivity

About

MedPharm ERP - Medical & Pharmaceutical Enterprise Resource Planning System. PyQt6 desktop app, Flask patient portal, Windows app, macOS app, iOS app, Android app, and SQLAlchemy database with 75+medications (default) and ALL pharmaceutical, medicine, and herbal/vitamins database updates. Copyright (C) 2026 Enlightec Ltd. (www.enlightec.com)

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors