printers: low-toner alerts with configurable thresholds + support-team routing
Poll Zabbix for toner levels on a schedule and email/webhook on a downward crossing. Warning fires at or below the warning threshold (default 5%), critical at the critical threshold (default 0%); both thresholds are settings. State lives in printersupplyalerts so an alert fires once per crossing and re-arms after a refill. Recipients mirror the printedparts pattern: plugin-scoped shopdb users + roles + free-text emails (falling back to the site alert_recipients), and a chosen support team's webhook (falling back to the site alert_webhook_url). - PrinterSupplyAlert model + migration printers0002supplyalerts - alerttier(remaining, warning, critical) + check_supplies poller - flask printers check-toner-alerts CLI (run via scheduled task/cron) - printers alert settings + Low-Toner Alerts settings page - 7 tests: tier boundaries, once-per-crossing + re-arm, toner-only scope, custom thresholds, support-team webhook routing
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""printers: printersupplyalerts table (toner alert crossing state).
|
||||
|
||||
Stores the last tier alerted per (printer, supply) so the poller fires once
|
||||
on a downward crossing and re-arms after a refill. Idempotent create.
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'printers0002supplyalerts'
|
||||
down_revision = 'printers0001anchor'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
if 'printersupplyalerts' in inspector.get_table_names():
|
||||
return
|
||||
op.create_table(
|
||||
'printersupplyalerts',
|
||||
sa.Column('printersupplyalertid', sa.Integer(), primary_key=True),
|
||||
sa.Column('printerid', sa.Integer(), nullable=False),
|
||||
sa.Column('supplykey', sa.String(length=64), nullable=False),
|
||||
sa.Column('lasttier', sa.String(length=16), nullable=False,
|
||||
server_default='ok'),
|
||||
sa.Column('createddate', sa.DateTime(), nullable=False),
|
||||
sa.Column('modifieddate', sa.DateTime(), nullable=False),
|
||||
sa.Column('isactive', sa.Boolean(), nullable=False,
|
||||
server_default=sa.true()),
|
||||
sa.ForeignKeyConstraint(['printerid'], ['printers.printerid'],
|
||||
ondelete='CASCADE'),
|
||||
sa.UniqueConstraint('printerid', 'supplykey',
|
||||
name='uq_printersupplyalert_printer_key'),
|
||||
)
|
||||
op.create_index('ix_printersupplyalerts_printerid',
|
||||
'printersupplyalerts', ['printerid'])
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
if 'printersupplyalerts' not in inspector.get_table_names():
|
||||
return
|
||||
op.drop_table('printersupplyalerts')
|
||||
Reference in New Issue
Block a user