Skip to content

Commit 62496c3

Browse files
ColinToftkingMonkehludavidcagithub-code-quality[bot]
authored
Add more tests (#74)
* update models and run migrations * Move address from user to driver * migration * add user service and update driver service 😭 * IT WORKS DWD AOWD OAWJD OAWDJ OAWJD OAWJDO WAJDOA * fix logout button 😭 * ITS WORKING 😭 * add admin to seed script * tests fix maybe ? * lint * format * Fix type checking issues * Edited Claude yml to debug * removed secret * fix for pull request finding 'Unused local variable' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Order migration after existing migrations * Add tests * Removing incorrect diffs * Moved to Python 3.11 and fixed remaining bugs) --------- Co-authored-by: Hy Lac <hylacnguyen2006@gmail.com> Co-authored-by: David Lu <davidjylu7@gmail.com> Co-authored-by: David Lu <151972620+ludavidca@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
1 parent d96429d commit 62496c3

File tree

17 files changed

+780
-46
lines changed

17 files changed

+780
-46
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
if: steps.changes.outputs.python-backend == 'true'
6161
uses: actions/setup-python@v5
6262
with:
63-
python-version: '3.10'
63+
python-version: '3.11'
6464

6565
- name: Cache pip dependencies
6666
if: steps.changes.outputs.python-backend == 'true'

.github/workflows/pytest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
- name: Set up Python
4545
uses: actions/setup-python@v5
4646
with:
47-
python-version: "3.10"
47+
python-version: "3.11"
4848
cache: "pip"
4949
cache-dependency-path: backend/python/requirements.txt
5050

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ The project uses **GitHub Actions** for continuous integration. All workflows ar
691691
- **Triggers:** Push or PR to `main` for `backend/python/**` paths
692692
- Sets up PostgreSQL service container
693693
- Runs `pytest -q --disable-warnings -ra`
694-
- Python 3.10
694+
- Python 3.11
695695

696696
3. **`claude-code-review.yml`** - Automated Code Review
697697
- **Triggers:** PR ready for review, or `@claude review` comment

backend/python/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.10
1+
FROM python:3.11
22

33
WORKDIR /app
44

backend/python/app/routers/driver_routes.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from typing import Any
23
from uuid import UUID
34

45
from fastapi import APIRouter, Depends, HTTPException, Query, status
@@ -15,6 +16,25 @@
1516
router = APIRouter(prefix="/drivers", tags=["drivers"])
1617

1718

