Skip to content

Commit a00f5b5

Browse files
authored
Merge pull request #34 from GenerateNU/28-registration-and-unregistration
28 registration and unregistration
2 parents ab13e8d + 481a507 commit a00f5b5

File tree

3 files changed

+115
-6
lines changed

3 files changed

+115
-6
lines changed

app/api/endpoints/registration.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from typing import Annotated
22

3-
from fastapi import APIRouter, Depends, HTTPException, Query
3+
from fastapi import APIRouter, Body, Depends, HTTPException, Query, status
44

55
from app.api.endpoints.user import get_current_user
66
from app.models.registration import registration_model
77
from app.models.volunteer import volunteer_model
88
from app.schemas.event import Event
9-
from app.schemas.registration import Registration, RegistrationStatus
9+
from app.schemas.registration import CreateRegistrationRequest, Registration, RegistrationStatus
1010
from app.schemas.user import User, UserType
1111

1212
router = APIRouter()
@@ -21,7 +21,9 @@ async def get_volunteers_by_event(event_id: str) -> list[Registration]:
2121
@router.get("/events/{volunteer_id}", response_model=list[Event])
2222
async def get_events_by_volunteer(
2323
volunteer_id: str,
24-
status: Annotated[RegistrationStatus | None, Query(description="Filter by status")] = None,
24+
registration_status: Annotated[
25+
RegistrationStatus | None, Query(description="Filter by status")
26+
] = None,
2527
current_user: Annotated[User, Depends(get_current_user)] = None,
2628
) -> list[Event]:
2729
volunteer = await volunteer_model.get_volunteer_by_id(volunteer_id)
@@ -37,4 +39,43 @@ async def get_events_by_volunteer(
3739
elif current_user.user_type != UserType.ADMIN:
3840
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")
3941

40-
return await registration_model.get_events_by_volunteer(volunteer_id, status)
42+
return await registration_model.get_events_by_volunteer(volunteer_id, registration_status)
43+
44+
45+
@router.post("/new", response_model=Registration)
46+
async def create_registrion(
47+
registration: Annotated[CreateRegistrationRequest, Body(...)],
48+
current_user: Annotated[User, Depends(get_current_user)],
49+
) -> Registration:
50+
if current_user.user_type != UserType.VOLUNTEER:
51+
raise HTTPException(
52+
status_code=status.HTTP_403_FORBIDDEN, detail="Only volunteers can register for events"
53+
)
54+
55+
if current_user.entity_id is None:
56+
raise HTTPException(
57+
status_code=status.HTTP_400_BAD_REQUEST,
58+
detail="You must be associated with a volunteer profile to register for an event",
59+
)
60+
61+
return await registration_model.create_registration(registration, current_user.entity_id)
62+
63+
64+
@router.put("/unregister/{registration_id}", response_model=Registration)
65+
async def unregister_registration(
66+
registration_id: str,
67+
current_user: Annotated[User, Depends(get_current_user)],
68+
) -> Registration:
69+
if current_user.user_type != UserType.VOLUNTEER:
70+
raise HTTPException(
71+
status_code=status.HTTP_403_FORBIDDEN,
72+
detail="Only volunteers can unregister from events",
73+
)
74+
75+
if current_user.entity_id is None:
76+
raise HTTPException(
77+
status_code=status.HTTP_400_BAD_REQUEST,
78+
detail="You must be associated with a volunteer profile to unregister from an event",
79+
)
80+
81+
return await registration_model.unregister_registration(registration_id, current_user.entity_id)

app/models/registration.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from datetime import datetime
2+
13
from bson import ObjectId
24
from fastapi import HTTPException, status
35
from motor.motor_asyncio import AsyncIOMotorCollection # noqa: TCH002
46

57
from app.database.mongodb import db
68
from app.models.event import event_model
79
from app.schemas.event import Event
8-
from app.schemas.registration import Registration, RegistrationStatus
10+
from app.schemas.registration import CreateRegistrationRequest, Registration, RegistrationStatus
911

1012

1113
class RegistrationModel:
@@ -70,10 +72,71 @@ async def get_events_by_volunteer(
7072
event_docs = await self.registrations.aggregate(pipeline).to_list(length=None)
7173
return [event_model.to_event(event) for event in event_docs]
7274

73-
def _to_volunteer(self, doc) -> Registration:
75+
async def create_registration(
76+
self, registration: CreateRegistrationRequest, volunteer_id: str
77+
) -> Registration:
78+
event_obj_id = ObjectId(registration.event_id)
79+
volunteer_obj_id = ObjectId(volunteer_id)
80+
81+
existing = await self.registrations.find_one(
82+
{"event_id": event_obj_id, "volunteer_id": volunteer_obj_id}
83+
)
84+
85+
if existing:
86+
await self.registrations.update_one(
87+
{"_id": existing["_id"]},
88+
{
89+
"$set": {
90+
"registration_status": RegistrationStatus.UPCOMING,
91+
"registered_at": datetime.now(),
92+
}
93+
},
94+
)
95+
updated_doc = await self.registrations.find_one({"_id": existing["_id"]})
96+
return self._to_registration(updated_doc)
97+
98+
registration_data = {
99+
"event_id": event_obj_id,
100+
"volunteer_id": volunteer_obj_id,
101+
"registered_at": datetime.now(),
102+
"registration_status": RegistrationStatus.UPCOMING,
103+
"clocked_in": False,
104+
"clocked_out": False,
105+
}
106+
107+
result = await self.registrations.insert_one(registration_data)
108+
inserted_doc = await self.registrations.find_one({"_id": result.inserted_id})
109+
110+
return self._to_registration(inserted_doc)
111+
112+
async def unregister_registration(
113+
self, registration_id: str, volunteer_id: str
114+
) -> Registration:
115+
registration = await self.registrations.find_one({"_id": ObjectId(registration_id)})
116+
117+
if not registration:
118+
raise HTTPException(
119+
status_code=status.HTTP_404_NOT_FOUND, detail="Registration not found"
120+
)
121+
122+
if str(registration["volunteer_id"]) != volunteer_id:
123+
raise HTTPException(
124+
status_code=status.HTTP_403_FORBIDDEN,
125+
detail="Not authorized to unregister from this event",
126+
)
127+
128+
await self.registrations.update_one(
129+
{"_id": ObjectId(registration_id)},
130+
{"$set": {"registration_status": RegistrationStatus.UNREGISTERED}},
131+
)
132+
updated_doc = await self.registrations.find_one({"_id": ObjectId(registration_id)})
133+
return self._to_registration(updated_doc)
134+
135+
def _to_registration(self, doc) -> Registration:
74136
registration_data = doc.copy()
75137
registration_data["id"] = str(registration_data["_id"])
76138
registration_data["volunteer_id"] = str(registration_data["volunteer_id"])
139+
registration_data["event_id"] = str(registration_data["event_id"])
77140
return Registration(**registration_data)
78141

79142

app/schemas/registration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class RegistrationStatus(str, Enum):
88
UPCOMING = "upcoming"
99
COMPLETED = "completed"
1010
INCOMPLETED = "incompleted"
11+
UNREGISTERED = "unregistered"
1112

1213

1314
class Registration(BaseModel):
@@ -21,3 +22,7 @@ class Registration(BaseModel):
2122

2223
class Config:
2324
from_attributes = True
25+
26+
27+
class CreateRegistrationRequest(BaseModel):
28+
event_id: str

0 commit comments

Comments
 (0)