Skip to content

Commit f59ad23

Browse files
authored
Store User Language on Registration (#79)
Add a column to the user table to store their language (either English or French) On registration, when they select either french or english we need to store that. Then update the directory to show the user’s language rather than hardcoding it to English ## Notion ticket link <!-- Please replace with your ticket's URL --> [Ticket Name](https://www.notion.so/uwblueprintexecs/Store-User-Language-on-Registration-2a310f3fb1dc8055b3a4fded02a1f6a0?source=copy_link) <!-- 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 ff283d6 commit f59ad23

File tree

20 files changed

+499
-36
lines changed

20 files changed

+499
-36
lines changed

backend/app/models/User.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ class FormStatus(str, PyEnum):
2121
REJECTED = "rejected"
2222

2323

24+
class Language(str, PyEnum):
25+
ENGLISH = "en"
26+
FRENCH = "fr"
27+
28+
2429
class User(Base):
2530
__tablename__ = "users"
2631
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
@@ -42,6 +47,16 @@ class User(Base):
4247
nullable=False,
4348
default=FormStatus.INTAKE_TODO,
4449
)
50+
language = Column(
51+
SQLEnum(
52+
Language,
53+
name="language_enum",
54+
create_type=False,
55+
values_callable=lambda enum_cls: [member.value for member in enum_cls],
56+
),
57+
nullable=False,
58+
default=Language.ENGLISH,
59+
)
4560

4661
role = relationship("Role")
4762

backend/app/models/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from .Task import Task, TaskPriority, TaskStatus, TaskType
2222
from .TimeBlock import TimeBlock
2323
from .Treatment import Treatment
24-
from .User import FormStatus, User
24+
from .User import FormStatus, Language, User
2525
from .UserData import UserData
2626
from .VolunteerData import VolunteerData
2727

@@ -44,6 +44,7 @@
4444
"Form",
4545
"FormSubmission",
4646
"FormStatus",
47+
"Language",
4748
"Task",
4849
"TaskType",
4950
"TaskPriority",

