ams_history.py 4.0 KB

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