firefighter-incident 0.0.28__tar.gz → 0.0.30__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/PKG-INFO +1 -1
  2. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/_version.py +2 -2
  3. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/update_status.py +76 -28
  4. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/views.py +22 -6
  5. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/messages/slack_messages.py +96 -52
  6. firefighter_incident-0.0.30/firefighter_tests/test_incidents/test_forms/test_mitigated_reopening.py +193 -0
  7. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +2 -2
  8. firefighter_incident-0.0.30/firefighter_tests/test_slack/messages/test_postmortem_reminder.py +136 -0
  9. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/.gitignore +0 -0
  10. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/LICENSE +0 -0
  11. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/README.md +0 -0
  12. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/__init__.py +0 -0
  13. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/__init__.py +0 -0
  14. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/admin.py +0 -0
  15. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/apps.py +0 -0
  16. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/authentication.py +0 -0
  17. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/migrations/0001_initial.py +0 -0
  18. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/migrations/0002_alter_apitokenproxy_options.py +0 -0
  19. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/migrations/0003_alter_apitokenproxy_options.py +0 -0
  20. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/migrations/__init__.py +0 -0
  21. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/models.py +0 -0
  22. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/permissions.py +0 -0
  23. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/renderer.py +0 -0
  24. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/serializers.py +0 -0
  25. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/urls.py +0 -0
  26. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/__init__.py +0 -0
  27. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/_base.py +0 -0
  28. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/components.py +0 -0
  29. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/environments.py +0 -0
  30. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/groups.py +0 -0
  31. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/incident_cost_types.py +0 -0
  32. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/incident_costs.py +0 -0
  33. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/incidents.py +0 -0
  34. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/api/views/severities.py +0 -0
  35. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/__init__.py +0 -0
  36. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/avatar/__init__.py +0 -0
  37. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/avatar/avatar.html +0 -0
  38. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/avatar/avatar.py +0 -0
  39. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/card/__init__.py +0 -0
  40. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/card/card.html +0 -0
  41. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/card/card.py +0 -0
  42. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/export_button/__init__.py +0 -0
  43. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/export_button/export_button.html +0 -0
  44. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/export_button/export_button.py +0 -0
  45. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form/__init__.py +0 -0
  46. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form/form.html +0 -0
  47. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form/form.py +0 -0
  48. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form_field/__init__.py +0 -0
  49. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form_field/form_field.html +0 -0
  50. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/form_field/form_field.py +0 -0
  51. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/messages/__init__.py +0 -0
  52. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/messages/messages.html +0 -0
  53. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/messages/messages.py +0 -0
  54. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/modal/__init__.py +0 -0
  55. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/modal/modal.html +0 -0
  56. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/components/modal/modal.py +0 -0
  57. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/__init__.py +0 -0
  58. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/admin.py +0 -0
  59. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/apps.py +0 -0
  60. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/client.py +0 -0
  61. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/__init__.py +0 -0
  62. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/commands/__init__.py +0 -0
  63. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/commands/sort_postmortems.py +0 -0
  64. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/commands/sort_runbooks.py +0 -0
  65. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/commands/sync_postmortems.py +0 -0
  66. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/management/commands/sync_runbooks.py +0 -0
  67. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/migrations/0001_initial_oss.py +0 -0
  68. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/migrations/__init__.py +0 -0
  69. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/models.py +0 -0
  70. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/serializers.py +0 -0
  71. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/service.py +0 -0
  72. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/signals/__init__.py +0 -0
  73. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/signals/incident_updated.py +0 -0
  74. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tables.py +0 -0
  75. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/__init__.py +0 -0
  76. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/archive_postmortems.py +0 -0
  77. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/sort_runbooks.py +0 -0
  78. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/sync_pages_content.py +0 -0
  79. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/sync_postmortems.py +0 -0
  80. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/tasks/sync_runbooks.py +0 -0
  81. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/templates/oncall_team.xml +0 -0
  82. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/templates/pages/runbook_list.html +0 -0
  83. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/urls.py +0 -0
  84. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/utils.py +0 -0
  85. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/__init__.py +0 -0
  86. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/api.py +0 -0
  87. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/postmortem/__init__.py +0 -0
  88. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/postmortem/postmortem_detail.py +0 -0
  89. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/runbook/__init__.py +0 -0
  90. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/confluence/views/runbook/runbook_list.py +0 -0
  91. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/__init__.py +0 -0
  92. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/admin.py +0 -0
  93. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/apps.py +0 -0
  94. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/asgi.py +0 -0
  95. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/celery_client.py +0 -0
  96. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/fields_forms_widgets.py +0 -0
  97. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/filters.py +0 -0
  98. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/formats/__init__.py +0 -0
  99. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/formats/en/__init__.py +0 -0
  100. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/formats/en/formats.py +0 -0
  101. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/http_client.py +0 -0
  102. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/management/__init__.py +0 -0
  103. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/management/commands/__init__.py +0 -0
  104. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/management/commands/task.py +0 -0
  105. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/middleware.py +0 -0
  106. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/__init__.py +0 -0
  107. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/__init__.py +0 -0
  108. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/api.py +0 -0
  109. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/caches.py +0 -0
  110. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/celery.py +0 -0
  111. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/common.py +0 -0
  112. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/confluence.py +0 -0
  113. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/jira_app.py +0 -0
  114. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/logging.py +0 -0
  115. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/pagerduty.py +0 -0
  116. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/raid.py +0 -0
  117. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/components/slack.py +0 -0
  118. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/environments/__init__.py +0 -0
  119. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/environments/dev.py +0 -0
  120. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/environments/prod.py +0 -0
  121. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/settings_builder.py +0 -0
  122. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/settings/settings_utils.py +0 -0
  123. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/sso.py +0 -0
  124. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/tables_utils.py +0 -0
  125. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/templates/admin/base.html +0 -0
  126. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/templates/admin/login.html +0 -0
  127. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/templates/admin/send_message_conversation.html +0 -0
  128. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/templates/robots.txt +0 -0
  129. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/urls.py +0 -0
  130. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/utils.py +0 -0
  131. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/views.py +0 -0
  132. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/firefighter/wsgi.py +0 -0
  133. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/__init__.py +0 -0
  134. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/admin.py +0 -0
  135. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/apps.py +0 -0
  136. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/enums.py +0 -0
  137. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/factories.py +0 -0
  138. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/__init__.py +0 -0
  139. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/close_incident.py +0 -0
  140. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/closure_reason.py +0 -0
  141. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/create_incident.py +0 -0
  142. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/edit.py +0 -0
  143. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/select_impact.py +0 -0
  144. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/unified_incident.py +0 -0
  145. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/update_key_events.py +0 -0
  146. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/update_roles.py +0 -0
  147. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/forms/utils.py +0 -0
  148. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/management/__init__.py +0 -0
  149. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/management/commands/__init__.py +0 -0
  150. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/management/commands/backdate_incident_mitigated.py +0 -0
  151. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/management/commands/test_postmortem_reminders.py +0 -0
  152. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/menus.py +0 -0
  153. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0001_initial_oss.py +0 -0
  154. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0002_alter_severity_name_alter_user_password_featureteam.py +0 -0
  155. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0003_delete_featureteam.py +0 -0
  156. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0004_incidentupdate_environment.py +0 -0
  157. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0005_enable_from_p1_to_p5_priority.py +0 -0
  158. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0006_update_group_names.py +0 -0
  159. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0007_update_component_name.py +0 -0
  160. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0008_impact_level.py +0 -0
  161. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0009_update_sla.py +0 -0
  162. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0010_update_components.py +0 -0
  163. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0011_update_incidents.py +0 -0
  164. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0012_alter_impactlevel.py +0 -0
  165. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0013_add_missing_component.py +0 -0
  166. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0014_update_components_slack_groups.py +0 -0
  167. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0015_update_impact_level.py +0 -0
  168. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0016_update_business_incidents_and_level.py +0 -0
  169. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0017_reorder_impact_types.py +0 -0
  170. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0018_update_impactlevel_names.py +0 -0
  171. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0019_set_security_components_private.py +0 -0
  172. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0020_create_incident_category_model.py +0 -0
  173. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py +0 -0
  174. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0022_add_incident_category_fields.py +0 -0
  175. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0023_populate_incident_category_references.py +0 -0
  176. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0024_remove_component_fields_and_model.py +0 -0
  177. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0025_make_incident_category_required.py +0 -0
  178. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0026_alter_incidentcategory_options_and_more.py +0 -0
  179. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0027_add_closure_fields.py +0 -0
  180. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0028_add_closure_reason_constraint.py +0 -0
  181. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0029_add_custom_fields_to_incident.py +0 -0
  182. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/0030_add_mitigated_at_field.py +0 -0
  183. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/migrations/__init__.py +0 -0
  184. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/__init__.py +0 -0
  185. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/environment.py +0 -0
  186. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/group.py +0 -0
  187. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/impact.py +0 -0
  188. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident.py +0 -0
  189. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_category.py +0 -0
  190. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_cost.py +0 -0
  191. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_cost_type.py +0 -0
  192. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_membership.py +0 -0
  193. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_role_type.py +0 -0
  194. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/incident_update.py +0 -0
  195. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/metric_type.py +0 -0
  196. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/milestone_type.py +0 -0
  197. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/priority.py +0 -0
  198. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/severity.py +0 -0
  199. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/models/user.py +0 -0
  200. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/signals.py +0 -0
  201. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/css/incident.css +0 -0
  202. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/css/main.css +0 -0
  203. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/css/main.min.css +0 -0
  204. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/css/tailwind.css +0 -0
  205. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/android-chrome-192x192.png +0 -0
  206. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/android-chrome-512x512.png +0 -0
  207. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/apple-touch-icon.png +0 -0
  208. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/favicon-16x16.png +0 -0
  209. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/favicon-32x32.png +0 -0
  210. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/favicon.ico +0 -0
  211. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/favicon/site.webmanifest +0 -0
  212. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/gameday.png +0 -0
  213. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/logo-firefighter.png +0 -0
  214. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/p1.png +0 -0
  215. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/p2.png +0 -0
  216. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/p3.png +0 -0
  217. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/p4.png +0 -0
  218. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/img/p5.png +0 -0
  219. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/js/main.js +0 -0
  220. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/static/js/main.min.js +0 -0
  221. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/tables.py +0 -0
  222. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/tasks/__init__.py +0 -0
  223. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/tasks/updateoncall.py +0 -0
  224. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/errors/base.html +0 -0
  225. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/filter.html +0 -0
  226. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/table/priority_column.html +0 -0
  227. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/table/status_column.html +0 -0
  228. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/table.html +0 -0
  229. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/widgets/form_container.html +0 -0
  230. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/widgets/grouped_checkbox_nested.html +0 -0
  231. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/incidents/widgets/input_option.html +0 -0
  232. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/index.html +0 -0
  233. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/created_at_help.html +0 -0
  234. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/environment_pill.html +0 -0
  235. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/footer.html +0 -0
  236. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/header.html +0 -0
  237. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/incident_card.html +0 -0
  238. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/incident_metrics.html +0 -0
  239. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/incident_timeline.html +0 -0
  240. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/incident_update_key_events_view.html +0 -0
  241. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/incident_update_key_events_view_modal.html +0 -0
  242. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/partial_table_list_paginated.html +0 -0
  243. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/priority_icon.html +0 -0
  244. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/priority_pill.html +0 -0
  245. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/status_pill.html +0 -0
  246. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/table.html +0 -0
  247. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/user_card.html +0 -0
  248. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/partials/user_tooltip.html +0 -0
  249. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/layouts/view_filters.html +0 -0
  250. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/dashboard.html +0 -0
  251. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/docs_metrics.html +0 -0
  252. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_category_detail.html +0 -0
  253. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_category_list.html +0 -0
  254. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_create.html +0 -0
  255. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_detail.html +0 -0
  256. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_list.html +0 -0
  257. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_role_types_detail.html +0 -0
  258. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_role_types_list.html +0 -0
  259. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_statistics.html +0 -0
  260. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_statistics_partial.html +0 -0
  261. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/incident_update_key_events_form.html +0 -0
  262. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/templates/pages/user_detail.html +0 -0
  263. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/urls.py +0 -0
  264. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/__init__.py +0 -0
  265. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/components/__init__.py +0 -0
  266. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/components/details.py +0 -0
  267. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/components/list.py +0 -0
  268. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/date_filter.py +0 -0
  269. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/date_utils.py +0 -0
  270. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/docs/__init__.py +0 -0
  271. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/docs/metrics.py +0 -0
  272. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/docs/role_types.py +0 -0
  273. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/errors.py +0 -0
  274. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/reports.py +0 -0
  275. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/users/__init__.py +0 -0
  276. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/incidents/views/users/details.py +0 -0
  277. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/__init__.py +0 -0
  278. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/admin.py +0 -0
  279. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/apps.py +0 -0
  280. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/client.py +0 -0
  281. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/management/__init__.py +0 -0
  282. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/management/commands/__init__.py +0 -0
  283. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/migrations/0001_initial_oss.py +0 -0
  284. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/migrations/0002_add_jira_postmortem_model.py +0 -0
  285. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/migrations/__init__.py +0 -0
  286. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/models.py +0 -0
  287. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/service_postmortem.py +0 -0
  288. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/signals/__init__.py +0 -0
  289. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/signals/incident_key_events_updated.py +0 -0
  290. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/signals/postmortem_created.py +0 -0
  291. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/tasks/__init__.py +0 -0
  292. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/tasks/sync_users_jira.py +0 -0
  293. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/templates/jira/postmortem/impact.txt +0 -0
  294. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/templates/jira/postmortem/incident_summary.txt +0 -0
  295. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/templates/jira/postmortem/mitigation_actions.txt +0 -0
  296. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/templates/jira/postmortem/root_causes.txt +0 -0
  297. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/templates/jira/postmortem/timeline.txt +0 -0
  298. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/types.py +0 -0
  299. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/jira_app/utils.py +0 -0
  300. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/logging/__init__.py +0 -0
  301. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/logging/custom_json_formatter.py +0 -0
  302. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/logging/pretty_formatter.py +0 -0
  303. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/__init__.py +0 -0
  304. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/admin.py +0 -0
  305. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/apps.py +0 -0
  306. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/client.py +0 -0
  307. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/forms/__init__.py +0 -0
  308. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/forms/create_pagerduty_incident.py +0 -0
  309. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/migrations/0001_initial_oss.py +0 -0
  310. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/migrations/__init__.py +0 -0
  311. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/models.py +0 -0
  312. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/service.py +0 -0
  313. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/signals/__init__.py +0 -0
  314. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/signals/get_invites_from_pagerduty.py +0 -0
  315. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/signals/incident_channel_done_oncall.py +0 -0
  316. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/tasks/__init__.py +0 -0
  317. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/tasks/fetch_oncall.py +0 -0
  318. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/tasks/fetch_services.py +0 -0
  319. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/tasks/fetch_users.py +0 -0
  320. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/tasks/trigger_oncall.py +0 -0
  321. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/templates/pages/oncall_list.html +0 -0
  322. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/templates/pages/oncall_trigger.html +0 -0
  323. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/templates/partials/trigger_oncall_form_view.html +0 -0
  324. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/templates/partials/trigger_oncall_form_view_modal.html +0 -0
  325. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/urls.py +0 -0
  326. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/views/__init__.py +0 -0
  327. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/views/oncall_list.py +0 -0
  328. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/pagerduty/views/oncall_trigger.py +0 -0
  329. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/__init__.py +0 -0
  330. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/admin.py +0 -0
  331. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/apps.py +0 -0
  332. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/client.py +0 -0
  333. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/forms.py +0 -0
  334. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/messages.py +0 -0
  335. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/migrations/0001_initial_oss.py +0 -0
  336. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/migrations/0002_featureteam_remove_qualifierrotation_jira_user_and_more.py +0 -0
  337. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/migrations/0003_delete_raidarea.py +0 -0
  338. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/migrations/__init__.py +0 -0
  339. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/models.py +0 -0
  340. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/resources.py +0 -0
  341. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/serializers.py +0 -0
  342. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/service.py +0 -0
  343. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/signals/__init__.py +0 -0
  344. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/signals/incident_updated.py +0 -0
  345. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/tasks/__init__.py +0 -0
  346. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/types.py +0 -0
  347. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/urls.py +0 -0
  348. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/utils.py +0 -0
  349. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/raid/views/__init__.py +0 -0
  350. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/__init__.py +0 -0
  351. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/admin.py +0 -0
  352. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/apps.py +0 -0
  353. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/factories.py +0 -0
  354. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/forms/__init__.py +0 -0
  355. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/forms/sos_form.py +0 -0
  356. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/management/__init__.py +0 -0
  357. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/management/commands/__init__.py +0 -0
  358. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/management/commands/generate_manifest.py +0 -0
  359. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/management/commands/switch_test_users.py +0 -0
  360. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/messages/__init__.py +0 -0
  361. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/messages/base.py +0 -0
  362. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0001_initial_oss.py +0 -0
  363. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0002_usergroup_tag.py +0 -0
  364. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0003_alter_usergroup_tag.py +0 -0
  365. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0004_alter_usergroup_components.py +0 -0
  366. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0005_add_incident_categories_fields.py +0 -0
  367. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0006_copy_components_to_incident_categories.py +0 -0
  368. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0007_remove_components_fields.py +0 -0
  369. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0008_alter_conversation_incident_categories_and_more.py +0 -0
  370. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/0009_add_postmortem_reminder_periodic_task.py +0 -0
  371. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/migrations/__init__.py +0 -0
  372. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/__init__.py +0 -0
  373. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/conversation.py +0 -0
  374. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/incident_channel.py +0 -0
  375. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/message.py +0 -0
  376. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/sos.py +0 -0
  377. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/user.py +0 -0
  378. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/models/user_group.py +0 -0
  379. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/rules.py +0 -0
  380. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/__init__.py +0 -0
  381. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/create_incident_conversation.py +0 -0
  382. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/get_users.py +0 -0
  383. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/handle_incident_channel_done.py +0 -0
  384. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/incident_closed.py +0 -0
  385. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/incident_updated.py +0 -0
  386. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/postmortem_created.py +0 -0
  387. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/signals/roles_reminders.py +0 -0
  388. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/slack_app.py +0 -0
  389. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/slack_incident_context.py +0 -0
  390. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/slack_templating.py +0 -0
  391. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/__init__.py +0 -0
  392. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/fetch_conversations_members.py +0 -0
  393. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/reminder_postmortem.py +0 -0
  394. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/send_message.py +0 -0
  395. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/send_postmortem_reminders.py +0 -0
  396. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/send_reminders.py +0 -0
  397. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/sync_users.py +0 -0
  398. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/update_usergroups_members.py +0 -0
  399. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/tasks/update_users.py +0 -0
  400. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/urls.py +0 -0
  401. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/utils.py +0 -0
  402. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/__init__.py +0 -0
  403. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/__init__.py +0 -0
  404. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/actions_and_shortcuts.py +0 -0
  405. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_archive.py +0 -0
  406. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_id_changed.py +0 -0
  407. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_rename.py +0 -0
  408. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_shared.py +0 -0
  409. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_unarchive.py +0 -0
  410. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/channel_unshared.py +0 -0
  411. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/commands.py +0 -0
  412. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/home.py +0 -0
  413. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/member_joined_channel.py +0 -0
  414. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/member_left_channel.py +0 -0
  415. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/message.py +0 -0
  416. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/message_deleted.py +0 -0
  417. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/events/reaction_added.py +0 -0
  418. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/__init__.py +0 -0
  419. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/__init__.py +0 -0
  420. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/base.py +0 -0
  421. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/base_mixins.py +0 -0
  422. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/form_utils.py +0 -0
  423. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/mixins.py +0 -0
  424. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/base_modal/modal_utils.py +0 -0
  425. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/close.py +0 -0
  426. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/closure_reason.py +0 -0
  427. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/downgrade_workflow.py +0 -0
  428. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/edit.py +0 -0
  429. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/key_event_message.py +0 -0
  430. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/open.py +0 -0
  431. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/__init__.py +0 -0
  432. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/check_current_incidents.py +0 -0
  433. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/details/__init__.py +0 -0
  434. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/details/unified.py +0 -0
  435. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/select_impact.py +0 -0
  436. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/set_details.py +0 -0
  437. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/opening/types.py +0 -0
  438. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/postmortem.py +0 -0
  439. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/select.py +0 -0
  440. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/send_sos.py +0 -0
  441. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/status.py +0 -0
  442. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/trigger_oncall.py +0 -0
  443. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/update.py +0 -0
  444. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/update_roles.py +0 -0
  445. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/update_status.py +0 -0
  446. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/modals/utils.py +0 -0
  447. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter/slack/views/views.py +0 -0
  448. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/environments.json +0 -0
  449. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/groups.json +0 -0
  450. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/impact_level.json +0 -0
  451. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/impact_type.json +0 -0
  452. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/incident_categories.json +0 -0
  453. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/incident_role_type.json +0 -0
  454. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/metric_type.json +0 -0
  455. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/milestone_type.json +0 -0
  456. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/priorities.json +0 -0
  457. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_fixtures/incidents/severities.json +0 -0
  458. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/__init__.py +0 -0
  459. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/conftest.py +0 -0
  460. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_api/test_api_landbot.py +0 -0
  461. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_api/test_api_urls.py +0 -0
  462. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_api/test_renderer.py +0 -0
  463. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_confluence/test_confluence_utils.py +0 -0
  464. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_firefighter/test_firefighter_utils.py +0 -0
  465. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_firefighter/test_logging.py +0 -0
  466. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_firefighter/test_sso.py +0 -0
  467. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_firefighter/test_urls.py +0 -0
  468. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_enums.py +0 -0
  469. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/conftest.py +0 -0
  470. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_closure_reason.py +0 -0
  471. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_form_select_impact.py +0 -0
  472. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_form_utils.py +0 -0
  473. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_unified_incident_form.py +0 -0
  474. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +0 -0
  475. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +0 -0
  476. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_update_key_events.py +0 -0
  477. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +0 -0
  478. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_incident_urls.py +0 -0
  479. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_models/test_incident_category.py +0 -0
  480. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_models/test_incident_model.py +0 -0
  481. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_models/test_migrations/test_incident_migrations.py +0 -0
  482. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_utils/test_date_utils.py +0 -0
  483. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_views/test_incident_detail_view.py +0 -0
  484. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_incidents/test_views/test_index_view.py +0 -0
  485. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/__init__.py +0 -0
  486. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/conftest.py +0 -0
  487. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_incident_key_events_sync.py +0 -0
  488. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_jira_client_watchers.py +0 -0
  489. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_models.py +0 -0
  490. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_postmortem_issue_link.py +0 -0
  491. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_postmortem_service.py +0 -0
  492. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_jira_app/test_timeline_template.py +0 -0
  493. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/conftest.py +0 -0
  494. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_alert_p4_p5.py +0 -0
  495. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_client.py +0 -0
  496. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_client_users.py +0 -0
  497. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_forms.py +0 -0
  498. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_models.py +0 -0
  499. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_serializers.py +0 -0
  500. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_service.py +0 -0
  501. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_signals.py +0 -0
  502. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_transitions.py +0 -0
  503. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_utils.py +0 -0
  504. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_raid_views.py +0 -0
  505. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_raid/test_zendesk_integration.py +0 -0
  506. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/conftest.py +0 -0
  507. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/messages/__init__.py +0 -0
  508. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/messages/test_slack_messages.py +0 -0
  509. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_conversation_tags.py +0 -0
  510. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_models/test_conversations.py +0 -0
  511. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_models/test_incident_channel.py +0 -0
  512. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_models/test_slack_user.py +0 -0
  513. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_signals_downgrade.py +0 -0
  514. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/test_slack_utils.py +0 -0
  515. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/conftest.py +0 -0
  516. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_close.py +0 -0
  517. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +0 -0
  518. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_edit.py +0 -0
  519. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py +0 -0
  520. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_key_event_message.py +0 -0
  521. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_open.py +0 -0
  522. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_opening_unified.py +0 -0
  523. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_postmortem_modal.py +0 -0
  524. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_send_sos.py +0 -0
  525. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_status.py +0 -0
  526. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_update_status.py +0 -0
  527. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/firefighter_tests/test_slack/views/modals/test_utils.py +0 -0
  528. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/gunicorn.conf.py +0 -0
  529. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/main.py +0 -0
  530. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/manage.py +0 -0
  531. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/package-lock.json +0 -0
  532. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/package.json +0 -0
  533. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/pyproject.toml +0 -0
  534. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/scripts/gen_credits.py +0 -0
  535. {firefighter_incident-0.0.28 → firefighter_incident-0.0.30}/scripts/hatch_build.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firefighter-incident
