ams_history.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. """API routes for AMS sensor history."""
  2. from datetime import datetime, timedelta
  3. from fastapi import APIRouter, Depends, Query
  4. from pydantic import BaseModel
  5. from sqlalchemy import and_, func, select
  6. from sqlalchemy.ext.asyncio import AsyncSession
  7. from backend.app.core.auth import RequirePermissionIfAuthEnabled
  8. from backend.app.core.database import get_db
  9. from backend.app.core.permissions import Permission
  10. from backend.app.models.ams_history import AMSSensorHistory
  11. from backend.app.models.user import User
  12. router = APIRouter(prefix="/ams-history", tags=["ams-history"])
  13. class AMSHistoryPoint(BaseModel):
  14. recorded_at: datetime
  15. humidity: float | None
  16. humidity_raw: float | None
  17. temperature: float | None
  18. class AMSHistoryResponse(BaseModel):
  19. printer_id: int
  20. ams_id: int
  21. data: list[AMSHistoryPoint]
  22. min_humidity: float | None
  23. max_humidity: float | None
  24. avg_humidity: float | None
  25. min_temperature: float | None
  26. max_temperature: float | None
  27. avg_temperature: float | None
  28. @router.get("/{printer_id}/{ams_id}", response_model=AMSHistoryResponse)
  29. async def get_ams_history(
  30. printer_id: int,
  31. ams_id: int,
  32. hours: int = Query(default=24, ge=1, le=168, description="Hours of history (1-168)"),
  33. db: AsyncSession = Depends(get_db),
  34. _: User | None = RequirePermissionIfAuthEnabled(Permission.AMS_HISTORY_READ),
  35. ):
  36. """Get AMS sensor history for a specific printer and AMS unit."""
  37. since = datetime.now() - timedelta(hours=hours)
  38. # Get data points
  39. result = await db.execute(
  40. select(AMSSensorHistory)
  41. .where(
  42. and_(
  43. AMSSensorHistory.printer_id == printer_id,
  44. AMSSensorHistory.ams_id == ams_id,
  45. AMSSensorHistory.recorded_at >= since,
  46. )
  47. )
  48. .order_by(AMSSensorHistory.recorded_at)
  49. )
  50. records = result.scalars().all()
  51. # Calculate stats
  52. stats_result = await db.execute(
  53. select(
  54. func.min(AMSSensorHistory.humidity).label("min_humidity"),
  55. func.max(AMSSensorHistory.humidity).label("max_humidity"),
  56. func.avg(AMSSensorHistory.humidity).label("avg_humidity"),
  57. func.min(AMSSensorHistory.temperature).label("min_temp"),
  58. func.max(AMSSensorHistory.temperature).label("max_temp"),
  59. func.avg(AMSSensorHistory.temperature).label("avg_temp"),
  60. ).where(
  61. and_(
  62. AMSSensorHistory.printer_id == printer_id,
  63. AMSSensorHistory.ams_id == ams_id,
  64. AMSSensorHistory.recorded_at >= since,
  65. )
  66. )
  67. )
  68. stats = stats_result.one()
  69. return AMSHistoryResponse(
  70. printer_id=printer_id,
  71. ams_id=ams_id,
  72. data=[
  73. AMSHistoryPoint(
  74. recorded_at=r.recorded_at,
  75. humidity=r.humidity,
  76. humidity_raw=r.humidity_raw,
  77. temperature=r.temperature,
  78. )
  79. for r in records
  80. ],
  81. min_humidity=stats.min_humidity,
  82. max_humidity=stats.max_humidity,
  83. avg_humidity=round(stats.avg_humidity, 1) if stats.avg_humidity else None,
  84. min_temperature=stats.min_temp,
  85. max_temperature=stats.max_temp,
  86. avg_temperature=round(stats.avg_temp, 1) if stats.avg_temp else None,
  87. )
  88. @router.delete("/{printer_id}")
  89. async def delete_old_history(
  90. printer_id: int,
  91. days: int = Query(default=30, ge=1, le=365, description="Delete data older than X days"),
  92. db: AsyncSession = Depends(get_db),
  93. _: User | None = RequirePermissionIfAuthEnabled(Permission.AMS_HISTORY_READ),
  94. ):
  95. """Delete old AMS history data for a printer."""
  96. cutoff = datetime.now() - timedelta(days=days)
  97. result = await db.execute(
  98. select(func.count(AMSSensorHistory.id)).where(
  99. and_(
  100. AMSSensorHistory.printer_id == printer_id,
  101. AMSSensorHistory.recorded_at < cutoff,
  102. )
  103. )
  104. )
  105. count = result.scalar()
  106. await db.execute(
  107. AMSSensorHistory.__table__.delete().where(
  108. and_(
  109. AMSSensorHistory.printer_id == printer_id,
  110. AMSSensorHistory.recorded_at < cutoff,
  111. )
  112. )
  113. )
  114. await db.commit()
  115. return {"deleted": count, "message": f"Deleted {count} records older than {days} days"}