backend/app/routes/auth.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,7 @@ async def get_current_user(
124124
if not user:
125125
raise HTTPException(status_code=404, detail="User not found")
126126

127-
return UserCreateResponse(
128-
id=user.id,
129-
first_name=user.first_name,
130-
last_name=user.last_name,
131-
email=user.email,
132-
role_id=user.role_id,
133-
auth_id=user.auth_id,
134-
approved=user.approved,
135-
form_status=user.form_status,
136-
)
127+
return UserCreateResponse.model_validate(user)
137128
except HTTPException:
138129
raise
139130
except Exception as e:

backend/app/schemas/user.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ class FormStatus(str, Enum):
5050
REJECTED = "rejected"
5151

5252

53+
class Language(str, Enum):
54+
ENGLISH = "en"
55+
FRENCH = "fr"
56+
57+
5358
class UserBase(BaseModel):
5459
"""
5560
Base schema for user model with common attributes shared across schemas.
@@ -107,6 +112,7 @@ class UserUpdateRequest(BaseModel):
107112
role: Optional[UserRole] = None
108113
approved: Optional[bool] = None
109114
form_status: Optional[FormStatus] = None
115+
language: Optional[Language] = None
110116

111117

112118
class UserCreateResponse(BaseModel):
@@ -122,6 +128,7 @@ class UserCreateResponse(BaseModel):
122128
auth_id: str
123129
approved: bool
124130
form_status: FormStatus
131+
language: Language
125132

126133
# from_attributes enables automatic mapping from SQLAlchemy model to Pydantic model
127134
model_config = ConfigDict(from_attributes=True)
@@ -145,6 +152,7 @@ class UserResponse(BaseModel):
145152
user_data: Optional[UserDataResponse] = None
146153
volunteer_data: Optional[VolunteerDataResponse] = None
147154
availability: List[AvailabilityTemplateSlot] = []
155+
language: Language
148156

149157
model_config = ConfigDict(from_attributes=True)
150158

backend/app/seeds/users.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from app.models.SuggestedTime import suggested_times
1515
from app.models.Task import Task
1616
from app.models.Treatment import Treatment
17-
from app.models.User import FormStatus, User
17+
from app.models.User import FormStatus, Language, User
1818
from app.models.UserData import UserData
1919
from app.models.VolunteerData import VolunteerData
2020
from app.utilities.form_constants import ExperienceId, TreatmentId
@@ -375,6 +375,7 @@ def seed_users(session: Session) -> None:
375375
approved=True,
376376
active=True,
377377
form_status=FormStatus.INTAKE_TODO,
378+
language=Language.ENGLISH,
378379
)
379380
session.add(user)
380381
session.flush() # Get user ID

backend/app/services/implementations/intake_form_processor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from sqlalchemy.orm import Session
77

8-
from app.models import Experience, FormStatus, Treatment, User, UserData
8+
from app.models import Experience, FormStatus, Language, Treatment, User, UserData
99

1010
logger = logging.getLogger(__name__)
1111

@@ -86,6 +86,8 @@ def process_form_submission(self, user_id: str, form_data: Dict[str, Any]) -> Us
8686

8787
if "additional_info" in form_data:
8888
user_data.additional_info = self._trim_text(form_data.get("additional_info"))
89+
# Update language for owning user
90+
owning_user.language = form_data.get("language", Language.ENGLISH)
8991

9092
# Commit all changes
9193
self.db.commit()

backend/app/services/implementations/match_service.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -806,10 +806,11 @@ def _attach_initial_suggested_times(self, match: Match, volunteer: User) -> None
806806
)
807807
volunteer_tz = timezone.utc
808808

809-
# Project templates onto the next week
810-
projection_weeks = 1
809+
# Project templates 8 days ahead (1 week + 1 day) to ensure we capture at least one future
810+
# occurrence of each template day, even if today's times have already passed
811+
projection_days = 8
811812

812-
for day_offset in range(projection_weeks * 7):
813+
for day_offset in range(projection_days):
813814
# Calculate target date in UTC
814815
target_date_utc = now + timedelta(days=day_offset)
815816

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""add language enum and column to users model
2+
3+
Revision ID: 329a7ab72d38
4+
Revises: 8d2cd99b9eb8
5+
Create Date: 2025-11-06 19:37:26.647630
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
import sqlalchemy as sa
12+
from alembic import op
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = "329a7ab72d38"
16+
down_revision: Union[str, None] = "8d2cd99b9eb8"
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
_LANGUAGE_VALUES = (
21+
"en",
22+
"fr",
23+
)
24+
25+
_DEFAULT_LANGUAGE = "en"
26+
27+
28+
def upgrade() -> None:
29+
op.execute("CREATE TYPE language_enum AS ENUM ('en', 'fr')")
30+
31+
op.add_column(
32+
"users",
33+
sa.Column(
34+
"language",
35+
sa.Enum(*_LANGUAGE_VALUES, name="language_enum", create_type=False),
36+
nullable=False,
37+
server_default=_DEFAULT_LANGUAGE,
38+
),
39+
)
40+
41+
42+
def downgrade() -> None:
43+
op.drop_column("users", "language")
44+
op.execute("DROP TYPE language_enum")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""merge multiple heads drop_available_times_table
2+
3+
Revision ID: 77423a45f740
4+
Revises: 93cc5dac324a, dda4b46776e9
5+
Create Date: 2025-11-24 21:20:43.406772
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
# revision identifiers, used by Alembic.
12+
revision: str = "77423a45f740"
13+
down_revision: Union[str, None] = ("93cc5dac324a", "dda4b46776e9")
14+
branch_labels: Union[str, Sequence[str], None] = None
15+
depends_on: Union[str, Sequence[str], None] = None
16+
17+
18+
def upgrade() -> None:
19+
pass
20+
21+
22+
def downgrade() -> None:
23+
pass
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""merge multiple heads
2+
3+
Revision ID: 93cc5dac324a
4+
Revises: 23dae9594e1d, 329a7ab72d38
5+
Create Date: 2025-11-24 19:04:33.167997
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
# revision identifiers, used by Alembic.
12+
revision: str = "93cc5dac324a"
13+
down_revision: Union[str, None] = ("23dae9594e1d", "329a7ab72d38")
14+
branch_labels: Union[str, Sequence[str], None] = None
15+
depends_on: Union[str, Sequence[str], None] = None
16+
17+
18+
def upgrade() -> None:
19+
pass
20+
21+
22+
def downgrade() -> None:
23+
pass

0 commit comments

Comments
 (0)