3
- Version: 0.0.28
3
+ Version: 0.0.30
4
4
  Summary: Incident Management tool made for Slack using Django
5
5
  Project-URL: Repository, https://github.com/ManoManoTech/firefighter-incident
6
6
  Project-URL: Documentation, https://manomanotech.github.io/firefighter-incident/latest/
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.28'
32
- __version_tuple__ = version_tuple = (0, 0, 28)
31
+ __version__ = version = '0.0.30'
32
+ __version_tuple__ = version_tuple = (0, 0, 30)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -43,9 +43,14 @@ class UpdateStatusForm(forms.Form):
43
43
  ),
44
44
  )
45
45
 
46
- def __init__(self, *args: Any, incident: Incident | None = None, **kwargs: Any) -> None:
46
+ def __init__(
47
+ self, *args: Any, incident: Incident | None = None, **kwargs: Any
48
+ ) -> None:
47
49
  super().__init__(*args, **kwargs)
48
50
 
51
+ # Store incident for later use in clean()
52
+ self.incident = incident
53
+
49
54
  # Dynamically adjust status choices based on incident requirements
50
55
  if incident:
51
56
  self._set_status_choices(incident)
@@ -62,8 +67,9 @@ class UpdateStatusForm(forms.Form):
62
67
  and incident.priority.needs_postmortem
