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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ jobs:
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
pdm run alembic upgrade heads

- name: Seed database with reference data
working-directory: ./backend
run: |
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
pdm run seed

- name: Run linting
working-directory: ./backend
run: |
Expand Down Expand Up @@ -169,6 +175,12 @@ jobs:
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
pdm run alembic upgrade heads

- name: Seed database with reference data
working-directory: ./backend
run: |
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
pdm run seed

- name: Start backend server
working-directory: ./backend
run: |
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,15 @@ To apply the migration, run the following command:
```bash
pdm run alembic upgrade head
```

## Database Seeding

```bash
# Seed the database with reference data
cd backend && pdm run seed

### Adding New Seed Data

1. Create or modify seed files in `backend/app/seeds/`
2. Update the runner in `backend/app/seeds/runner.py`
3. Run `pdm run seed` to apply changes
10 changes: 10 additions & 0 deletions backend/app/seeds/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""
Database seeding system for LLSC backend.

This module provides a clean separation between schema migrations and reference data seeding.
All reference data (roles, treatments, experiences, etc.) is managed here instead of in migrations.
"""

from .runner import seed_database

__all__ = ["seed_database"]
36 changes: 36 additions & 0 deletions backend/app/seeds/experiences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Seed experiences data."""

from sqlalchemy.orm import Session

from app.models.Experience import Experience


def seed_experiences(session: Session) -> None:
"""Seed the experiences table with cancer-related experiences."""

experiences_data = [
{"id": 1, "name": "Brain Fog"},
{"id": 2, "name": "Communication Challenges"},
{"id": 3, "name": "Compassion Fatigue"},
{"id": 4, "name": "Feeling Overwhelmed"},
{"id": 5, "name": "Fatigue"},
{"id": 6, "name": "Fertility Issues"},
{"id": 7, "name": "Graft vs Host"},
{"id": 8, "name": "Returning to work or school after/during treatment"},
{"id": 9, "name": "Speaking to your family or friends about the diagnosis"},
{"id": 10, "name": "Relapse"},
{"id": 11, "name": "Anxiety / Depression"},
{"id": 12, "name": "PTSD"},
]

for experience_data in experiences_data:
# Check if experience already exists
existing_experience = session.query(Experience).filter_by(id=experience_data["id"]).first()
if not existing_experience:
experience = Experience(**experience_data)
session.add(experience)
print(f"Added experience: {experience_data['name']}")
else:
print(f"Experience already exists: {experience_data['name']}")

session.commit()
42 changes: 42 additions & 0 deletions backend/app/seeds/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Seed forms data."""

import uuid

from sqlalchemy.orm import Session

from app.models.Form import Form


def seed_forms(session: Session) -> None:
"""Seed the forms table with default form configurations."""

forms_data = [
{
"id": "12345678-1234-1234-1234-123456789012",
"name": "Participant Intake Form",
"version": 1,
"type": "intake",
},
{
"id": "12345678-1234-1234-1234-123456789013",
"name": "Volunteer Intake Form",
"version": 1,
"type": "intake",
},
]

for form_data in forms_data:
# Check if form already exists
form_id = uuid.UUID(form_data["id"])
existing_form = session.query(Form).filter_by(id=form_id).first()
if not existing_form:
# Convert string UUID to UUID object
form_data_copy = form_data.copy()
form_data_copy["id"] = form_id
form = Form(**form_data_copy)
session.add(form)
print(f"Added form: {form_data['name']}")
else:
print(f"Form already exists: {form_data['name']}")

session.commit()
32 changes: 32 additions & 0 deletions backend/app/seeds/qualities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Seed qualities data."""

from sqlalchemy.orm import Session

from app.models.Quality import Quality


def seed_qualities(session: Session) -> None:
"""Seed the qualities table with matching qualities."""

qualities_data = [
{"slug": "same_age", "label": "the same age as"},
{"slug": "same_gender_identity", "label": "the same gender identity as"},
{"slug": "same_ethnic_or_cultural_group", "label": "the same ethnic or cultural group as"},
{"slug": "same_marital_status", "label": "the same marital status as"},
{"slug": "same_parental_status", "label": "the same parental status as"},
{"slug": "same_diagnosis", "label": "the same diagnosis as"},
]

for quality_data in qualities_data:
# Check if quality already exists
existing_quality = session.query(Quality).filter_by(slug=quality_data["slug"]).first()
if not existing_quality:
quality = Quality(**quality_data)
session.add(quality)
print(f"Added quality: {quality_data['slug']}")
else:
# Update label in case it changed
existing_quality.label = quality_data["label"]
print(f"Quality already exists (updated label): {quality_data['slug']}")

session.commit()
27 changes: 27 additions & 0 deletions backend/app/seeds/roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Seed roles data."""

from sqlalchemy.orm import Session

from app.models.Role import Role


def seed_roles(session: Session) -> None:
"""Seed the roles table with default roles."""

roles_data = [
{"id": 1, "name": "participant"},
{"id": 2, "name": "volunteer"},
{"id": 3, "name": "admin"},
]

for role_data in roles_data:
# Check if role already exists
existing_role = session.query(Role).filter_by(id=role_data["id"]).first()
if not existing_role:
role = Role(**role_data)
session.add(role)
print(f"Added role: {role_data['name']}")
else:
print(f"Role already exists: {role_data['name']}")

session.commit()
102 changes: 102 additions & 0 deletions backend/app/seeds/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Database seeding runner."""

import argparse
import logging
import os
import sys

from dotenv import load_dotenv
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.utilities.constants import LOGGER_NAME

# Import all seed functions
from .experiences import seed_experiences
from .forms import seed_forms
from .qualities import seed_qualities
from .roles import seed_roles
from .treatments import seed_treatments

# Load environment variables
load_dotenv()

log = logging.getLogger(LOGGER_NAME("seeds"))


def get_database_session():
"""Create a database session for seeding."""
database_url = os.getenv("POSTGRES_DATABASE_URL")
if not database_url:
raise ValueError("POSTGRES_DATABASE_URL environment variable is required")

engine = create_engine(database_url)
SessionLocal = sessionmaker(bind=engine)
return SessionLocal()


def seed_database(verbose: bool = True) -> None:
"""
Run all database seeding functions.

Args:
verbose: Whether to print detailed output
"""
if verbose:
print("🌱 Starting database seeding...")

session = get_database_session()

try:
# Run all seed functions in dependency order
seed_functions = [
("Roles", seed_roles),
("Treatments", seed_treatments),
("Experiences", seed_experiences),
("Qualities", seed_qualities),
("Forms", seed_forms),
]

for name, seed_func in seed_functions:
if verbose:
print(f"\n📦 Seeding {name}...")
try:
seed_func(session)
if verbose:
print(f"✅ {name} seeded successfully")
except Exception as e:
print(f"❌ Error seeding {name}: {str(e)}")
log.error(f"Error seeding {name}: {str(e)}")
raise

if verbose:
print("\n🎉 Database seeding completed successfully!")

except Exception as e:
session.rollback()
if verbose:
print(f"\n❌ Database seeding failed: {str(e)}")
raise
finally:
session.close()


def main():
"""CLI entry point for database seeding."""

parser = argparse.ArgumentParser(description="Seed the LLSC database with reference data")
parser.add_argument("--quiet", "-q", action="store_true", help="Suppress output")
parser.add_argument("--env", help="Environment (currently unused but for future extension)")

args = parser.parse_args()

try:
seed_database(verbose=not args.quiet)
sys.exit(0)
except Exception as e:
print(f"Seeding failed: {str(e)}")
sys.exit(1)


if __name__ == "__main__":
main()
38 changes: 38 additions & 0 deletions backend/app/seeds/treatments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Seed treatments data."""

from sqlalchemy.orm import Session

from app.models.Treatment import Treatment


def seed_treatments(session: Session) -> None:
"""Seed the treatments table with cancer treatments."""

treatments_data = [
{"id": 1, "name": "Unknown"},
{"id": 2, "name": "Watch and Wait / Active Surveillance"},
{"id": 3, "name": "Chemotherapy"},
{"id": 4, "name": "Immunotherapy"},
{"id": 5, "name": "Oral Chemotherapy"},
{"id": 6, "name": "Radiation"},
{"id": 7, "name": "Maintenance Chemotherapy"},
{"id": 8, "name": "Palliative Care"},
{"id": 9, "name": "Transfusions"},
{"id": 10, "name": "Autologous Stem Cell Transplant"},
{"id": 11, "name": "Allogeneic Stem Cell Transplant"},
{"id": 12, "name": "Haplo Stem Cell Transplant"},
{"id": 13, "name": "CAR-T"},
{"id": 14, "name": "BTK Inhibitors"},
]

for treatment_data in treatments_data:
# Check if treatment already exists
existing_treatment = session.query(Treatment).filter_by(id=treatment_data["id"]).first()
if not existing_treatment:
treatment = Treatment(**treatment_data)
session.add(treatment)
print(f"Added treatment: {treatment_data['name']}")
else:
print(f"Treatment already exists: {treatment_data['name']}")

session.commit()

This file was deleted.

Loading