"""Worklog router — internal time-tracking records with integrity verification.""" # Import datetime from datetime from datetime import datetime # Import Optional from typing from typing import Optional # Import UUID from uuid from uuid import UUID # Import APIRouter, Depends from fastapi from fastapi import APIRouter, Depends # Import BaseModel, Field from pydantic from pydantic import BaseModel, Field # Import Session from sqlalchemy.orm from sqlalchemy.orm import Session # Import get_db from app.database from app.database import get_db # Import get_current_user, require_any_role from app.dependencies.auth from app.dependencies.auth import get_current_user, require_any_role # Import UnitOfWork from app.domain.unit_of_work from app.domain.unit_of_work import UnitOfWork # Import User from app.models.user from app.models.user import User # Import worklog_service from app.services from app.services import worklog_service # Assign router = APIRouter(prefix="/worklogs", tags=["worklogs"]) router = APIRouter(prefix="/worklogs", tags=["worklogs"]) # ── Schemas ────────────────────────────────────────────────────────────── class WorklogCreate(BaseModel): """Payload for logging a work session against an entity.""" # Assign entity_type = Field(..., max_length=50) entity_type: str = Field(..., max_length=50) # entity_id: UUID entity_id: UUID # Assign activity_type = Field(..., max_length=100) activity_type: str = Field(..., max_length=100) # started_at: datetime started_at: datetime # Assign ended_at = None ended_at: Optional[datetime] = None # Assign duration_seconds = Field(..., gt=0) duration_seconds: int = Field(..., gt=0) # Assign description = None description: Optional[str] = None # Define class WorklogOut class WorklogOut(BaseModel): """Serialized worklog entry returned by the API.""" # id: UUID id: UUID # entity_type: str entity_type: str # entity_id: UUID entity_id: UUID # user_id: UUID user_id: UUID # activity_type: str activity_type: str # started_at: datetime started_at: datetime # Assign ended_at = None ended_at: Optional[datetime] = None # duration_seconds: int duration_seconds: int # Assign description = None description: Optional[str] = None # Assign tempo_synced = None tempo_synced: Optional[datetime] = None # Assign integrity_hash = None integrity_hash: Optional[str] = None # created_at: datetime created_at: datetime # Define class Config class Config: """ORM mode configuration for SQLAlchemy model mapping.""" # Assign from_attributes = True from_attributes = True # ── Endpoints ──────────────────────────────────────────────────────────── @router.post("", response_model=WorklogOut, status_code=201) # Define function create def create( # Entry: body body: WorklogCreate, # Entry: db db: Session = Depends(get_db), # Entry: user user: User = Depends(require_any_role("red_tech", "blue_tech", "red_lead", "blue_lead")), ) -> WorklogOut: """Create a manually-logged worklog entry. Args: body (WorklogCreate): Worklog fields including entity, activity type, and duration. db (Session): SQLAlchemy database session. user (User): Authenticated team member creating the worklog. Returns: WorklogOut: The newly created worklog with integrity hash and all fields. """ # Open context manager with UnitOfWork(db) as uow: # Assign wl = worklog_service.create_worklog( wl = worklog_service.create_worklog( db, # Keyword argument: entity_type entity_type=body.entity_type, # Keyword argument: entity_id entity_id=body.entity_id, # Keyword argument: user_id user_id=user.id, # Keyword argument: activity_type activity_type=body.activity_type, # Keyword argument: started_at started_at=body.started_at, # Keyword argument: ended_at ended_at=body.ended_at, # Keyword argument: duration_seconds duration_seconds=body.duration_seconds, # Keyword argument: description description=body.description, ) # Call uow.commit() uow.commit() # Reload ORM object attributes from the database db.refresh(wl) # Return wl return wl # Apply the @router.get decorator @router.get("", response_model=list[WorklogOut]) # Define function list_all def list_all( # Entry: entity_type entity_type: Optional[str] = None, # Entry: entity_id entity_id: Optional[UUID] = None, # Entry: user_id user_id: Optional[UUID] = None, # Entry: db db: Session = Depends(get_db), # Entry: _user _user: User = Depends(get_current_user), ) -> list[WorklogOut]: """List worklogs with optional filters. Args: entity_type (Optional[str]): Filter by entity type (e.g. ``test``, ``campaign``). entity_id (Optional[UUID]): Filter by the UUID of the associated entity. user_id (Optional[UUID]): Filter by the UUID of the worklog author. db (Session): SQLAlchemy database session. _user (User): Authenticated user making the request (unused, enforces auth). Returns: list[WorklogOut]: Serialised list of worklog entries matching the filters. """ # Return worklog_service.list_worklogs( return worklog_service.list_worklogs( db, # Keyword argument: entity_type entity_type=entity_type, # Keyword argument: entity_id entity_id=entity_id, # Keyword argument: user_id user_id=user_id, ) # Apply the @router.get decorator @router.get("/{worklog_id}", response_model=WorklogOut) # Define function get_one def get_one( # Entry: worklog_id worklog_id: UUID, # Entry: db db: Session = Depends(get_db), # Entry: _user _user: User = Depends(get_current_user), ) -> WorklogOut: """Get a single worklog by ID. Args: worklog_id (UUID): Primary key of the worklog to retrieve. db (Session): SQLAlchemy database session. _user (User): Authenticated user making the request (unused, enforces auth). Returns: WorklogOut: Full worklog detail including integrity hash. """ # Return worklog_service.get_worklog_or_raise(db, worklog_id) return worklog_service.get_worklog_or_raise(db, worklog_id) # Apply the @router.get decorator @router.get("/{worklog_id}/verify") # Define function verify_integrity def verify_integrity( # Entry: worklog_id worklog_id: UUID, # Entry: db db: Session = Depends(get_db), # Entry: _user _user: User = Depends(get_current_user), ) -> dict: """Check whether a worklog's integrity hash is still valid. Args: worklog_id (UUID): Primary key of the worklog to verify. db (Session): SQLAlchemy database session. _user (User): Authenticated user making the request (unused, enforces auth). Returns: dict: Contains ``worklog_id`` (str) and ``integrity_valid`` (bool). """ # Assign wl = worklog_service.get_worklog_or_raise(db, worklog_id) wl = worklog_service.get_worklog_or_raise(db, worklog_id) # Return { return { # Literal argument value "worklog_id": str(wl.id), # Literal argument value "integrity_valid": worklog_service.verify_worklog_integrity(wl), }