63
68
  and incident.environment.value == "PRD"
64
69
  )
65
-
66
- allowed_statuses = self._get_allowed_statuses(current_status, requires_postmortem=requires_postmortem)
70
+ allowed_statuses = self._get_allowed_statuses(
71
+ current_status, requires_postmortem=requires_postmortem
72
+ )
67
73
 
68
74
  # If we got a list of enum values, convert to choices and include current status
69
75
  if allowed_statuses:
@@ -72,10 +78,6 @@ class UpdateStatusForm(forms.Form):
72
78
  # Convert values to strings to match what Slack sends in form submissions
73
79
  choices = [(str(s.value), s.label) for s in allowed_statuses]
74
80
  status_field.choices = choices # type: ignore[attr-defined]
75
- logger.debug(
76
- f"Set status choices for incident #{incident.id}: {choices} "
77
- f"(current_status={current_status}, requires_postmortem={requires_postmortem})"
78
- )
79
81
 
80
82
  def _get_allowed_statuses(
81
83
  self, current_status: IncidentStatus, *, requires_postmortem: bool
@@ -84,19 +86,17 @@ class UpdateStatusForm(forms.Form):
84
86
 
85
87
  Returns None if choices should be set directly (for default fallback cases).
86
88
  """
87
- status_field = self.fields["status"]
88
-
89
89
  # For incidents requiring post-mortem (P1/P2 in PRD)
90
90
  if requires_postmortem:
91
- return self._get_postmortem_allowed_statuses(current_status, status_field)
91
+ return self._get_postmortem_allowed_statuses(current_status)
92
92
 
93
93
  # For P3+ incidents (no post-mortem needed)
94
- return self._get_no_postmortem_allowed_statuses(current_status, status_field)
94
+ return self._get_no_postmortem_allowed_statuses(current_status)
95
95
 
96
96
  def _get_postmortem_allowed_statuses(
97
- self, current_status: IncidentStatus, status_field: Any
97
+ self, current_status: IncidentStatus
98
98
  ) -> list[IncidentStatus] | None:
99
- """Get allowed statuses for incidents requiring postmortem."""
99
+ """Get allowed statuses for incidents requiring postmortem (P1/P2)."""
100
100
  if current_status == IncidentStatus.OPEN:
101
101
  return [IncidentStatus.INVESTIGATING, IncidentStatus.CLOSED]
102
102
  if current_status == IncidentStatus.INVESTIGATING:
@@ -104,18 +104,22 @@ class UpdateStatusForm(forms.Form):
104
104
  if current_status == IncidentStatus.MITIGATING:
105
105
  return [IncidentStatus.MITIGATED]
106
106
  if current_status == IncidentStatus.MITIGATED:
107
- return [IncidentStatus.POST_MORTEM]
107
+ # P1/P2 can: go to POST_MORTEM (required) OR reopen to INVESTIGATING/MITIGATING (with reason)
108
+ return [
109
+ IncidentStatus.POST_MORTEM, # Required next step
110
+ IncidentStatus.INVESTIGATING, # Reopen option (with reason)
111
+ IncidentStatus.MITIGATING, # Reopen option (with reason)
112
+ ]
108
113
  if current_status == IncidentStatus.POST_MORTEM:
109
114
  return [IncidentStatus.CLOSED]
110
115
 
111
- # Default: all statuses up to closed
112
- self._set_default_choices(status_field, current_status, IncidentStatus.choices_lte(IncidentStatus.CLOSED))
116
+ # For any other status, return None to use the default choices
113
117
  return None
114
118
 
115
119
  def _get_no_postmortem_allowed_statuses(
116
- self, current_status: IncidentStatus, status_field: Any
120
+ self, current_status: IncidentStatus
117
121
  ) -> list[IncidentStatus] | None:
118
- """Get allowed statuses for incidents not requiring postmortem."""
122
+ """Get allowed statuses for incidents not requiring postmortem (P3/P4/P5)."""
119
123
  if current_status == IncidentStatus.OPEN:
120
124
  return [IncidentStatus.INVESTIGATING, IncidentStatus.CLOSED]
121
125
  if current_status == IncidentStatus.INVESTIGATING:
@@ -123,12 +127,14 @@ class UpdateStatusForm(forms.Form):
123
127
  if current_status == IncidentStatus.MITIGATING:
124
128
  return [IncidentStatus.MITIGATED]
125
129
  if current_status == IncidentStatus.MITIGATED:
126
- return [IncidentStatus.CLOSED]
127
-
128
- # Default fallback
129
- self._set_default_choices(
130
- status_field, current_status, IncidentStatus.choices_lte_skip_postmortem(IncidentStatus.CLOSED)
131
- )
130
+ # P3/P4/P5 can: go to CLOSED (normal next step) OR reopen to INVESTIGATING/MITIGATING (with reason)
131
+ return [
132
+ IncidentStatus.CLOSED, # Normal next step for P3+
133
+ IncidentStatus.INVESTIGATING, # Reopen option (with reason)
134
+ IncidentStatus.MITIGATING, # Reopen option (with reason)
135
+ ]
136
+
137
+ # For any other status, return None to use the default choices
132
138
  return None
133
139
 
134
140
  def _set_default_choices(
@@ -136,14 +142,21 @@ class UpdateStatusForm(forms.Form):
136
142
  ) -> None:
137
143
  """Set status field choices to default, ensuring current status is included."""
138
144
  # Convert default_choices to string keys to match Slack form submissions
139
- status_field.choices = [(str(choice[0]), choice[1]) for choice in default_choices]
145
+ status_field.choices = [
146
+ (str(choice[0]), choice[1]) for choice in default_choices
147
+ ]
140
148
  existing_values = {choice[0] for choice in status_field.choices}
141
149
  if str(current_status.value) not in existing_values:
142
150
  # Insert current status at the beginning
143
- status_field.choices = [(str(current_status.value), current_status.label), *status_field.choices]
151
+ status_field.choices = [
152
+ (str(current_status.value), current_status.label),
153
+ *status_field.choices,
154
+ ]
144
155
 
145
156
  @staticmethod
146
- def requires_closure_reason(incident: Incident, target_status: IncidentStatus) -> bool:
157
+ def requires_closure_reason(
158
+ incident: Incident, target_status: IncidentStatus
159
+ ) -> bool:
147
160
  """Check if closing this incident to the target status requires a closure reason.
148
161
 
149
162
  Based on the workflow diagram:
@@ -155,4 +168,39 @@ class UpdateStatusForm(forms.Form):
155
168
  current_status = incident.status
156
169
 
157
170
  # Require reason if closing from Opened or Investigating (for any priority)
158
- return current_status.value in {IncidentStatus.OPEN, IncidentStatus.INVESTIGATING}
171
+ return current_status.value in {
172
+ IncidentStatus.OPEN,
173
+ IncidentStatus.INVESTIGATING,
174
+ }
175
+
176
+ def clean_message(self) -> str:
177
+ """Validate message field, ensuring reopening from MITIGATED has sufficient justification."""
178
+ message = self.cleaned_data.get("message", "").strip()
179
+
180
+ # Get the incident and status from form data/initialization
181
+ incident = getattr(self, "incident", None)
182
+ status_value = self.data.get("status") # Use raw form data as cleaned_data may not be ready yet
183
+
184
+ if incident and status_value:
185
+ current_status = incident.status
186
+
187
+ # Convert status value to enum if it's a string/number
188
+ try:
189
+ if isinstance(status_value, str):
190
+ status = IncidentStatus(int(status_value))
191
+ else:
192
+ status = IncidentStatus(status_value)
193
+ except (ValueError, TypeError):
194
+ # Invalid status value, let other validation handle it
195
+ return message
196
+
197
+ # If reopening from MITIGATED to earlier phases, require substantial justification
198
+ if (current_status == IncidentStatus.MITIGATED and
199
+ status in {IncidentStatus.INVESTIGATING, IncidentStatus.MITIGATING} and
200
+ len(message) < 10):
201
+ raise forms.ValidationError(
202
+ "A detailed justification (minimum 10 characters) is required when reopening "
203
+ "an incident from MITIGATED status."
204
+ )
205
+
206
+ return message
@@ -82,7 +82,9 @@ class IncidentStatisticsView(FilterView):
82
82
  filterset_class = IncidentFilterSet
83
83
  model = Incident
84
84
  queryset = (
85
- Incident.objects.select_related("priority", "incident_category__group", "environment")
85
+ Incident.objects.select_related(
86
+ "priority", "incident_category__group", "environment"
87
+ )
86
88
  .all()
87
89
  .order_by("-id")
88
90
  )
@@ -123,7 +125,9 @@ class DashboardView(generic.ListView[Incident]):
123
125
  )
