Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions backend/script/reset_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ async def reset_dev():

for location_data in data["locations"]:
location = LocationEntity(
citation_count=location_data["citation_count"],
warning_count=location_data["warning_count"],
hold_expiration=parse_date(location_data.get("hold_expiration")),
formatted_address=location_data["formatted_address"],
google_place_id=location_data["google_place_id"],
Expand Down
6 changes: 2 additions & 4 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from src.modules.account.account_router import account_router
from src.modules.complaint.complaint_router import complaint_router
from src.modules.incident.incident_router import incident_router
from src.modules.location.location_router import location_router
from src.modules.party.party_router import party_router
from src.modules.police.police_router import police_router
from src.modules.student.student_router import student_router

app = FastAPI()
Expand Down Expand Up @@ -45,5 +44,4 @@ def read_root():
app.include_router(party_router)
app.include_router(student_router)
app.include_router(location_router)
app.include_router(police_router)
app.include_router(complaint_router)
app.include_router(incident_router)
4 changes: 2 additions & 2 deletions backend/src/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
"""

from .account.account_entity import AccountEntity
from .complaint.complaint_entity import ComplaintEntity
from .incident.incident_entity import IncidentEntity
from .location.location_entity import LocationEntity
from .party.party_entity import PartyEntity
from .police.police_entity import PoliceEntity
from .student.student_entity import StudentEntity

__all__ = [
"AccountEntity",
"ComplaintEntity",
"IncidentEntity",
"LocationEntity",
"PartyEntity",
"PoliceEntity",
Expand Down
17 changes: 0 additions & 17 deletions backend/src/modules/complaint/complaint_model.py

This file was deleted.

76 changes: 0 additions & 76 deletions backend/src/modules/complaint/complaint_router.py

This file was deleted.

92 changes: 0 additions & 92 deletions backend/src/modules/complaint/complaint_service.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
from datetime import datetime
from datetime import UTC
from typing import TYPE_CHECKING, Self

from sqlalchemy import DateTime, ForeignKey, Integer, String
from sqlalchemy import DateTime, Enum, ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, MappedAsDataclass, mapped_column, relationship
from src.core.database import EntityBase
from src.modules.complaint.complaint_model import ComplaintData, ComplaintDto
from src.modules.incident.incident_model import IncidentData, IncidentDto, IncidentSeverity

if TYPE_CHECKING:
from src.modules.location.location_entity import LocationEntity


class ComplaintEntity(MappedAsDataclass, EntityBase):
__tablename__ = "complaints"
class IncidentEntity(MappedAsDataclass, EntityBase):
__tablename__ = "incidents"

id: Mapped[int] = mapped_column(Integer, primary_key=True, init=False)
location_id: Mapped[int] = mapped_column(
Integer, ForeignKey("locations.id", ondelete="CASCADE"), nullable=False
)
complaint_datetime: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
incident_datetime: Mapped[str] = mapped_column(DateTime(timezone=True), nullable=False)
severity: Mapped[IncidentSeverity] = mapped_column(Enum(IncidentSeverity), nullable=False)
description: Mapped[str] = mapped_column(String, nullable=False, default="")

# Relationships
Expand All @@ -26,18 +27,25 @@ class ComplaintEntity(MappedAsDataclass, EntityBase):
)

@classmethod
def from_data(cls, data: ComplaintData) -> Self:
def from_data(cls, data: IncidentData) -> Self:
return cls(
location_id=data.location_id,
complaint_datetime=data.complaint_datetime,
incident_datetime=data.incident_datetime,
severity=data.severity,
description=data.description,
)

def to_dto(self) -> ComplaintDto:
def to_dto(self) -> IncidentDto:
"""Convert entity to model."""
return ComplaintDto(
# Ensure incident_datetime is timezone-aware
incident_dt = self.incident_datetime
if incident_dt.tzinfo is None:
incident_dt = incident_dt.replace(tzinfo=UTC)

return IncidentDto(
id=self.id,
location_id=self.location_id,
complaint_datetime=self.complaint_datetime,
incident_datetime=incident_dt,
description=self.description,
severity=self.severity,
)
24 changes: 24 additions & 0 deletions backend/src/modules/incident/incident_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from enum import Enum

from pydantic import AwareDatetime, BaseModel


class IncidentSeverity(Enum):
COMPLAINT = "complaint"
WARNING = "warning"
CITATION = "citation"


class IncidentData(BaseModel):
"""Data DTO for an incident without id."""

location_id: int
incident_datetime: AwareDatetime
description: str = ""
severity: IncidentSeverity


class IncidentDto(IncidentData):
"""Output DTO for an incident."""

id: int
76 changes: 76 additions & 0 deletions backend/src/modules/incident/incident_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from fastapi import APIRouter, Depends, status
from src.core.authentication import authenticate_admin, authenticate_staff_or_admin
from src.modules.account.account_model import AccountDto

from .incident_model import IncidentData, IncidentDto
from .incident_service import IncidentService

incident_router = APIRouter(prefix="/api/locations", tags=["incidents"])


@incident_router.get(
"/{location_id}/incidents",
response_model=list[IncidentDto],
status_code=status.HTTP_200_OK,
summary="Get all incidents for a location",
description="Returns all incidents associated with a given location. Staff or admin only.",
)
async def get_incidents_by_location(
location_id: int,
incident_service: IncidentService = Depends(),
_: AccountDto = Depends(authenticate_staff_or_admin),
) -> list[IncidentDto]:
"""Get all incidents for a location."""
return await incident_service.get_incidents_by_location(location_id)


@incident_router.post(
"/{location_id}/incidents",
response_model=IncidentDto,
status_code=status.HTTP_201_CREATED,
summary="Create an incident for a location",
description="Creates a new incident associated with a location. Admin only.",
)
async def create_incident(
location_id: int,
incident_data: IncidentData,
incident_service: IncidentService = Depends(),
_: AccountDto = Depends(authenticate_admin),
) -> IncidentDto:
"""Create an incident for a location."""
return await incident_service.create_incident(location_id, incident_data)


@incident_router.put(
"/{location_id}/incidents/{incident_id}",
response_model=IncidentDto,
status_code=status.HTTP_200_OK,
summary="Update an incident",
description="Updates an existing incident. Admin only.",
)
async def update_incident(
location_id: int,
incident_id: int,
incident_data: IncidentData,
incident_service: IncidentService = Depends(),
_: AccountDto = Depends(authenticate_admin),
) -> IncidentDto:
"""Update an incident."""
return await incident_service.update_incident(incident_id, location_id, incident_data)


@incident_router.delete(
"/{location_id}/incidents/{incident_id}",
response_model=IncidentDto,
status_code=status.HTTP_200_OK,
summary="Delete an incident",
description="Deletes an incident. Admin only.",
)
async def delete_incident(
location_id: int,
incident_id: int,
incident_service: IncidentService = Depends(),
_: AccountDto = Depends(authenticate_admin),
) -> IncidentDto:
"""Delete an incident."""
return await incident_service.delete_incident(incident_id)
Loading