Skip to content

Commit be5e4bf

Browse files
committed
backend from before with resolved merge conflicts?
1 parent 0a3673a commit be5e4bf

File tree

14 files changed

+1009
-2
lines changed

14 files changed

+1009
-2
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Interface for volunteer data service operations.
3+
Defines the contract for volunteer data CRUD operations.
4+
"""
5+
6+
from abc import ABC, abstractmethod
7+
from typing import List
8+
9+
from app.schemas.volunteer_data import (
10+
VolunteerDataCreateRequest,
11+
VolunteerDataResponse,
12+
VolunteerDataUpdateRequest,
13+
)
14+
15+
16+
class IVolunteerDataService(ABC):
17+
"""
18+
Interface for volunteer data service operations
19+
"""
20+
21+
@abstractmethod
22+
async def create_volunteer_data(
23+
self, volunteer_data: VolunteerDataCreateRequest
24+
) -> VolunteerDataResponse:
25+
"""Create new volunteer data entry"""
26+
pass
27+
28+
@abstractmethod
29+
async def get_volunteer_data_by_id(self, volunteer_data_id: str) -> VolunteerDataResponse:
30+
"""Get volunteer data by ID"""
31+
pass
32+
33+
@abstractmethod
34+
async def get_volunteer_data_by_user_id(self, user_id: str) -> VolunteerDataResponse:
35+
"""Get volunteer data by user ID"""
36+
pass
37+
38+
@abstractmethod
39+
async def get_all_volunteer_data(self) -> List[VolunteerDataResponse]:
40+
"""Get all volunteer data entries"""
41+
pass
42+
43+
@abstractmethod
44+
async def update_volunteer_data_by_id(
45+
self, volunteer_data_id: str, volunteer_data_update: VolunteerDataUpdateRequest
46+
) -> VolunteerDataResponse:
47+
"""Update volunteer data by ID"""
48+
pass
49+
50+
@abstractmethod
51+
async def delete_volunteer_data_by_id(self, volunteer_data_id: str) -> None:
52+
"""Delete volunteer data by ID"""
53+
pass

backend/app/models/User.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ class User(Base):
4949
participant_matches = relationship("Match", back_populates="participant", foreign_keys=[Match.participant_id])
5050

5151
volunteer_matches = relationship("Match", back_populates="volunteer", foreign_keys=[Match.volunteer_id])
52+
53+
volunteer_data = relationship("VolunteerData", back_populates="user", uselist=False)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import uuid
2+
from datetime import datetime
3+
4+
from sqlalchemy import Column, DateTime, ForeignKey, Text
5+
from sqlalchemy.dialects.postgresql import UUID
6+
from sqlalchemy.orm import relationship
7+
8+
from .Base import Base
9+
10+
11+
class VolunteerData(Base):
12+
__tablename__ = "volunteer_data"
13+
14+
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
15+
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=True)
16+
experience = Column(Text, nullable=True)
17+
references_json = Column(Text, nullable=True)
18+
additional_comments = Column(Text, nullable=True)
19+
submitted_at = Column(DateTime, default=datetime.utcnow, nullable=False)
20+
21+
user = relationship("User", back_populates="volunteer_data")

backend/app/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .Treatment import Treatment
2525
from .User import FormStatus, User
2626
from .UserData import UserData
27+
from .VolunteerData import VolunteerData
2728

2829
# Used to avoid import errors for the models
2930
__all__ = [
@@ -48,6 +49,7 @@
4849
"TaskType",
4950
"TaskPriority",
5051
"TaskStatus",
52+
"VolunteerData",
5153
]
5254

5355
log = logging.getLogger(LOGGER_NAME("models"))
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
from fastapi import APIRouter, Depends, HTTPException
2+
3+
from app.middleware.auth import has_roles
4+
from app.schemas.user import UserRole
5+
from app.schemas.volunteer_data import (
6+
VolunteerDataCreateRequest,
7+
VolunteerDataListResponse,
8+
VolunteerDataPublicSubmission,
9+
VolunteerDataResponse,
10+
VolunteerDataUpdateRequest,
11+
)
12+
from app.services.implementations.volunteer_data_service import VolunteerDataService
13+
from app.utilities.service_utils import get_volunteer_data_service
14+
15+
router = APIRouter(
16+
prefix="/volunteer-data",
17+
tags=["volunteer-data"],
18+
)
19+
20+
21+
# Public endpoint - anyone can submit volunteer data
22+
@router.post("/submit", response_model=VolunteerDataResponse)
23+
async def submit_volunteer_data(
24+
volunteer_data: VolunteerDataPublicSubmission,
25+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
26+
):
27+
"""Public endpoint for volunteers to submit their application data"""
28+
try:
29+
create_request = VolunteerDataCreateRequest(
30+
user_id=None,
31+
experience=volunteer_data.experience,
32+
references_json=volunteer_data.references_json,
33+
additional_comments=volunteer_data.additional_comments,
34+
)
35+
return await volunteer_data_service.create_volunteer_data(create_request)
36+
except HTTPException as http_ex:
37+
raise http_ex
38+
except Exception as e:
39+
raise HTTPException(status_code=500, detail=str(e))
40+
41+
42+
# Admin only - create volunteer data
43+
@router.post("/", response_model=VolunteerDataResponse)
44+
async def create_volunteer_data(
45+
volunteer_data: VolunteerDataCreateRequest,
46+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
47+
authorized: bool = has_roles([UserRole.ADMIN]),
48+
):
49+
try:
50+
return await volunteer_data_service.create_volunteer_data(volunteer_data)
51+
except HTTPException as http_ex:
52+
raise http_ex
53+
except Exception as e:
54+
raise HTTPException(status_code=500, detail=str(e))
55+
56+
57+
# Admin only - get all volunteer data
58+
@router.get("/", response_model=VolunteerDataListResponse)
59+
async def get_all_volunteer_data(
60+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
61+
authorized: bool = has_roles([UserRole.ADMIN]),
62+
):
63+
try:
64+
volunteer_data_list = await volunteer_data_service.get_all_volunteer_data()
65+
return VolunteerDataListResponse(
66+
volunteer_data=volunteer_data_list, total=len(volunteer_data_list)
67+
)
68+
except HTTPException as http_ex:
69+
raise http_ex
70+
except Exception as e:
71+
raise HTTPException(status_code=500, detail=str(e))
72+
73+
74+
# Admin only - get volunteer data by ID
75+
@router.get("/{volunteer_data_id}", response_model=VolunteerDataResponse)
76+
async def get_volunteer_data(
77+
volunteer_data_id: str,
78+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
79+
authorized: bool = has_roles([UserRole.ADMIN]),
80+
):
81+
try:
82+
return await volunteer_data_service.get_volunteer_data_by_id(volunteer_data_id)
83+
except HTTPException as http_ex:
84+
raise http_ex
85+
except Exception as e:
86+
raise HTTPException(status_code=500, detail=str(e))
87+
88+
89+
# Admin only - get volunteer data by user ID
90+
@router.get("/user/{user_id}", response_model=VolunteerDataResponse)
91+
async def get_volunteer_data_by_user(
92+
user_id: str,
93+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
94+
authorized: bool = has_roles([UserRole.ADMIN]),
95+
):
96+
try:
97+
return await volunteer_data_service.get_volunteer_data_by_user_id(user_id)
98+
except HTTPException as http_ex:
99+
raise http_ex
100+
except Exception as e:
101+
raise HTTPException(status_code=500, detail=str(e))
102+
103+
104+
# Admin only - update volunteer data
105+
@router.put("/{volunteer_data_id}", response_model=VolunteerDataResponse)
106+
async def update_volunteer_data(
107+
volunteer_data_id: str,
108+
volunteer_data_update: VolunteerDataUpdateRequest,
109+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
110+
authorized: bool = has_roles([UserRole.ADMIN]),
111+
):
112+
try:
113+
return await volunteer_data_service.update_volunteer_data_by_id(
114+
volunteer_data_id, volunteer_data_update
115+
)
116+
except HTTPException as http_ex:
117+
raise http_ex
118+
except Exception as e:
119+
raise HTTPException(status_code=500, detail=str(e))
120+
121+
122+
# Admin only - delete volunteer data
123+
@router.delete("/{volunteer_data_id}")
124+
async def delete_volunteer_data(
125+
volunteer_data_id: str,
126+
volunteer_data_service: VolunteerDataService = Depends(get_volunteer_data_service),
127+
authorized: bool = has_roles([UserRole.ADMIN]),
128+
):
129+
try:
130+
await volunteer_data_service.delete_volunteer_data_by_id(volunteer_data_id)
131+
return {"message": "Volunteer data deleted successfully"}
132+
except HTTPException as http_ex:
133+
raise http_ex
134+
except Exception as e:
135+
raise HTTPException(status_code=500, detail=str(e))
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
Pydantic schemas for volunteer data validation and serialization.
3+
Handles volunteer data CRUD and response models for the API.
4+
"""
5+
6+
from datetime import datetime
7+
from typing import List, Optional
8+
from uuid import UUID
9+
10+
from pydantic import BaseModel, ConfigDict, Field
11+
12+
13+
class VolunteerDataBase(BaseModel):
14+
"""
15+
Base schema for volunteer data model with common attributes.
16+
"""
17+
18+
experience: Optional[str] = Field(None, description="Volunteer experience description")
19+
references_json: Optional[str] = Field(None, description="JSON string containing references")
20+
additional_comments: Optional[str] = Field(None, description="Additional comments about volunteering")
21+
22+
23+
class VolunteerDataCreateRequest(VolunteerDataBase):
24+
"""
25+
Request schema for creating volunteer data
26+
"""
27+
28+
user_id: Optional[UUID] = Field(
29+
None,
30+
description="User ID this volunteer data belongs to (optional for public submissions)"
31+
)
32+
33+
34+
class VolunteerDataPublicSubmission(VolunteerDataBase):
35+
"""
36+
Request schema for public volunteer data submissions (no user_id required)
37+
"""
38+
39+
pass
40+
41+
42+
class VolunteerDataUpdateRequest(BaseModel):
43+
"""
44+
Request schema for updating volunteer data, all fields optional
45+
"""
46+
47+
experience: Optional[str] = Field(None, description="Volunteer experience description")
48+
references_json: Optional[str] = Field(None, description="JSON string containing references")
49+
additional_comments: Optional[str] = Field(None, description="Additional comments about volunteering")
50+
51+
52+
class VolunteerDataResponse(BaseModel):
53+
"""
54+
Response schema for volunteer data
55+
"""
56+
57+
id: UUID
58+
user_id: UUID
59+
experience: Optional[str]
60+
references_json: Optional[str]
61+
additional_comments: Optional[str]
62+
submitted_at: datetime
63+
64+
model_config = ConfigDict(from_attributes=True)
65+
66+
67+
class VolunteerDataListResponse(BaseModel):
68+
"""
69+
Response schema for listing volunteer data
70+
"""
71+
72+
volunteer_data: List[VolunteerDataResponse]
73+
total: int

backend/app/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from . import models
1010
from .middleware.auth_middleware import AuthMiddleware
11-
from .routes import auth, availability, intake, match, matching, ranking, send_email, suggested_times, task, test, user
11+
from .routes import auth, availability, intake, match, matching, ranking, send_email, suggested_times, task, test, user, volunteer_data
1212
from .utilities.constants import LOGGER_NAME
1313
from .utilities.firebase_init import initialize_firebase
1414
from .utilities.ses.ses_init import ensure_ses_templates
@@ -71,6 +71,7 @@ async def lifespan(_: FastAPI):
7171
app.include_router(matching.router)
7272
app.include_router(intake.router)
7373
app.include_router(ranking.router)
74+
app.include_router(volunteer_data.router)
7475
app.include_router(send_email.router)
7576
app.include_router(task.router)
7677
app.include_router(test.router)

0 commit comments

Comments
 (0)