124
126
  queryset = (
125
127
  Incident.objects.filter(_status__lt=IncidentStatus.CLOSED)
126
- .select_related("priority", "incident_category__group", "environment", "created_by")
128
+ .select_related(
129
+ "priority", "incident_category__group", "environment", "created_by"
130
+ )
127
131
  .order_by("_status", "priority__value")
128
132
  .annotate(latest_event_ts=Subquery(sub.values("event_ts")))
129
133
  )
@@ -148,10 +152,22 @@ class IncidentDetailView(CustomDetailView[Incident]):
148
152
  "conversation",
149
153
  "created_by",
150
154
  ]
151
- if settings.ENABLE_CONFLUENCE:
152
- select_related.append("postmortem_for")
153
- if getattr(settings, "ENABLE_JIRA_POSTMORTEM", False):
154
- select_related.append("jira_postmortem_for")
155
+
156
+ # Always load post-mortem relationships to display existing data
157
+ # even if creation is disabled
158
+ try:
159
+ # Only add if confluence app is installed
160
+ if "firefighter.confluence" in settings.INSTALLED_APPS:
161
+ select_related.append("postmortem_for")
162
+ except ImportError:
163
+ pass
164
+
165
+ try:
166
+ # Only add if jira_app is installed
167
+ if "firefighter.jira_app" in settings.INSTALLED_APPS:
168
+ select_related.append("jira_postmortem_for")
169
+ except ImportError:
170
+ pass
155
171
  queryset = Incident.objects.select_related(*select_related).prefetch_related(
156
172
  Prefetch(
157
173
  "incidentupdate_set",
@@ -124,7 +124,10 @@ class SlackMessageIncidentPostMortemReminder(SlackMessageSurface):
124
124
  ),
125
125
  )
