Initial Setup & Configuration
After Bundle Deployment, this guide covers the Frappe provisioning that creates the CTMS data model, RBAC, and seed data.
The zynctl.sh deploy command runs all 5 stages automatically via the ctms-init container. This page is a reference for what each stage creates and how to run them manually if needed.
Frappe Provisioning Overview
The CTMS platform extends Frappe Cloud with a data model created in 5 stages:
| Stage | Creates | Count |
|---|---|---|
| 1 | Custom DocTypes | 34 (across 4 dependency phases) |
| 2 | Custom Fields on built-in DocTypes | 15 fields |
| 3 | RBAC data (Frappe native roles, CTMS roles, resources, permissions) | 539 records |
| 4 | Master/seed data (lookups, drugs, sites, study events) | 127 records |
| 5 | Default Healthcare Practitioner | 1 record |
Automated Provisioning (Docker)
The ctms-init container runs all 5 stages automatically:
docker compose --env-file .env.production --profile init up ctms-init
Selective or dry-run:
# Run only stages 3 and 4
CTMS_INIT_STAGES=3,4 docker compose --env-file .env.production run --rm ctms-init
# Run only Stage 5 (default practitioner)
CTMS_INIT_STAGES=5 docker compose --env-file .env.production run --rm ctms-init
# Preview without making changes
CTMS_INIT_DRY_RUN=true docker compose --env-file .env.production run --rm ctms-init
The init scripts are idempotent — they skip existing records. Safe to re-run at any time.
Manual Provisioning (Python Scripts)
For granular control, run each stage individually using the Python seed scripts.
Prerequisites
- Python 3.10+ installed
- The environment file configured with Frappe credentials:
FRAPPE_URL=https://your-frappe-instance.frappe.cloud
FRAPPE_API_TOKEN=api-key:api-secret
The scripts support both FRAPPE_API_TOKEN=key:secret and separate FRAPPE_API_KEY / FRAPPE_API_SECRET formats.
# One-time setup
cd ctms.devops/scripts/frappe-seed
python -m venv venv
source venv/bin/activate
pip install requests python-dotenv structlog
Step 1: Create Custom DocTypes
Create all 34 custom DocTypes in the correct dependency order across 4 phases:
| Phase | Count | DocTypes | Description |
|---|---|---|---|
| 1 – Standalone | 15 | CTMS Role, CTMS Resource, CTMS Action, Study Status, Study Type, Study Phase, Event Type, Site Status, Intervention Model, Masking, Control, Allocation, Classification, Purpose, zynomi_drug_master | No dependencies on other custom DocTypes |
| 2 – Links Phase 1 | 5 | CTMS Permission, CTMS Navigation, Study Event, Site Location, Study | Link fields reference Phase 1 DocTypes |
| 3 – Links Phase 2 | 5 | CTMS Nav Permission, Site, Subject, Consent, FamilyMedicalHistory | Link fields reference Phase 2 DocTypes |
| 4 – Links Phase 3 | 9 | Study Sites, study_personnel, PhysicalExamination, AdverseEvents, Study CRF, CRF_FORM_MICRONUTRIENTS_ANTIOXIDANTS_HIV_INFECTION, 3 CRF forms | Link fields reference Phase 3 DocTypes |
The DocTypes break down into three functional categories:
- RBAC (6): CTMS Role, Resource, Action, Permission, Navigation, Nav Permission
- Study Design (7): Intervention Model, Masking, Control, Allocation, Classification, Purpose, zynomi_drug_master
- Clinical / CRF (21): Study, Subject, Site, Consent, CRF forms, Study CRF, etc.
Running the Script
cd ctms.devops/scripts/frappe-seed
source venv/bin/activate
# Preview (dry run)
python frappe_setup_custom_doctypes.py --dry-run --env ../../.env.example
# Create ALL 34 custom DocTypes
python frappe_setup_custom_doctypes.py --env ../../.env.example
# Create subsets
python frappe_setup_custom_doctypes.py --rbac-only --env ../../.env.example # 6 RBAC DocTypes
python frappe_setup_custom_doctypes.py --clinical-only --env ../../.env.example # 28 clinical DocTypes
| Flag | Description |
|---|---|
--env <path> | Path to environment file (e.g., ../../.env.example) |
--dry-run | Preview what would be created without making changes |
--rbac-only | Create only the 6 CTMS RBAC DocTypes |
--clinical-only | Create only the 28 clinical/study-design/CRF DocTypes |
--log-level | Logging verbosity: DEBUG, INFO, WARNING, ERROR (default: INFO) |
DocTypes must be created in phase order because later phases reference earlier ones via Link fields. The script handles this automatically.
Step 2: Create Custom Fields on Built-in DocTypes
Add custom fields to Frappe's built-in DocTypes. These extend standard Healthcare module entities with CTMS-specific attributes.
What Gets Created
| Built-in DocType | Field | Type | Description |
|---|---|---|---|
| Healthcare Practitioner | education | Text | Educational qualifications |
| Healthcare Practitioner | experience | Int | Years of professional experience |
| Healthcare Practitioner | biography | Text Editor | Professional biography (rich text) |
| Healthcare Practitioner | surgeries | Int | Number of surgeries performed |
| Vital Signs | study_event | Link → Study Event | Associates vital signs with a study event |
| Vital Signs | study | Link → Study | Associates vital signs with a study |
| Patient | height | Data | Patient height |
| Patient | weight | Data | Patient weight |
| Drug Prescription | send_reminders | Check | Enable medication reminders |
| Drug Prescription | snooze_after | Small Text | Snooze reminder configuration |
| Drug Prescription | start_date | Date | Prescription start date |
| Drug Prescription | end_date | Date | Prescription end date |
| Nursing Task | clinical_trial | Select | Associate nursing task with a clinical trial |
Total: 15 custom fields across 5 built-in DocTypes.
Running the Script
# Preview
python frappe_setup_custom_fields.py --dry-run --env ../../.env.example
# Create all custom fields
python frappe_setup_custom_fields.py --env ../../.env.example
# Create fields for a specific DocType
python frappe_setup_custom_fields.py --doctype "Healthcare Practitioner" --env ../../.env.example
| Flag | Description |
|---|---|
--env <path> | Path to environment file |
--dry-run | Preview without making changes |
--doctype <name> | Create custom fields for a specific DocType only |
--log-level | Logging verbosity (default: INFO) |
Existing fields are skipped. Safe to re-run at any time.
Step 3: Seed RBAC Data
Seed the complete RBAC (Role-Based Access Control) configuration from the baseline application.
What Gets Seeded
| DocType | Count | Description |
|---|---|---|
| Role (Frappe native) | 4 | Platform Administrator, Study Designer, Study Coordinator, Principal Investigator — created in Frappe's built-in Role DocType so user signup can assign roles via Link fields |
| CTMS Role | 4 | Same 4 roles in the CTMS custom DocType — used by the RBAC permission matrix |
| CTMS Resource | 24 | Protected resources (study, subject, site, form, event, user, etc.) |
| CTMS Action | 8 | create, read, update, delete, export, import, bulk_delete, generate |
| CTMS Permission | 488 | Complete role × resource × action permission matrix |
| CTMS Navigation | 11 | Dashboard, Studies, Subjects, Sites, eCRF Designer, Reports, Management, etc. |
Running the Script
# Seed all RBAC data
python frappe_seed_rbac.py --seed-defaults --env ../../.env.example
# Preview changes
python frappe_seed_rbac.py --seed-defaults --dry-run --env ../../.env.example
| Flag | Description |
|---|---|
--seed-defaults | Seed default roles, resources, actions, permissions, and navigation |
--all | Create RBAC DocTypes and seed data (legacy — prefer Step 1 + --seed-defaults) |
--dry-run | Preview without making changes |
--env <path> | Path to environment file |
--log-level | Logging verbosity (default: INFO) |
After seeding, verify at your Frappe instance:
- Frappe native Roles:
https://your-frappe-url/api/resource/Role?filters=[["name","in",["Platform Administrator","Study Designer","Study Coordinator","Principal Investigator"]]] - CTMS Roles:
https://your-frappe-url/app/ctms-role - Permissions:
https://your-frappe-url/app/ctms-permission
Expected totals: 4 Frappe native roles, 4 CTMS roles, 24 resources, 8 actions, 488 permissions (539 total records).
Uses upsert logic: creates missing records, updates records that differ, skips unchanged records.
Step 4: Seed Master / Reference Data
Populate study-design lookup tables, medical departments, laboratory items, the drug catalogue, clinical trial sites, and study events.
What Gets Seeded
Study-Design Lookups
| DocType | Count | Description |
|---|---|---|
| Intervention Model | 4 | Crossover, Factorial, Parallel, Single Group |
| Masking | 5 | Open Label, Single / Double / Triple / Quadruple Blind |
| Control | 5 | Active, Dose Comparison, Historical, No Treatment, Placebo |
| Allocation | 3 | Randomized, Non-Randomized, Not Applicable |
| Classification | 7 | Bioavailability, Bioequivalence, Efficacy, PK, PD, Safety, Safety/Efficacy |
| Purpose | 7 | Basic Science, Diagnostic, HSR, Prevention, Screening, Supportive Care, Treatment |
| Event Type | 3 | Common, Screening, Visit Based |
| Study Type | 2 | Interventional, Observational |
| Study Phase | 4 | Phase I, Phase II, Phase III, Phase IV |
| Study Status | 5 | Planning, Recruiting, Active, Completed, Suspended |
| Site Status | 3 | Active, Inactive, Pending |
Clinical Infrastructure
| DocType | Count | Description |
|---|---|---|
| Medical Department | 1 | Clinical Trial |
| Item (Laboratory) | 4 | Laboratory, Imaging, USG Abdomen, X-Ray Chest |
| Location | 1 | Princeton - College Road |
Study Events
| DocType | Count | Description |
|---|---|---|
| Study Event | 17 | Screening Visit, Baseline Visit, Cycle 1 Day 1/8/15, Cycle 2 Day 1/8/15, Cycle 3 Day 1, End of Treatment, 30-Day Follow-up, Final Visit, Early Termination, Unscheduled Visit (repeating), Adverse Event (repeating), Visit 1, Visit 2 |
Study events represent milestones and visits in a clinical trial schedule. They are used to link CRF forms, Adverse Events, Vital Signs, and other clinical data to specific timepoints.
Demo Sites (Sample Data)
Site Locations and Sites are sample/demo data for multi-site clinical trial demonstrations. Replace or extend these with your actual site information for production use.
| DocType | Count | Description |
|---|---|---|
| Site Location | 2 | Memorial Cancer Center (New York, NY), Johns Hopkins Oncology Center (Baltimore, MD) |
| Site | 2 | Memorial Cancer Center - Oncology Trials Unit (120 participants), Johns Hopkins - Sidney Kimmel Clinical Trials (80 participants) |
Drug Catalogue
| DocType | Count | Description |
|---|---|---|
| zynomi_drug_master | 52 | Drug catalogue with ATC codes, dosage forms, and status |
Summary
| Category | Records |
|---|---|
| Study-design lookups | 48 |
| Clinical infrastructure | 6 |
| Study events | 17 |
| Demo sites (locations + sites) | 4 |
| Drug catalogue | 52 |
| Total | 127 |
Running the Script
# Seed all master data
python frappe_seed_master_data.py --env ../../.env.example
# Preview changes
python frappe_seed_master_data.py --dry-run --env ../../.env.example
# Seed subsets
python frappe_seed_master_data.py --lookups-only --env ../../.env.example # 31 lookup records
python frappe_seed_master_data.py --drugs-only --env ../../.env.example # 52 drug records
| Flag | Description |
|---|---|
--env <path> | Path to environment file |
--dry-run | Preview without creating |
--lookups-only | Seed only study-design lookup tables (31 records) |
--drugs-only | Seed only drug master catalogue (52 records) |
--log-level | Logging verbosity (default: INFO) |
After seeding, verify at your Frappe instance:
- Study Design:
https://your-frappe-url/app/intervention-model,/app/masking,/app/control, etc. - Study Events:
https://your-frappe-url/app/study-event - Sites:
https://your-frappe-url/app/site - Site Locations:
https://your-frappe-url/app/site-location - Drug Master:
https://your-frappe-url/app/zynomi_drug_master
Expected totals: 48 lookups + 6 clinical infra + 17 study events + 4 demo sites + 52 drugs = 127 records.
Skips records that already exist (matched by name or drug_code). Safe to re-run.
Step 5: Create Default Healthcare Practitioner
Create the default Healthcare Practitioner used by the CTMS platform. The practitioner's auto-generated ID is referenced as NEXT_PUBLIC_DEFAULT_PRACTITIONER_ID in all deployment .env files.
What Gets Created
| DocType | Count | Description |
|---|---|---|
| Healthcare Practitioner | 1 | Mary Williams — default practitioner (naming series: HLC-PRAC-.YYYY.-) |
Practitioner details:
| Field | Value |
|---|---|
| First Name | Mary |
| Last Name | Williams |
| Gender | Female |
| Department | Clinical Trial (created in Stage 4) |
| Status | Active |
| Practitioner Type | External |
Running the Script
# Create the default practitioner
python frappe_seed_practitioner.py --env ../../.env.example
# Preview (dry run)
python frappe_seed_practitioner.py --dry-run --env ../../.env.example
| Flag | Description |
|---|---|
--env <path> | Path to environment file |
--dry-run | Preview without making changes |
--log-level | Logging verbosity (default: INFO) |
Environment variable overrides:
| Variable | Default | Description |
|---|---|---|
PRACTITIONER_FIRST_NAME | Mary | First name |
PRACTITIONER_LAST_NAME | Williams | Last name |
PRACTITIONER_GENDER | Female | Gender |
PRACTITIONER_DEPARTMENT | Clinical Trial | Medical department (must exist) |
PRACTITIONER_STATUS | Active | Status |
PRACTITIONER_TYPE | External | Practitioner type |
Output
After creation, the script prints the auto-generated practitioner ID:
┌─────────────────────────────────────────────────────┐
│ NEXT_PUBLIC_DEFAULT_PRACTITIONER_ID=HLC-PRAC-2026-00001
│ PRACTITIONER_NAME="Mary Williams"
│ │
│ Update these values in your .env / .env.<instance> │
└─────────────────────────────────────────────────────┘
Copy these values into your .env / .env.<instance> file.
If a practitioner with the same first name and last name already exists, the script skips creation and prints the existing practitioner's ID.
Manual Alternative (API Gateway)
If you prefer to create the practitioner manually (e.g., via the API Gateway or Frappe UI):
Option A: REST API via API Gateway
# Create Healthcare Practitioner via API Gateway
curl -X POST "https://your-frappe-url/api/resource/Healthcare%20Practitioner" \
-H "Authorization: token <api_key>:<api_secret>" \
-H "Content-Type: application/json" \
-d '{
"naming_series": "HLC-PRAC-.YYYY.-",
"first_name": "Mary",
"last_name": "Williams",
"gender": "Female",
"status": "Active",
"practitioner_type": "External",
"department": "Clinical Trial"
}'
The response data.name field contains the practitioner ID (e.g., HLC-PRAC-2026-00001).
Option B: Frappe UI
- Navigate to
https://your-frappe-url/app/healthcare-practitioner/new. - Fill in: First Name: Mary, Last Name: Williams, Gender: Female, Department: Clinical Trial, Status: Active, Type: External.
- Click Save. The practitioner ID will appear in the URL bar.
After Manual Creation
Update your .env file:
NEXT_PUBLIC_DEFAULT_PRACTITIONER_ID=HLC-PRAC-2026-XXXXX
PRACTITIONER_NAME="Mary Williams"
The "Clinical Trial" medical department must exist before creating the practitioner (it is created in Stage 4). If running manually, ensure Stage 4 has been completed first, or create the department manually at https://your-frappe-url/app/medical-department/new.
Supabase Table Provisioning (Required for All Deployments)
In addition to Frappe provisioning, the ctms-supabase-seed container creates CTMS-specific tables in the Supabase PostgreSQL database. This is required for both cloud and self-hosted Supabase.
docker compose --env-file .env.production --profile init run --rm ctms-supabase-seed
This creates:
profilestable (auto-linked toauth.usersvia trigger)devicestablemedication_consumption_logstablenotification_logstableget_medication_status()function- Row-Level Security policies on all tables
For cloud Supabase, set DATABASE_URL and SUPABASE_AUTH_URL in your .env file to point to your managed instance.
If you previously created these tables via the Supabase SQL Editor, the seed container will detect them and skip creation. The container approach is recommended as it creates all tables, functions, and RLS policies consistently.
Branding & Logo Configuration
Customize the platform's logo and branding by providing logo files and setting environment variables.
Environment Variables
| Variable | Description | Example |
|---|---|---|
NEXT_PUBLIC_LOGO_PREFIX | Path prefix to logo files (without extension) | /i/mycompany/logo |
NEXT_PUBLIC_LOGO_EXT | File extension | png or svg |
NEXT_PUBLIC_BRAND_NAME | Client brand name (leave blank for unbranded) | MyCompany |
NEXT_PUBLIC_SITE_URL | Site URL for SEO | https://your-domain.com |
NEXT_PUBLIC_SITE_DESCRIPTION | Description for search engines | Clinical Trial Management |
NEXT_PUBLIC_OG_IMAGE | OpenGraph image path | /og-image.png |
Required Logo Files
Based on the prefix and extension, provide these four files in the public/ folder:
| File Pattern | Usage | Example |
|---|---|---|
{prefix}.{ext} | Main logo (light/default) | /i/mycompany/logo.png |
{prefix}-dark.{ext} | Main logo (dark variant for sidebar) | /i/mycompany/logo-dark.png |
{prefix}-mark.{ext} | Logo mark/icon (light/default) | /i/mycompany/logo-mark.png |
{prefix}-mark-dark.{ext} | Logo mark (dark variant for collapsed sidebar) | /i/mycompany/logo-mark-dark.png |
Logo Usage by Context
| Context | Logo Used |
|---|---|
| Login / Signup pages | Main logo ({prefix}.{ext}) |
| Sidebar (expanded) | Dark variant ({prefix}-dark.{ext}) |
| Sidebar (collapsed) | Mark dark variant ({prefix}-mark-dark.{ext}) |
Sublink Mobile App Branding
| Variable | Description |
|---|---|
NUXT_PUBLIC_APP_NAME | App name shown in Sublink |
NUXT_PUBLIC_APP_TAGLINE | App tagline |
For best results, use transparent PNG or SVG files. Dark variants should be visible on dark/colored backgrounds (typically white or light-colored logos).
Setup Checklist
| Step | Action | Status |
|---|---|---|
| 1 | Create 34 custom DocTypes | ☐ |
| 2 | Create 15 custom fields on built-in DocTypes | ☐ |
| 3 | Seed 539 RBAC records (incl. 4 Frappe native roles) | ☐ |
| 4 | Seed 88 master data records | ☐ |
| 5 | Create default Healthcare Practitioner | ☐ |
| 6 | Configure branding & logo files | ☐ |
Next Steps
- Bundle Deployment — Deploy the full platform
- Docker Compose Profiles — Service profiles and startup order
- Environment Variables — Configuration reference