feat(nadia): Expose missing calculator operations as API endpoints
Outcome: done Changed: src/typosaurus_sandbox/presentation/api/v1/calculator.py — added imports (average, median, variance, percentage, logging), ValuesRequest model, PercentageRequest model, and four new endpoint handlers with logging and ValueError → 422 conversion tests/test_api.py — added 26 test cases across four new test classes Verified by: `make verify` — 93 tests, 0 failures, 0 new warnings Findings: - `ValuesRequest` model accepts `values: list[float]` and serves /average, /median, /variance - `PercentageRequest` model accepts `value: float, total: float` for /percentage - All four endpoints log at DEBUG level on invocation - Empty list (average, median, variance) and zero total (percentage) produce HTTP 422 with descriptive detail Open: none Confidence: high — 26 new tests pass, zero regressions, pattern matches existing endpoint conventions Typosaurus-Run: 34b5946ec981488091bee588eb919ff2 Typosaurus-Node: 644d226aefca431c81cf8be8a46d2070 Typosaurus-Agent: @nadia Refs: #28
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from typosaurus_sandbox.domain.calculator import add, clamp, clamp_to_byte, subtract
|
||||
from typosaurus_sandbox.domain.calculator import add, average, clamp, clamp_to_byte, median, percentage, subtract, variance
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
calculator_router = APIRouter(prefix="/api/v1/calculator")
|
||||
|
||||
@@ -28,6 +32,15 @@ class ClampToByteRequest(BaseModel):
|
||||
value: int = Field(ge=-2147483648, le=2147483647)
|
||||
|
||||
|
||||
class ValuesRequest(BaseModel):
|
||||
values: list[float]
|
||||
|
||||
|
||||
class PercentageRequest(BaseModel):
|
||||
value: float
|
||||
total: float
|
||||
|
||||
|
||||
class IntResult(BaseModel):
|
||||
result: int
|
||||
|
||||
@@ -38,11 +51,13 @@ class FloatResult(BaseModel):
|
||||
|
||||
@calculator_router.post("/add", response_model=IntResult)
|
||||
def calculate_add(body: AddRequest) -> IntResult:
|
||||
logger.debug("add %d + %d", body.left, body.right)
|
||||
return IntResult(result=add(body.left, body.right))
|
||||
|
||||
|
||||
@calculator_router.post("/subtract", response_model=IntResult)
|
||||
def calculate_subtract(body: SubtractRequest) -> IntResult:
|
||||
logger.debug("subtract %d - %d", body.left, body.right)
|
||||
return IntResult(result=subtract(body.left, body.right))
|
||||
|
||||
|
||||
@@ -57,4 +72,46 @@ def calculate_clamp(body: ClampRequest) -> FloatResult:
|
||||
|
||||
@calculator_router.post("/clamp-to-byte", response_model=IntResult)
|
||||
def calculate_clamp_to_byte(body: ClampToByteRequest) -> IntResult:
|
||||
logger.debug("clamp-to-byte %d", body.value)
|
||||
return IntResult(result=clamp_to_byte(body.value))
|
||||
|
||||
|
||||
@calculator_router.post("/average", response_model=FloatResult)
|
||||
def calculate_average(body: ValuesRequest) -> FloatResult:
|
||||
logger.debug("average of %d values", len(body.values))
|
||||
try:
|
||||
result = average(body.values)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=422, detail="values list must not be empty")
|
||||
return FloatResult(result=result)
|
||||
|
||||
|
||||
@calculator_router.post("/median", response_model=FloatResult)
|
||||
def calculate_median(body: ValuesRequest) -> FloatResult:
|
||||
logger.debug("median of %d values", len(body.values))
|
||||
try:
|
||||
result = median(body.values)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=422, detail="values list must not be empty")
|
||||
return FloatResult(result=result)
|
||||
|
||||
|
||||
@calculator_router.post("/variance", response_model=FloatResult)
|
||||
def calculate_variance(body: ValuesRequest) -> FloatResult:
|
||||
logger.debug("variance of %d values", len(body.values))
|
||||
try:
|
||||
result = variance(body.values)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=422, detail="values list must not be empty")
|
||||
return FloatResult(result=result)
|
||||
|
||||
|
||||
@calculator_router.post("/percentage", response_model=FloatResult)
|
||||
def calculate_percentage(body: PercentageRequest) -> FloatResult:
|
||||
logger.debug("percentage %f of %f", body.value, body.total)
|
||||
try:
|
||||
result = percentage(body.value, body.total)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=422, detail="total must not be zero")
|
||||
return FloatResult(result=result)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user