126
126
  )
127
- elif hasattr(self.incident, "jira_postmortem_for") and self.incident.jira_postmortem_for:
127
+ elif (
128
+ hasattr(self.incident, "jira_postmortem_for")
129
+ and self.incident.jira_postmortem_for
130
+ ):
128
131
  jira_pm = self.incident.jira_postmortem_for
129
132
  blocks.append(
130
133
  SectionBlock(
@@ -138,28 +141,30 @@ class SlackMessageIncidentPostMortemReminder(SlackMessageSurface):
138
141
  )
139
142
 
140
143
  # Continue with remaining steps
141
- blocks.extend([
142
- SectionBlock(
143
- text=f"3. Submit the key events to {APP_DISPLAY_NAME}",
144
- **accessory_kwargs,
145
- ),
146
- SectionBlock(
147
- text="4. Once everything has been submitted, close the incident",
148
- accessory=ButtonElement(
149
- text="Close incident",
150
- value=str(self.incident.id),
151
- action_id=CloseModal.open_action,
144
+ blocks.extend(
145
+ [
146
+ SectionBlock(
147
+ text=f"3. Submit the key events to {APP_DISPLAY_NAME}",
148
+ **accessory_kwargs,
152
149
  ),
153
- ),
154
- DividerBlock(),
155
- ContextBlock(
156
- elements=[
157
- MarkdownTextObject(
158
- text=":bulb: Updating the status and creating a post-mortem is crucial for the Platform Operations Report presented during the Tech Weekly."
159
- )
160
- ]
161
- ),
162
- ])
150
+ SectionBlock(
151
+ text="4. Once everything has been submitted, close the incident",
152
+ accessory=ButtonElement(
153
+ text="Close incident",
154
+ value=str(self.incident.id),
155
+ action_id=CloseModal.open_action,
156
+ ),
157
+ ),
158
+ DividerBlock(),
159
+ ContextBlock(
160
+ elements=[
161
+ MarkdownTextObject(
162
+ text=":bulb: Updating the status and creating a post-mortem is crucial for the Platform Operations Report presented during the Tech Weekly."
163
+ )
164
+ ]
165
+ ),
166
+ ]
167
+ )
163
168
 
164
169
  if POSTMORTEM_HELP_URL:
165
170
  blocks.insert(
@@ -223,11 +228,7 @@ class SlackMessageIncidentFixedNextActions(SlackMessageSurface):
223
228
  postmortem_text += " on Jira."
224
229
 
225
230
  blocks.append(
226
- ContextBlock(
227
- elements=[
228
- MarkdownTextObject(text=postmortem_text)
229
- ]
230
- )
231
+ ContextBlock(elements=[MarkdownTextObject(text=postmortem_text)])
231
232
  )
232
233
 
233
234
  return blocks
@@ -261,11 +262,17 @@ class SlackMessageIncidentDeclaredAnnouncement(SlackMessageSurface):
261
262
  if hasattr(self.incident, "custom_fields") and self.incident.custom_fields:
262
263
  custom_fields = self.incident.custom_fields
263
264
  if custom_fields.get("zendesk_ticket_id"):
264
- fields.append(f":ticket: *Zendesk Ticket:* {custom_fields['zendesk_ticket_id']}")
265
+ fields.append(
266
+ f":ticket: *Zendesk Ticket:* {custom_fields['zendesk_ticket_id']}"
267
+ )
265
268
  if custom_fields.get("seller_contract_id"):
266
- fields.append(f":memo: *Seller Contract:* {custom_fields['seller_contract_id']}")
269
+ fields.append(
270
+ f":memo: *Seller Contract:* {custom_fields['seller_contract_id']}"
271
+ )
267
272
  if custom_fields.get("zoho_desk_ticket_id"):
268
- fields.append(f":ticket: *Zoho Desk Ticket:* {custom_fields['zoho_desk_ticket_id']}")
273
+ fields.append(
274
+ f":ticket: *Zoho Desk Ticket:* {custom_fields['zoho_desk_ticket_id']}"
275
+ )
269
276
  if custom_fields.get("is_key_account") is True:
270
277
  fields.append(":star: *Key Account*")
271
278
  if custom_fields.get("is_seller_in_golden_list") is True:
@@ -296,10 +303,17 @@ class SlackMessageIncidentDeclaredAnnouncement(SlackMessageSurface):
296
303
  pm_fields = []
297
304
 
298
305
  if hasattr(self.incident, "postmortem_for") and self.incident.postmortem_for:
299
- pm_fields.append(f":confluence: <{self.incident.postmortem_for.page_url}|*Confluence Post-mortem*>")
306
+ pm_fields.append(
307
+ f":confluence: <{self.incident.postmortem_for.page_url}|*Confluence Post-mortem*>"
308
+ )
300
309
 
301
- if hasattr(self.incident, "jira_postmortem_for") and self.incident.jira_postmortem_for:
302
- pm_fields.append(f":jira_new: <{self.incident.jira_postmortem_for.issue_url}|*Jira Post-mortem ({self.incident.jira_postmortem_for.jira_issue_key})*>")
310
+ if (
311
+ hasattr(self.incident, "jira_postmortem_for")
312
+ and self.incident.jira_postmortem_for
313
+ ):
314
+ pm_fields.append(
315
+ f":jira_new: <{self.incident.jira_postmortem_for.issue_url}|*Jira Post-mortem ({self.incident.jira_postmortem_for.jira_issue_key})*>"
316
+ )
303
317
 
304
318
  if not pm_fields:
305
319
  return []
@@ -582,7 +596,8 @@ class SlackMessageIncidentStatusUpdated(SlackMessageSurface):
582
596
  value=str(self.incident.id),
583
597
  action_id=UpdateStatusModal.open_action,
584
598
  )
585
- if self.in_channel and self.incident.status != IncidentStatus.CLOSED
599
+ if self.in_channel
600
+ and self.incident.status != IncidentStatus.CLOSED
586
601
  else None
587
602
  ),
588
603
  ),
