Files
Aegis/backend/app/routers/worklogs.py
T
kitos d2a46feba8 refactor(docs+comments): add Google-style docstrings and inline comments across backend
Task D — Google-style docstrings (Args/Returns) on every public function,
method, and class across all 158 Python files in the backend. Zero ruff D
violations (pydocstyle Google convention).

Task E — Explanatory one-line comment before every code line (~11600 new
comments). ruff check passes clean after isort re-sort.
2026-06-11 11:06:55 +02:00

246 lines
7.7 KiB
Python

"""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),
}