Skip to content

Commit 40d3098

Browse files
committed
added AvailabilityTemplate table to store volunteer general availabilities with days of week, added unit tests, updated other parts to use new table
1 parent 4955100 commit 40d3098

22 files changed

+1841
-522
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from sqlalchemy import Boolean, Column, ForeignKey, Integer, Time
2+
from sqlalchemy.dialects.postgresql import UUID
3+
from sqlalchemy.orm import relationship
4+
from sqlalchemy.sql import func
5+
from sqlalchemy import DateTime
6+
7+
from .Base import Base
8+
9+
10+
class AvailabilityTemplate(Base):
11+
"""
12+
Stores recurring weekly availability patterns for volunteers.
13+
Each template represents a time slot on a specific day of the week.
14+
These templates are projected forward to create specific TimeBlocks for matches.
15+
"""
16+
__tablename__ = "availability_templates"
17+
18+
id = Column(Integer, primary_key=True)
19+
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
20+
21+
# Day of week: 0=Monday, 1=Tuesday, ..., 6=Sunday
22+
day_of_week = Column(Integer, nullable=False)
23+
24+
# Time of day (just time, no date)
25+
start_time = Column(Time, nullable=False) # e.g., 14:00:00
26+
end_time = Column(Time, nullable=False) # e.g., 16:00:00
27+
28+
# Optional: for future enhancements (e.g., temporarily disable a template)
29+
is_active = Column(Boolean, default=True)
30+
31+
created_at = Column(DateTime(timezone=True), server_default=func.now())
32+
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
33+
34+
user = relationship("User", back_populates="availability_templates")
35+

backend/app/models/TimeBlock.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,3 @@ class TimeBlock(Base):
1414

1515
# suggested matches
1616
suggested_matches = relationship("Match", secondary="suggested_times", back_populates="suggested_time_blocks")
17-
18-
# the availability that the timeblock is a part of for a given user
19-
users = relationship("User", secondary="available_times", back_populates="availability")

backend/app/models/User.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ class User(Base):
4545

4646
role = relationship("Role")
4747

48-
# time blocks in an availability for a user
49-
availability = relationship("TimeBlock", secondary="available_times", back_populates="users")
48+
# recurring availability templates (day of week + time)
49+
availability_templates = relationship("AvailabilityTemplate", back_populates="user")
5050

5151
participant_matches = relationship("Match", back_populates="participant", foreign_keys=[Match.participant_id])
5252

backend/app/models/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55

66
from app.utilities.constants import LOGGER_NAME
77

8-
from .AvailableTime import available_times
9-
108
# Make sure all models are here to reflect all current models
119
# when autogenerating new migration
10+
from .AvailabilityTemplate import AvailabilityTemplate
1211
from .Base import Base
1312
from .Experience import Experience
1413
from .Form import Form
@@ -35,8 +34,8 @@
3534
"Match",
3635
"MatchStatus",
3736
"User",
38-
"available_times",
3937
"suggested_times",
38+
"AvailabilityTemplate",
4039
"UserData",
4140
"Treatment",
4241
"Experience",
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
from typing import List
22
from uuid import UUID
3+
from datetime import time
34

45
from pydantic import BaseModel
56

6-
from app.schemas.time_block import TimeBlockEntity, TimeRange
7+
from app.schemas.time_block import TimeBlockEntity
8+
9+
10+
class AvailabilityTemplateSlot(BaseModel):
11+
"""Represents a single availability template slot (day of week + time range)"""
12+
day_of_week: int # 0=Monday, 1=Tuesday, ..., 6=Sunday
13+
start_time: time # e.g., 14:00:00
14+
end_time: time # e.g., 16:00:00
715

816

917
class CreateAvailabilityRequest(BaseModel):
1018
user_id: UUID
11-
available_times: List[TimeRange]
19+
templates: List[AvailabilityTemplateSlot]
1220

1321

1422
class CreateAvailabilityResponse(BaseModel):
@@ -22,17 +30,15 @@ class GetAvailabilityRequest(BaseModel):
2230

2331
class AvailabilityEntity(BaseModel):
2432
user_id: UUID
25-
available_times: List[TimeBlockEntity]
33+
templates: List[AvailabilityTemplateSlot]
2634

2735

2836
class DeleteAvailabilityRequest(BaseModel):
2937
user_id: UUID
30-
delete: list[TimeRange] = []
38+
templates: List[AvailabilityTemplateSlot] = []
3139

3240

3341
class DeleteAvailabilityResponse(BaseModel):
3442
user_id: UUID
3543
deleted: int
36-
37-
# return the user’s availability after the update
38-
availability: List[TimeBlockEntity]
44+
templates: List[AvailabilityTemplateSlot] # remaining templates after deletion

backend/app/schemas/user.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .time_block import TimeBlockEntity
1313
from .user_data import UserDataResponse
1414
from .volunteer_data import VolunteerDataResponse
15+
from .availability import AvailabilityTemplateSlot
1516

1617
# TODO:
1718
# confirm complexity rules for fields (such as password)
@@ -143,7 +144,7 @@ class UserResponse(BaseModel):
143144
form_status: FormStatus
144145
user_data: Optional[UserDataResponse] = None
145146
volunteer_data: Optional[VolunteerDataResponse] = None
146-
availability: List[TimeBlockEntity] = []
147+
availability: List[AvailabilityTemplateSlot] = []
147148

148149
model_config = ConfigDict(from_attributes=True)
149150

backend/app/seeds/users.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from app.models.FormSubmission import FormSubmission
1616
from app.models.Task import Task
1717
from app.models.SuggestedTime import suggested_times
18+
from app.models.AvailabilityTemplate import AvailabilityTemplate
1819
from app.utilities.form_constants import ExperienceId, TreatmentId
1920
from sqlalchemy import delete
2021

@@ -360,10 +361,8 @@ def seed_users(session: Session) -> None:
360361
if existing_user.volunteer_data:
361362
session.delete(existing_user.volunteer_data)
362363

363-
# Clear availability relationships (delete time blocks)
364-
if existing_user.availability:
365-
for time_block in list(existing_user.availability):
366-
session.delete(time_block)
364+
# Clear availability templates
365+
session.query(AvailabilityTemplate).filter_by(user_id=existing_user.id).delete()
367366

368367
# Now delete the user
369368
session.delete(existing_user)

0 commit comments

Comments
 (0)