@@ -600,6 +615,32 @@ class SlackMessageIncidentStatusUpdated(SlackMessageSurface):
600
615
  ]
601
616
  )
602
617
  )
618
+
619
+ # Add post mortem reminder if incident is back to MITIGATED and a post mortem already exists
620
+ if (self.incident.status == IncidentStatus.MITIGATED
621
+ and hasattr(self.incident, "jira_postmortem_for")
622
+ and self.incident.jira_postmortem_for):
623
+ jira_pm = self.incident.jira_postmortem_for
624
+ blocks.extend([
625
+ DividerBlock(),
626
+ SectionBlock(
627
+ text=(
628
+ f":memo: *Reminder:* This incident already has an existing post mortem:\n"
629
+ f"• Jira: <{jira_pm.issue_url}|{jira_pm.jira_issue_key}>\n\n"
630
+ f"Please update it with any new findings from this reopening."
631
+ )
632
+ ),
633
+ SectionBlock(
634
+ text="Need guidance on how to fill Post-Mortems in Jira?",
635
+ accessory=ButtonElement(
636
+ text="Open documentation",
637
+ url="https://manomano.atlassian.net/wiki/spaces/TC/pages/5639635000/How+to+fill+Post-Mortems+in+Jira",
638
+ value="jira_postmortem_documentation",
639
+ action_id="open_link",
640
+ ),
641
+ )
642
+ ])
643
+
603
644
  return blocks
