Files
Aether/_deprecated_py_src/api/admin/stats/performance.py
T

94 lines
3.4 KiB
Python
Raw Normal View History

"""Admin performance stats routes."""
from __future__ import annotations
from datetime import date, timezone
from typing import Any
from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.orm import Session
from src.api.base.admin_adapter import AdminApiAdapter
from src.api.base.context import ApiRequestContext
from src.config.constants import CacheTTL
from src.database import get_db
from src.models.database import StatsDaily
from src.services.system.stats_aggregator import StatsAggregatorService
from src.services.system.time_range import TimeRangeParams
from src.utils.cache_decorator import cache_result
from .common import _apply_admin_default_range, _build_time_range_params, pipeline
router = APIRouter()
class AdminPercentilesAdapter(AdminApiAdapter):
def __init__(self, time_range: TimeRangeParams | None) -> None:
self.time_range = _apply_admin_default_range(time_range)
@cache_result(
key_prefix="admin:stats:performance:percentiles",
ttl=CacheTTL.ADMIN_USAGE_AGGREGATION,
user_specific=False,
vary_by=[
"time_range.start_date",
"time_range.end_date",
"time_range.preset",
"time_range.timezone",
"time_range.tz_offset_minutes",
],
)
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
if not self.time_range:
return []
time_range = self.time_range
is_utc = (time_range.timezone in {None, "UTC"}) and time_range.tz_offset_minutes == 0
if is_utc:
start_utc, end_utc = time_range.to_utc_datetime_range()
rows = (
context.db.query(StatsDaily)
.filter(StatsDaily.date >= start_utc, StatsDaily.date < end_utc)
.order_by(StatsDaily.date.asc())
.all()
)
result = []
for row in rows:
date_str = (
row.date.astimezone(timezone.utc).date().isoformat()
if row.date.tzinfo
else row.date.date().isoformat()
)
result.append(
{
"date": date_str,
"p50_response_time_ms": row.p50_response_time_ms,
"p90_response_time_ms": row.p90_response_time_ms,
"p99_response_time_ms": row.p99_response_time_ms,
"p50_first_byte_time_ms": row.p50_first_byte_time_ms,
"p90_first_byte_time_ms": row.p90_first_byte_time_ms,
"p99_first_byte_time_ms": row.p99_first_byte_time_ms,
}
)
return result
return StatsAggregatorService.compute_percentiles_by_local_day(context.db, time_range)
@router.get("/performance/percentiles")
async def get_percentiles(
request: Request,
db: Session = Depends(get_db),
start_date: date | None = Query(None),
end_date: date | None = Query(None),
preset: str | None = Query(None),
timezone_name: str | None = Query(None, alias="timezone"),
tz_offset_minutes: int | None = Query(0),
) -> Any:
time_range = _build_time_range_params(
start_date, end_date, preset, timezone_name, tz_offset_minutes
)
adapter = AdminPercentilesAdapter(time_range=time_range)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)