notification.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. """Notification provider and log models for push notifications."""
  2. from datetime import datetime
  3. from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
  4. from sqlalchemy.orm import relationship
  5. from backend.app.core.database import Base
  6. class NotificationDigestQueue(Base):
  7. """Model for queuing notifications to be sent in daily digest."""
  8. __tablename__ = "notification_digest_queue"
  9. id = Column(Integer, primary_key=True, index=True)
  10. provider_id = Column(Integer, ForeignKey("notification_providers.id", ondelete="CASCADE"), nullable=False)
  11. event_type = Column(String(50), nullable=False) # print_start, print_complete, etc.
  12. title = Column(String(255), nullable=False)
  13. message = Column(Text, nullable=False)
  14. printer_id = Column(Integer, ForeignKey("printers.id", ondelete="SET NULL"), nullable=True)
  15. printer_name = Column(String(100), nullable=True)
  16. created_at = Column(DateTime, default=datetime.utcnow, index=True)
  17. # Relationships
  18. provider = relationship("NotificationProvider", back_populates="digest_queue")
  19. class NotificationLog(Base):
  20. """Model for logging sent notifications."""
  21. __tablename__ = "notification_logs"
  22. id = Column(Integer, primary_key=True, index=True)
  23. provider_id = Column(Integer, ForeignKey("notification_providers.id", ondelete="CASCADE"), nullable=False)
  24. event_type = Column(String(50), nullable=False) # print_start, print_complete, etc.
  25. title = Column(String(255), nullable=False)
  26. message = Column(Text, nullable=False)
  27. success = Column(Boolean, default=True)
  28. error_message = Column(Text, nullable=True)
  29. printer_id = Column(Integer, ForeignKey("printers.id", ondelete="SET NULL"), nullable=True)
  30. printer_name = Column(String(100), nullable=True) # Store name in case printer is deleted
  31. created_at = Column(DateTime, default=datetime.utcnow, index=True)
  32. # Relationships
  33. provider = relationship("NotificationProvider", back_populates="logs")
  34. class NotificationProvider(Base):
  35. """Model for notification providers (WhatsApp, ntfy, Pushover, etc.)."""
  36. __tablename__ = "notification_providers"
  37. id = Column(Integer, primary_key=True, index=True)
  38. name = Column(String(100), nullable=False) # User-defined name
  39. provider_type = Column(String(50), nullable=False) # callmebot, ntfy, pushover, telegram, email
  40. enabled = Column(Boolean, default=True)
  41. # Provider-specific configuration stored as JSON string
  42. config = Column(Text, nullable=False)
  43. # Event triggers - print lifecycle
  44. on_print_start = Column(Boolean, default=False)
  45. on_print_complete = Column(Boolean, default=True)
  46. on_print_failed = Column(Boolean, default=True)
  47. on_print_stopped = Column(Boolean, default=True) # User cancelled/stopped print
  48. on_print_progress = Column(Boolean, default=False) # 25%, 50%, 75% milestones
  49. on_print_missing_spool_assignment = Column(Boolean, default=False) # Print started with unassigned required tray(s)
  50. # Event triggers - printer status
  51. on_printer_offline = Column(Boolean, default=False)
  52. on_printer_error = Column(Boolean, default=False) # AMS issues, etc.
  53. on_ai_failure_detection = Column(Boolean, default=False) # Obico spaghetti / failure detection (#1794)
  54. on_filament_low = Column(Boolean, default=False)
  55. on_maintenance_due = Column(Boolean, default=False) # Maintenance reminder
  56. # Event triggers - AMS environmental alarms (regular AMS with 4 slots)
  57. on_ams_humidity_high = Column(Boolean, default=False) # AMS humidity above threshold
  58. on_ams_temperature_high = Column(Boolean, default=False) # AMS temperature above threshold
  59. # Event triggers - AMS-HT environmental alarms (single slot heated AMS)
  60. on_ams_ht_humidity_high = Column(Boolean, default=False) # AMS-HT humidity above threshold
  61. on_ams_ht_temperature_high = Column(Boolean, default=False) # AMS-HT temperature above threshold
  62. # Event triggers - Build plate detection
  63. on_plate_not_empty = Column(Boolean, default=True) # Objects detected on plate before print
  64. # Event triggers - Bed cooled after print
  65. on_bed_cooled = Column(Boolean, default=False) # Bed cooled below threshold after print
  66. on_first_layer_complete = Column(Boolean, default=False) # First layer finished printing
  67. # Event triggers - Inventory stock alerts
  68. on_stock_reorder_alert = Column(Boolean, default=False) # SKU hits reorder point
  69. on_stock_break_alert = Column(Boolean, default=False) # Stock will run out before replenishment
  70. # Event triggers - Print queue
  71. on_queue_job_added = Column(Boolean, default=False) # Job added to queue
  72. on_queue_job_assigned = Column(Boolean, default=False) # Model-based job assigned to printer
  73. on_queue_job_started = Column(Boolean, default=False) # Queue job started printing
  74. on_queue_job_waiting = Column(Boolean, default=True) # Job waiting for filament or printer
  75. on_queue_job_skipped = Column(Boolean, default=True) # Job skipped (previous print failed)
  76. on_queue_job_failed = Column(Boolean, default=True) # Job failed to start
  77. on_queue_completed = Column(Boolean, default=False) # All pending jobs finished
  78. # Quiet hours (do not disturb)
  79. quiet_hours_enabled = Column(Boolean, default=False)
  80. quiet_hours_start = Column(String(5), nullable=True) # HH:MM format, e.g., "22:00"
  81. quiet_hours_end = Column(String(5), nullable=True) # HH:MM format, e.g., "07:00"
  82. # Daily digest (batch notifications into a single daily summary)
  83. daily_digest_enabled = Column(Boolean, default=False)
  84. daily_digest_time = Column(String(5), nullable=True) # HH:MM format, e.g., "08:00"
  85. # Optional: Link to specific printer (NULL = all printers)
  86. printer_id = Column(Integer, ForeignKey("printers.id", ondelete="SET NULL"), nullable=True)
  87. # Status tracking
  88. last_success = Column(DateTime, nullable=True)
  89. last_error = Column(Text, nullable=True)
  90. last_error_at = Column(DateTime, nullable=True)
  91. # Timestamps
  92. created_at = Column(DateTime, default=datetime.utcnow)
  93. updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
  94. # Relationships
  95. printer = relationship("Printer", back_populates="notification_providers")
  96. logs = relationship("NotificationLog", back_populates="provider", cascade="all, delete-orphan")
  97. digest_queue = relationship("NotificationDigestQueue", back_populates="provider", cascade="all, delete-orphan")