604
645
 
605
646
  def get_metadata(self) -> Metadata:
@@ -636,16 +677,12 @@ class SlackMessageIncidentPostMortemCreated(SlackMessageSurface):
636
677
 
637
678
  # Add Confluence link if available
638
679
  if hasattr(self.incident, "postmortem_for"):
639
- parts.append(
640
- f"• Confluence: {self.incident.postmortem_for.page_url}"
641
- )
680
+ parts.append(f"• Confluence: {self.incident.postmortem_for.page_url}")
642
681
 
643
682
  # Add Jira link if available
644
683
  if hasattr(self.incident, "jira_postmortem_for"):
645
684
  jira_pm = self.incident.jira_postmortem_for
646
- parts.append(
647
- f"• Jira: {jira_pm.issue_url} ({jira_pm.jira_issue_key})"
648
- )
685
+ parts.append(f"• Jira: {jira_pm.issue_url} ({jira_pm.jira_issue_key})")
649
686
 
650
687
  return "\n".join(parts)
651
688
 
@@ -653,7 +690,10 @@ class SlackMessageIncidentPostMortemCreated(SlackMessageSurface):
653
690
  blocks: list[Block] = [SectionBlock(text=self.get_text())]
654
691
 
655
692
  # Add documentation link if Jira post-mortem exists