19+
def driver_to_driver_read(driver: Any) -> DriverRead:
20+
"""Convert a Driver model instance to DriverRead."""
21+
return DriverRead(
22+
driver_id=driver.driver_id,
23+
user_id=driver.user_id,
24+
phone=driver.phone,
25+
license_plate=driver.license_plate,
26+
car_make_model=driver.car_make_model,
27+
active=driver.active,
28+
notes=driver.notes,
29+
address=driver.address,
30+
# User fields
31+
auth_id=driver.user.auth_id,
32+
name=driver.user.name,
33+
email=driver.user.email,
34+
role=driver.user.role,
35+
)
36+
37+
1838
@router.get("/", response_model=list[DriverRead])
1939
async def get_drivers(
2040
session: AsyncSession = Depends(get_session),
@@ -39,7 +59,7 @@ async def get_drivers(
3959
status_code=status.HTTP_404_NOT_FOUND,
4060
detail=f"Driver with id {driver_id} not found",
4161
)
42-
return [DriverRead.model_validate(driver)]
62+
return [driver_to_driver_read(driver)]
4363

4464
elif email:
4565
driver = await driver_service.get_driver_by_email(session, email)
@@ -48,11 +68,11 @@ async def get_drivers(
4868
status_code=status.HTTP_404_NOT_FOUND,
4969
detail=f"Driver with email {email} not found",
5070
)
51-
return [DriverRead.model_validate(driver)]
71+
return [driver_to_driver_read(driver)]
5272

5373
else:
5474
drivers = await driver_service.get_drivers(session)
55-
return [DriverRead.model_validate(driver) for driver in drivers]
75+
return [driver_to_driver_read(driver) for driver in drivers]
5676

5777
except HTTPException:
5878
raise
@@ -77,7 +97,7 @@ async def get_driver(
7797
status_code=status.HTTP_404_NOT_FOUND,
7898
detail=f"Driver with id {driver_id} not found",
7999
)
80-
return DriverRead.model_validate(driver)
100+
return driver_to_driver_read(driver)
81101

82102

83103
@router.post("/", response_model=DriverRead, status_code=status.HTTP_201_CREATED)
@@ -91,7 +111,7 @@ async def create_driver(
91111
"""
92112
try:
93113
created_driver = await driver_service.create_driver(session, driver)
94-
return DriverRead.model_validate(created_driver)
114+
return driver_to_driver_read(created_driver)
95115
except Exception as e:
96116
raise HTTPException(
97117
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
@@ -116,7 +136,7 @@ async def update_driver(
116136
status_code=status.HTTP_404_NOT_FOUND,
117137
detail=f"Driver with id {driver_id} not found",
118138
)
119-
return DriverRead.model_validate(updated_driver)
139+
return driver_to_driver_read(updated_driver)
120140

121141

122142
@router.delete("/{driver_id}", status_code=status.HTTP_204_NO_CONTENT)

backend/python/app/routers/route_group_routes.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ async def get_route_groups(
3434
)
3535
result = []
3636
for route_group in route_groups:
37-
data = RouteGroupRead.model_validate(route_group).model_dump()
38-
membership_count = len(route_group.route_group_memberships)
39-
data["num_routes"] = membership_count
37+
data = RouteGroupRead.model_validate(
38+
route_group, from_attributes=True
39+
).model_dump()
4040
if include_routes:
4141
data["routes"] = [
4242
{
@@ -77,15 +77,7 @@ async def create_route_group(
7777
created_route_group = await route_group_service.create_route_group(
7878
session, route_group
7979
)
80-
return RouteGroupRead(
81-
route_group_id=created_route_group.route_group_id,
82-
name=created_route_group.name,
83-
notes=created_route_group.notes,
84-
drive_date=created_route_group.drive_date,
85-
created_at=created_route_group.created_at,
86-
updated_at=created_route_group.updated_at,
87-
num_routes=0,
88-
)
80+
return RouteGroupRead.model_validate(created_route_group, from_attributes=True)
8981
except Exception as e:
9082
raise HTTPException(
9183
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
@@ -111,7 +103,9 @@ async def update_route_group(
111103
status_code=status.HTTP_404_NOT_FOUND,
112104
detail=f"RouteGroup with id {route_group_id} not found",
113105
)
114-
return RouteGroupRead.model_validate(updated_route_group)
106+
return RouteGroupRead.model_validate(updated_route_group, from_attributes=True)
107+
except HTTPException:
108+
raise
115109
except Exception as e:
116110
raise HTTPException(
117111
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
@@ -134,6 +128,8 @@ async def delete_route_group(
134128
status_code=status.HTTP_404_NOT_FOUND,
135129
detail=f"RouteGroup with id {route_group_id} not found",
136130
)
131+
except HTTPException:
132+
raise
137133
except Exception as e:
138134
raise HTTPException(
139135
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)

backend/python/app/seed_database.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ def main() -> None:
405405

406406
# Create locations from CSV
407407
print("Creating locations from CSV...")
408-
csv_path = "app/data/locations.csv"
408+
# Allow CSV path to be overridden via environment variable for testing
409+
csv_path = os.getenv("LOCATIONS_CSV_PATH", "app/data/locations.csv")
409410
locations_created = 0
410411

411412
non_school_groups = [

backend/python/app/services/implementations/driver_service.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async def create_driver(
113113
try:
114114
session.add(driver)
115115
await session.commit()
116-
await session.refresh(driver)
116+
await session.refresh(driver, attribute_names=["user"])
117117
return driver
118118

119119
except Exception as db_error:
@@ -128,7 +128,11 @@ async def update_driver_by_id(
128128
) -> Driver | None:
129129
"""Update driver by ID"""
130130
try:
131-
statement = select(Driver).where(Driver.driver_id == driver_id)
131+
statement = (
132+
select(Driver)
133+
.options(selectinload(Driver.user)) # type: ignore[arg-type]
134+
.where(Driver.driver_id == driver_id)
135+
)
132136
result = await session.execute(statement)
133137
driver = result.scalars().first()
134138

@@ -159,7 +163,7 @@ async def update_driver_by_id(
159163
driver.notes = driver_data.notes
160164

161165
await session.commit()
162-
await session.refresh(driver)
166+
await session.refresh(driver, attribute_names=["user"])
163167
return driver
164168

165169
except Exception as e:

backend/python/app/services/implementations/route_group_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ async def create_route_group(
2121
route_group = RouteGroup.model_validate(route_group_data)
2222
session.add(route_group)
2323
await session.commit()
24-
await session.refresh(route_group)
24+
await session.refresh(route_group, ["route_group_memberships"])
2525
return route_group
2626

2727
async def update_route_group(
@@ -46,7 +46,7 @@ async def update_route_group(
4646
setattr(route_group, field, value)
4747

4848
await session.commit()
49-
await session.refresh(route_group)
49+
await session.refresh(route_group, ["route_group_memberships"])
5050

5151
return route_group
5252

backend/python/app/services/jobs/email_reminder_jobs.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from app.models.driver import Driver
1313
from app.models.driver_assignment import DriverAssignment
1414
from app.models.route import Route
15+
from app.models.user import User
1516
from app.services.implementations.email_service import EmailService
1617

1718

@@ -40,21 +41,21 @@ async def process_daily_reminder_emails() -> None:
4041
# Get all drivers assigned to routes tomorrow
4142
statement = (
4243
select(
43-
Driver.user.email,
44+
User.email,
4445
DriverAssignment.time,
4546
Route.length,
4647
)
4748
.join(Route, DriverAssignment.route_id == Route.route_id) # type: ignore[arg-type]
4849
.join(Driver, DriverAssignment.driver_id == Driver.driver_id) # type: ignore[arg-type]
50+
.join(User, Driver.user_id == User.user_id) # type: ignore[arg-type]
4951
.where(
5052
and_(
51-
Driver.user.email is not None, # type: ignore[arg-type]
5253
DriverAssignment.time >= start_of_day, # type: ignore[arg-type]
5354
DriverAssignment.time <= end_of_day, # type: ignore[arg-type]
5455
DriverAssignment.completed.is_(False), # type: ignore[attr-defined]
5556
)
5657
)
57-
.order_by(Driver.user.email)
58+
.order_by(User.email)
5859
)
5960

6061
result = await session.execute(statement)

0 commit comments

Comments
 (0)