20260729_340_quality_issue_remediation.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """Add governed device-quality issue remediation lifecycle."""
  2. from alembic import op
  3. revision = "20260729_340"
  4. down_revision = "20260729_330"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. CREATE TABLE public.device_quality_issues (
  11. uid UUID PRIMARY KEY,
  12. issue_code VARCHAR(32) NOT NULL UNIQUE,
  13. source_violation_uid UUID NOT NULL,
  14. source_run_uid UUID NOT NULL
  15. REFERENCES public.device_quality_runs(uid) ON DELETE RESTRICT,
  16. rule_code VARCHAR(120) NOT NULL,
  17. severity VARCHAR(20) NOT NULL
  18. CHECK (severity IN ('info','warning','error','critical')),
  19. priority VARCHAR(20) NOT NULL
  20. CHECK (priority IN ('low','medium','high','critical')),
  21. asset_uid UUID NOT NULL
  22. REFERENCES public.device_assets(uid) ON DELETE RESTRICT,
  23. field_name VARCHAR(300) NOT NULL,
  24. source_uid UUID,
  25. source_mapping_uid UUID,
  26. message VARCHAR(1000) NOT NULL,
  27. evidence JSONB NOT NULL,
  28. recurrence_key CHAR(64) NOT NULL,
  29. occurrence_number INTEGER NOT NULL CHECK (occurrence_number > 0),
  30. status VARCHAR(30) NOT NULL DEFAULT 'open'
  31. CHECK (
  32. status IN (
  33. 'open','assigned','in_progress',
  34. 'pending_review','closed'
  35. )
  36. ),
  37. assignee_uid UUID
  38. REFERENCES public.users(id) ON DELETE RESTRICT,
  39. due_at TIMESTAMPTZ,
  40. current_version INTEGER NOT NULL DEFAULT 1
  41. CHECK (current_version > 0),
  42. created_by UUID NOT NULL
  43. REFERENCES public.users(id) ON DELETE RESTRICT,
  44. updated_by UUID NOT NULL
  45. REFERENCES public.users(id) ON DELETE RESTRICT,
  46. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  47. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  48. closed_at TIMESTAMPTZ
  49. );
  50. CREATE UNIQUE INDEX uq_device_quality_open_recurrence
  51. ON public.device_quality_issues(recurrence_key)
  52. WHERE status <> 'closed';
  53. CREATE INDEX idx_device_quality_issues_worklist
  54. ON public.device_quality_issues(
  55. status, priority, due_at, created_at DESC
  56. );
  57. CREATE INDEX idx_device_quality_issues_assignee
  58. ON public.device_quality_issues(
  59. assignee_uid, status, due_at
  60. );
  61. CREATE INDEX idx_device_quality_issues_recurrence
  62. ON public.device_quality_issues(
  63. recurrence_key, occurrence_number DESC
  64. );
  65. CREATE INDEX idx_device_quality_issues_source
  66. ON public.device_quality_issues(
  67. source_run_uid, source_violation_uid
  68. );
  69. CREATE TABLE public.device_quality_issue_remediations (
  70. uid UUID PRIMARY KEY,
  71. issue_uid UUID NOT NULL
  72. REFERENCES public.device_quality_issues(uid) ON DELETE CASCADE,
  73. round_number INTEGER NOT NULL CHECK (round_number > 0),
  74. summary VARCHAR(2000) NOT NULL,
  75. evidence_refs JSONB NOT NULL,
  76. submitted_by UUID NOT NULL
  77. REFERENCES public.users(id) ON DELETE RESTRICT,
  78. submitted_at TIMESTAMPTZ NOT NULL,
  79. review_status VARCHAR(20) NOT NULL DEFAULT 'pending'
  80. CHECK (review_status IN ('pending','passed','failed')),
  81. reviewed_by UUID
  82. REFERENCES public.users(id) ON DELETE RESTRICT,
  83. reviewed_at TIMESTAMPTZ,
  84. review_note VARCHAR(2000),
  85. verification_run_uid UUID
  86. REFERENCES public.device_quality_runs(uid) ON DELETE RESTRICT,
  87. UNIQUE (issue_uid, round_number)
  88. );
  89. CREATE INDEX idx_device_quality_issue_remediations
  90. ON public.device_quality_issue_remediations(
  91. issue_uid, round_number DESC
  92. );
  93. CREATE TABLE public.device_quality_issue_timeline (
  94. uid UUID PRIMARY KEY,
  95. issue_uid UUID NOT NULL
  96. REFERENCES public.device_quality_issues(uid) ON DELETE CASCADE,
  97. action VARCHAR(40) NOT NULL,
  98. from_status VARCHAR(30),
  99. to_status VARCHAR(30) NOT NULL
  100. CHECK (
  101. to_status IN (
  102. 'open','assigned','in_progress',
  103. 'pending_review','closed'
  104. )
  105. ),
  106. actor_uid UUID NOT NULL
  107. REFERENCES public.users(id) ON DELETE RESTRICT,
  108. note VARCHAR(2000),
  109. payload JSONB NOT NULL,
  110. created_at TIMESTAMPTZ NOT NULL
  111. );
  112. CREATE INDEX idx_device_quality_issue_timeline
  113. ON public.device_quality_issue_timeline(
  114. issue_uid, created_at, uid
  115. );
  116. """
  117. )
  118. def downgrade() -> None:
  119. # Remediation and review evidence is retained during application rollback.
  120. pass