656
- if hasattr(self.incident, "jira_postmortem_for") and self.incident.jira_postmortem_for:
693
+ if (
694
+ hasattr(self.incident, "jira_postmortem_for")
695
+ and self.incident.jira_postmortem_for
696
+ ):
657
697
  blocks.append(
658
698
  SectionBlock(
659
699
  text="Need guidance on how to fill Post-Mortems in Jira? See our documentation",
@@ -707,7 +747,9 @@ class SlackMessageIncidentPostMortemCreatedAnnouncement(SlackMessageSurface):
707
747
  SectionBlock(
708
748
  text=f"📔 *Post-mortem created for incident #{self.incident.id}*"
709
749
  ),
710
- SectionBlock(text=f"*{shorten(self.incident.title, 2995, placeholder='...')}*"),
750
+ SectionBlock(
751
+ text=f"*{shorten(self.incident.title, 2995, placeholder='...')}*"
752
+ ),
711
753
  DividerBlock(),
712
754
  SectionBlock(fields=fields),
713
755
  ]
@@ -770,17 +812,19 @@ class SlackMessagePostMortemReminder5Days(SlackMessageSurface):
770
812
  blocks.append(ActionsBlock(elements=pm_links))
771
813
 
772
814
  # Add action buttons
773
- blocks.extend([
774
- DividerBlock(),
775
- SectionBlock(
776
- text="Update the incident status or close it once the post-mortem is complete.",
777
- accessory=ButtonElement(
778
- text="Update status",
779
- value=str(self.incident.id),
780
- action_id=UpdateStatusModal.open_action,
815
+ blocks.extend(
816
+ [
817
+ DividerBlock(),
818
+ SectionBlock(
819
+ text="Update the incident status or close it once the post-mortem is complete.",
820
+ accessory=ButtonElement(
821
+ text="Update status",
822
+ value=str(self.incident.id),
823
+ action_id=UpdateStatusModal.open_action,
824
+ ),
781
825
  ),
782
- ),
783
- ])
826
+ ]
827
+ )
784
828
 
785
829
  return blocks
786
830
 
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ import uuid
4
+
5
+ import pytest
6
+ from django.test import TestCase
7
+
8
+ from firefighter.incidents.enums import IncidentStatus
9
+ from firefighter.incidents.factories import IncidentFactory
10
+ from firefighter.incidents.forms.update_status import UpdateStatusForm
11
+ from firefighter.incidents.models import Environment, Priority
12
+
13
+
14
+ def create_unique_priority(value: int, *, needs_postmortem: bool = False) -> Priority:
15
+ """Create a Priority with unique constraints handled properly."""
16
+ return Priority.objects.create(
17
+ value=value,
18
+ name=f"Priority-{value}-{uuid.uuid4().hex[:8]}",
19
+ order=value,
20
+ needs_postmortem=needs_postmortem,
21
+ description=f"Test priority {value}"
22
+ )
23
+
24
+
25
+ def create_unique_environment(value: str, *, exact_value: bool = False) -> Environment:
26
+ """Create an Environment with unique constraints handled properly.
27
+
28
+ Args:
29
+ value: Base value for the environment
30
+ exact_value: If True, use the value exactly and get_or_create (for testing specific logic)
31
+ """
32
+ if exact_value:
33
+ # For testing specific environment logic, use exact value with get_or_create
34
+ # This handles the case where fixtures already created an environment with this value
35
+ unique_suffix = uuid.uuid4().hex[:8]
36
+ environment, _ = Environment.objects.get_or_create(
37
+ value=value, # Exact value needed for logic testing
38
+ defaults={
39
+ "name": f"Environment {value} {unique_suffix}",
40
+ "description": f"Test environment {value} {unique_suffix}"
41
+ }
42
+ )
43
+ return environment
44
+
45
+ # Default case: create unique environment
46
+ unique_suffix = uuid.uuid4().hex[:8]
47
+ return Environment.objects.create(
48
+ value=f"{value}-{unique_suffix}",
49
+ name=f"Environment {value} {unique_suffix}",
50
+ description=f"Test environment {value} {unique_suffix}"
51
+ )
52
+
53
+
54
+ @pytest.mark.django_db
55
+ class TestMitigatedReopeningWorkflow(TestCase):
56
+ """Test the new workflow allowing reopening from MITIGATED status."""
57
+
58
+ def test_p1_mitigated_allows_reopening_and_postmortem(self):
59
+ """P1 incidents should be able to go to POST_MORTEM OR reopen to INVESTIGATING/MITIGATING."""
60
+ # Create P1 priority with needs_postmortem=True
61
+ priority = create_unique_priority(value=1001, needs_postmortem=True)
62
+ # IMPORTANT: Use exact "PRD" value for environment since requires_postmortem logic checks for exact match
63
+ environment = create_unique_environment("PRD", exact_value=True)
64
+
65
+ incident = IncidentFactory.create(
66
+ _status=IncidentStatus.MITIGATED,
67
+ priority=priority,
68
+ environment=environment
69
+ )
70
+ form = UpdateStatusForm(incident=incident)
71
+
72
+ # Get available statuses
73
+ status_choices = dict(form.fields["status"].choices)
74
+ available_statuses = {IncidentStatus(int(k)) for k in status_choices}
75
+
76
+ # P1 incidents should have POST_MORTEM (required for P1/P2)
77
+ assert IncidentStatus.POST_MORTEM in available_statuses
78
+ # P1 incidents should ALSO have direct reopening options (NEW behavior)
79
+ assert IncidentStatus.INVESTIGATING in available_statuses
80
+ assert IncidentStatus.MITIGATING in available_statuses
81
+
82
+ def test_can_reopen_from_mitigated_to_mitigating_p3(self):
83
+ """P3 incidents can return from MITIGATED to MITIGATING."""
84
+ priority = create_unique_priority(value=3001, needs_postmortem=False)
85
+ environment = create_unique_environment("STG")
86
+
87
+ incident = IncidentFactory.create(
88
+ _status=IncidentStatus.MITIGATED,
89
+ priority=priority,
90
+ environment=environment
91
+ )
92
+ form = UpdateStatusForm(incident=incident)
93
+
94
+ # Get available statuses
95
+ status_choices = dict(form.fields["status"].choices)
96
+ available_statuses = {IncidentStatus(int(k)) for k in status_choices}
97
+
98
+ # Should include both return options and CLOSED
99
+ assert IncidentStatus.INVESTIGATING in available_statuses
100
+ assert IncidentStatus.MITIGATING in available_statuses
101
+ assert IncidentStatus.CLOSED in available_statuses
102
+ # P3 should not include POST_MORTEM
103
+ assert IncidentStatus.POST_MORTEM not in available_statuses
104
+
105
+ def test_form_validation_requires_message_for_reopening(self):
106
+ """Form validation requires message when reopening from MITIGATED."""
107
+ priority = create_unique_priority(value=5001, needs_postmortem=False)
108
+ environment = create_unique_environment("INT")
109
+
110
+ incident = IncidentFactory.create(
111
+ _status=IncidentStatus.MITIGATED,
112
+ priority=priority,
113
+ environment=environment
114
+ )
115
+
116
+ # Test without message - should fail
117
+ form_data = {
118
+ "status": str(IncidentStatus.INVESTIGATING.value),
119
+ "priority": str(incident.priority.id),
120
+ "incident_category": str(incident.incident_category.id),
121
+ "message": "", # Empty message should cause validation error
122
+ }
123
+ form = UpdateStatusForm(data=form_data, incident=incident)
124
+
125
+ assert form.is_valid() is False, f"Form should be invalid but got valid=True, errors={form.errors}"
126
+ assert "message" in form.errors
127
+
128
+ def test_form_validation_requires_minimum_message_length(self):
129
+ """Form validation requires minimum message length for reopening."""
130
+ priority = create_unique_priority(value=5002, needs_postmortem=False)
131
+ environment = create_unique_environment("INT")
132
+
133
+ incident = IncidentFactory.create(
134
+ _status=IncidentStatus.MITIGATED,
135
+ priority=priority,
136
+ environment=environment
137
+ )
138
+
139
+ # Test with message too short - should fail
140
+ form_data = {
141
+ "status": str(IncidentStatus.INVESTIGATING.value),
142
+ "priority": str(incident.priority.id),
143
+ "incident_category": str(incident.incident_category.id),
144
+ "message": "short", # Less than 10 chars
145
+ }
146
+ form = UpdateStatusForm(data=form_data, incident=incident)
147
+
148
+ assert form.is_valid() is False
149
+ assert "message" in form.errors
150
+
151
+ def test_form_validation_accepts_valid_reopening_message(self):
152
+ """Form validation accepts valid message for reopening."""
153
+ priority = create_unique_priority(value=5003, needs_postmortem=False)
154
+ environment = create_unique_environment("INT")
155
+
156
+ incident = IncidentFactory.create(
157
+ _status=IncidentStatus.MITIGATED,
158
+ priority=priority,
159
+ environment=environment
160
+ )
161
+
162
+ # Test with valid message - should pass
163
+ form_data = {
164
+ "status": str(IncidentStatus.INVESTIGATING.value),
165
+ "priority": str(incident.priority.id),
166
+ "incident_category": str(incident.incident_category.id),
167
+ "message": "Investigation revealed additional issues requiring further analysis",
168
+ }
169
+ form = UpdateStatusForm(data=form_data, incident=incident)
170
+
171
+ assert form.is_valid() is True
172
+
173
+ def test_form_validation_normal_transitions_unaffected(self):
174
+ """Normal transitions not from MITIGATED should be unaffected."""
175
+ priority = create_unique_priority(value=5004, needs_postmortem=False)
176
+ environment = create_unique_environment("INT")
177
+
178
+ # Test from INVESTIGATING to MITIGATING (normal flow)
179
+ incident = IncidentFactory.create(
180
+ _status=IncidentStatus.INVESTIGATING,
181
+ priority=priority,
182
+ environment=environment
183
+ )
184
+
185
+ form_data = {
186
+ "status": str(IncidentStatus.MITIGATING.value),
187
+ "priority": str(incident.priority.id),
188
+ "incident_category": str(incident.incident_category.id),
189
+ "message": "", # Empty message should be OK for normal transitions
190
+ }
191
+ form = UpdateStatusForm(data=form_data, incident=incident)
192
+
193
+ assert form.is_valid() is True