"""Add tickets.sla_breach_notified column; retire sla_notified_tickets setting Revision ID: 006_add_sla_breach_notified Revises: 005_add_watchers_and_time_entries Create Date: 2026-07-02 Rationale --------- SLA breach-notification suppression was previously tracked as a comma-separated list of ticket IDs in a single SystemSetting row (sla_notified_tickets). That works but doesn't scale cleanly and requires parsing a string on every 30-minute scheduler run. This migration replaces it with a plain boolean column on the ticket itself — the natural place for a per-ticket flag — and backfills it from the existing SystemSetting value so already-notified tickets don't get re-notified after the upgrade. Apply ----- flask db upgrade Rollback -------- flask db downgrade (Note: the backfilled sla_breach_notified flags are not restored back into a SystemSetting row on downgrade — if you roll back and then upgrade again, already-breached tickets will re-notify once.) """ from alembic import op import sqlalchemy as sa revision = '006_add_sla_breach_notified' down_revision = '005_add_watchers_and_time_entries' branch_labels = None depends_on = None def upgrade(): op.add_column( 'tickets', sa.Column('sla_breach_notified', sa.Boolean, nullable=False, server_default='0'), ) # Backfill from the old comma-separated SystemSetting, if present. conn = op.get_bind() row = conn.execute( sa.text("SELECT value FROM system_settings WHERE `key` = 'sla_notified_tickets'") ).fetchone() if row and row[0]: ticket_ids = [int(x) for x in row[0].split(',') if x.strip().isdigit()] if ticket_ids: conn.execute( sa.text( "UPDATE tickets SET sla_breach_notified = 1 WHERE id IN :ids" ).bindparams(sa.bindparam('ids', expanding=True)), {'ids': ticket_ids}, ) conn.execute(sa.text("DELETE FROM system_settings WHERE `key` = 'sla_notified_tickets'")) def downgrade(): op.drop_column('tickets', 'sla_breach_notified')