Skip to content

Commit 7ee2940

Browse files
committed
reset migrations and created a seeder folder in the backend so that we don't seed the experiences/treatments/qualities in migrations
1 parent 0b49e14 commit 7ee2940

37 files changed

+604
-1474
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,15 @@ To apply the migration, run the following command:
331331
```bash
332332
pdm run alembic upgrade head
333333
```
334+
335+
## Database Seeding
336+
337+
```bash
338+
# Seed the database with reference data
339+
cd backend && pdm run seed
340+
341+
### Adding New Seed Data
342+
343+
1. Create or modify seed files in `backend/app/seeds/`
344+
2. Update the runner in `backend/app/seeds/runner.py`
345+
3. Run `pdm run seed` to apply changes

backend/app/seeds/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Database seeding system for LLSC backend.
3+
4+
This module provides a clean separation between schema migrations and reference data seeding.
5+
All reference data (roles, treatments, experiences, etc.) is managed here instead of in migrations.
6+
"""
7+
8+
from .runner import seed_database
9+
10+
__all__ = ["seed_database"]

backend/app/seeds/experiences.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Seed experiences data."""
2+
3+
from sqlalchemy.orm import Session
4+
5+
from app.models.Experience import Experience
6+
7+
8+
def seed_experiences(session: Session) -> None:
9+
"""Seed the experiences table with cancer-related experiences."""
10+
11+
experiences_data = [
12+
{"id": 1, "name": "Brain Fog"},
13+
{"id": 2, "name": "Communication Challenges"},
14+
{"id": 3, "name": "Compassion Fatigue"},
15+
{"id": 4, "name": "Feeling Overwhelmed"},
16+
{"id": 5, "name": "Fatigue"},
17+
{"id": 6, "name": "Fertility Issues"},
18+
{"id": 7, "name": "Graft vs Host"},
19+
{"id": 8, "name": "Returning to work or school after/during treatment"},
20+
{"id": 9, "name": "Speaking to your family or friends about the diagnosis"},
21+
{"id": 10, "name": "Relapse"},
22+
{"id": 11, "name": "Anxiety / Depression"},
23+
{"id": 12, "name": "PTSD"},
24+
]
25+
26+
for experience_data in experiences_data:
27+
# Check if experience already exists
28+
existing_experience = session.query(Experience).filter_by(id=experience_data["id"]).first()
29+
if not existing_experience:
30+
experience = Experience(**experience_data)
31+
session.add(experience)
32+
print(f"Added experience: {experience_data['name']}")
33+
else:
34+
print(f"Experience already exists: {experience_data['name']}")
35+
36+
session.commit()

backend/app/seeds/forms.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Seed forms data."""
2+
3+
import uuid
4+
from sqlalchemy.orm import Session
5+
6+
from app.models.Form import Form
7+
8+
9+
def seed_forms(session: Session) -> None:
10+
"""Seed the forms table with default form configurations."""
11+
12+
forms_data = [
13+
{
14+
"id": "12345678-1234-1234-1234-123456789012",
15+
"name": "Participant Intake Form",
16+
"version": 1,
17+
"type": "intake",
18+
},
19+
{
20+
"id": "12345678-1234-1234-1234-123456789013",
21+
"name": "Volunteer Intake Form",
22+
"version": 1,
23+
"type": "intake",
24+
},
25+
]
26+
27+
for form_data in forms_data:
28+
# Check if form already exists
29+
form_id = uuid.UUID(form_data["id"])
30+
existing_form = session.query(Form).filter_by(id=form_id).first()
31+
if not existing_form:
32+
# Convert string UUID to UUID object
33+
form_data_copy = form_data.copy()
34+
form_data_copy["id"] = form_id
35+
form = Form(**form_data_copy)
36+
session.add(form)
37+
print(f"Added form: {form_data['name']}")
38+
else:
39+
print(f"Form already exists: {form_data['name']}")
40+
41+
session.commit()

backend/app/seeds/qualities.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Seed qualities data."""
2+
3+
from sqlalchemy.orm import Session
4+
5+
from app.models.Quality import Quality
6+
7+
8+
def seed_qualities(session: Session) -> None:
9+
"""Seed the qualities table with matching qualities."""
10+
11+
qualities_data = [
12+
{"slug": "same_age", "label": "the same age as"},
13+
{"slug": "same_gender_identity", "label": "the same gender identity as"},
14+
{"slug": "same_ethnic_or_cultural_group", "label": "the same ethnic or cultural group as"},
15+
{"slug": "same_marital_status", "label": "the same marital status as"},
16+
{"slug": "same_parental_status", "label": "the same parental status as"},
17+
{"slug": "same_diagnosis", "label": "the same diagnosis as"},
18+
]
19+
20+
for quality_data in qualities_data:
21+
# Check if quality already exists
22+
existing_quality = session.query(Quality).filter_by(slug=quality_data["slug"]).first()
23+
if not existing_quality:
24+
quality = Quality(**quality_data)
25+
session.add(quality)
26+
print(f"Added quality: {quality_data['slug']}")
27+
else:
28+
# Update label in case it changed
29+
existing_quality.label = quality_data["label"]
30+
print(f"Quality already exists (updated label): {quality_data['slug']}")
31+
32+
session.commit()

backend/app/seeds/roles.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Seed roles data."""
2+
3+
from sqlalchemy.orm import Session
4+
5+
from app.models.Role import Role
6+
7+
8+
def seed_roles(session: Session) -> None:
9+
"""Seed the roles table with default roles."""
10+
11+
roles_data = [
12+
{"id": 1, "name": "participant"},
13+
{"id": 2, "name": "volunteer"},
14+
{"id": 3, "name": "admin"},
15+
]
16+
17+
for role_data in roles_data:
18+
# Check if role already exists
19+
existing_role = session.query(Role).filter_by(id=role_data["id"]).first()
20+
if not existing_role:
21+
role = Role(**role_data)
22+
session.add(role)
23+
print(f"Added role: {role_data['name']}")
24+
else:
25+
print(f"Role already exists: {role_data['name']}")
26+
27+
session.commit()

backend/app/seeds/runner.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""Database seeding runner."""
2+
3+
import logging
4+
import os
5+
import sys
6+
from sqlalchemy import create_engine
7+
from sqlalchemy.orm import sessionmaker
8+
from dotenv import load_dotenv
9+
10+
from app.utilities.constants import LOGGER_NAME
11+
12+
# Import all seed functions
13+
from .roles import seed_roles
14+
from .treatments import seed_treatments
15+
from .experiences import seed_experiences
16+
from .qualities import seed_qualities
17+
from .forms import seed_forms
18+
19+
# Load environment variables
20+
load_dotenv()
21+
22+
log = logging.getLogger(LOGGER_NAME("seeds"))
23+
24+
25+
def get_database_session():
26+
"""Create a database session for seeding."""
27+
database_url = os.getenv("POSTGRES_DATABASE_URL")
28+
if not database_url:
29+
raise ValueError("POSTGRES_DATABASE_URL environment variable is required")
30+
31+
engine = create_engine(database_url)
32+
SessionLocal = sessionmaker(bind=engine)
33+
return SessionLocal()
34+
35+
36+
def seed_database(verbose: bool = True) -> None:
37+
"""
38+
Run all database seeding functions.
39+
40+
Args:
41+
verbose: Whether to print detailed output
42+
"""
43+
if verbose:
44+
print("🌱 Starting database seeding...")
45+
46+
session = get_database_session()
47+
48+
try:
49+
# Run all seed functions in dependency order
50+
seed_functions = [
51+
("Roles", seed_roles),
52+
("Treatments", seed_treatments),
53+
("Experiences", seed_experiences),
54+
("Qualities", seed_qualities),
55+
("Forms", seed_forms),
56+
]
57+
58+
for name, seed_func in seed_functions:
59+
if verbose:
60+
print(f"\n📦 Seeding {name}...")
61+
try:
62+
seed_func(session)
63+
if verbose:
64+
print(f"✅ {name} seeded successfully")
65+
except Exception as e:
66+
print(f"❌ Error seeding {name}: {str(e)}")
67+
log.error(f"Error seeding {name}: {str(e)}")
68+
raise
69+
70+
if verbose:
71+
print("\n🎉 Database seeding completed successfully!")
72+
73+
except Exception as e:
74+
session.rollback()
75+
if verbose:
76+
print(f"\n❌ Database seeding failed: {str(e)}")
77+
raise
78+
finally:
79+
session.close()
80+
81+
82+
def main():
83+
"""CLI entry point for database seeding."""
84+
import argparse
85+
86+
parser = argparse.ArgumentParser(description="Seed the LLSC database with reference data")
87+
parser.add_argument("--quiet", "-q", action="store_true", help="Suppress output")
88+
parser.add_argument("--env", help="Environment (currently unused but for future extension)")
89+
90+
args = parser.parse_args()
91+
92+
try:
93+
seed_database(verbose=not args.quiet)
94+
sys.exit(0)
95+
except Exception as e:
96+
print(f"Seeding failed: {str(e)}")
97+
sys.exit(1)
98+
99+
100+
if __name__ == "__main__":
101+
main()

backend/app/seeds/treatments.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Seed treatments data."""
2+
3+
from sqlalchemy.orm import Session
4+
5+
from app.models.Treatment import Treatment
6+
7+
8+
def seed_treatments(session: Session) -> None:
9+
"""Seed the treatments table with cancer treatments."""
10+
11+
treatments_data = [
12+
{"id": 1, "name": "Unknown"},
13+
{"id": 2, "name": "Watch and Wait / Active Surveillance"},
14+
{"id": 3, "name": "Chemotherapy"},
15+
{"id": 4, "name": "Immunotherapy"},
16+
{"id": 5, "name": "Oral Chemotherapy"},
17+
{"id": 6, "name": "Radiation"},
18+
{"id": 7, "name": "Maintenance Chemotherapy"},
19+
{"id": 8, "name": "Palliative Care"},
20+
{"id": 9, "name": "Transfusions"},
21+
{"id": 10, "name": "Autologous Stem Cell Transplant"},
22+
{"id": 11, "name": "Allogeneic Stem Cell Transplant"},
23+
{"id": 12, "name": "Haplo Stem Cell Transplant"},
24+
{"id": 13, "name": "CAR-T"},
25+
{"id": 14, "name": "BTK Inhibitors"},
26+
]
27+
28+
for treatment_data in treatments_data:
29+
# Check if treatment already exists
30+
existing_treatment = session.query(Treatment).filter_by(id=treatment_data["id"]).first()
31+
if not existing_treatment:
32+
treatment = Treatment(**treatment_data)
33+
session.add(treatment)
34+
print(f"Added treatment: {treatment_data['name']}")
35+
else:
36+
print(f"Treatment already exists: {treatment_data['name']}")
37+
38+
session.commit()

backend/migrations/versions/062c84c8ff35_change_to_timezone_aware_timeblocks.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)