Skip to content

Commit dfbc24b

Browse files
authored
Reset migrations and created a seeder folder in the backend with the initial experiences/treatments/qualities/roles/forms (#51)
Hack the North pt. 1 Next steps: for the intake forms - updating the seed data with the new experiences/treatments if needed, fetching the experiences from the backend rather than hardcoding them in the frontend, having a system so that cancer patients and caregivers are shown different experiences. After that I will probably work on the participant dashboard. The designs for the forms are slightly updated now so that should also be changed (will leave this for later in the term) ## Notion ticket link <!-- Please replace with your ticket's URL --> [Ticket Name](https://www.notion.so/uwblueprintexecs/Task-Board-db95cd7b93f245f78ee85e3a8a6a316d) <!-- Give a quick summary of the implementation details, provide design justifications if necessary --> ## Implementation description * <!-- What should the reviewer do to verify your changes? Describe expected results and include screenshots when appropriate --> ## Steps to test 1. <!-- Draw attention to the substantial parts of your PR or anything you'd like a second opinion on --> ## What should reviewers focus on? * ## Checklist - [ ] My PR name is descriptive and in imperative tense - [ ] My commit messages are descriptive and in imperative tense. My commits are atomic and trivial commits are squashed or fixup'd into non-trivial commits - [ ] I have run the appropriate linter(s) - [ ] I have requested a review from the PL, as well as other devs who have background knowledge on this PR or who will be building on top of this PR
1 parent 0b49e14 commit dfbc24b

File tree

39 files changed

+619
-1475
lines changed

39 files changed

+619
-1475
lines changed

.github/workflows/backend-ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ jobs:
7575
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
7676
pdm run alembic upgrade heads
7777
78+
- name: Seed database with reference data
79+
working-directory: ./backend
80+
run: |
81+
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
82+
pdm run seed
83+
7884
- name: Run linting
7985
working-directory: ./backend
8086
run: |
@@ -169,6 +175,12 @@ jobs:
169175
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
170176
pdm run alembic upgrade heads
171177
178+
- name: Seed database with reference data
179+
working-directory: ./backend
180+
run: |
181+
export POSTGRES_DATABASE_URL="$POSTGRES_TEST_DATABASE_URL"
182+
pdm run seed
183+
172184
- name: Start backend server
173185
working-directory: ./backend
174186
run: |

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