"""phase18 — add reported_by column to issues table Background ---------- Issues created on the iPad by an inspector have no `assigned_to` value until a director assigns them via the web portal. The mobile API's list_issues endpoint filtered inspectors to `assigned_to == user.id`, so their own newly-submitted issues were invisible on the iPad until assigned. This migration adds `reported_by INT NULL FK → users.id` so the API can return issues the inspector either created OR was assigned to, without a join to the inspections table. The column is nullable for backward compatibility: existing issues created before this migration will have reported_by = NULL and continue to surface only via the assigned_to path. Uses INFORMATION_SCHEMA existence check — safe to re-run (CLAUDE.md rules 16, 17). Revision ID: phase18_issue_reported_by Revises: phase17_notification_event_type """ revision = 'phase18_issue_reported_by' down_revision = 'phase17_notification_event_type' branch_labels = None depends_on = None from alembic import op import sqlalchemy as sa def _column_exists(conn, table, column): result = conn.execute(sa.text( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS " "WHERE TABLE_SCHEMA = DATABASE() " "AND TABLE_NAME = :table AND COLUMN_NAME = :col" ), {"table": table, "col": column}) return result.scalar() > 0 def _fk_exists(conn, table, constraint_name): result = conn.execute(sa.text( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS " "WHERE TABLE_SCHEMA = DATABASE() " "AND TABLE_NAME = :table AND CONSTRAINT_NAME = :name" ), {"table": table, "name": constraint_name}) return result.scalar() > 0 def upgrade(): bind = op.get_bind() if not _column_exists(bind, 'issues', 'reported_by'): op.execute(sa.text( "ALTER TABLE issues " "ADD COLUMN reported_by INT NULL, " "ADD CONSTRAINT fk_issues_reported_by " " FOREIGN KEY (reported_by) REFERENCES users(id) " " ON DELETE SET NULL" )) def downgrade(): bind = op.get_bind() if _fk_exists(bind, 'issues', 'fk_issues_reported_by'): op.execute(sa.text( "ALTER TABLE issues DROP FOREIGN KEY fk_issues_reported_by" )) if _column_exists(bind, 'issues', 'reported_by'): op.execute(sa.text( "ALTER TABLE issues DROP COLUMN reported_by" ))