firefighter-incident 0.0.13__py3-none-any.whl → 0.0.15__py3-none-any.whl

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 (136) hide show
  1. firefighter/_version.py +16 -3
  2. firefighter/api/serializers.py +17 -8
  3. firefighter/api/urls.py +8 -1
  4. firefighter/api/views/_base.py +1 -1
  5. firefighter/api/views/components.py +5 -5
  6. firefighter/api/views/incidents.py +9 -9
  7. firefighter/confluence/signals/incident_updated.py +2 -2
  8. firefighter/firefighter/settings/components/raid.py +3 -0
  9. firefighter/incidents/admin.py +24 -24
  10. firefighter/incidents/enums.py +22 -2
  11. firefighter/incidents/factories.py +14 -5
  12. firefighter/incidents/forms/close_incident.py +4 -4
  13. firefighter/incidents/forms/closure_reason.py +45 -0
  14. firefighter/incidents/forms/create_incident.py +4 -4
  15. firefighter/incidents/forms/unified_incident.py +406 -0
  16. firefighter/incidents/forms/update_status.py +91 -5
  17. firefighter/incidents/menus.py +2 -2
  18. firefighter/incidents/migrations/0005_enable_from_p1_to_p5_priority.py +7 -5
  19. firefighter/incidents/migrations/0009_update_sla.py +7 -5
  20. firefighter/incidents/migrations/0020_create_incident_category_model.py +64 -0
  21. firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py +57 -0
  22. firefighter/incidents/migrations/0022_add_incident_category_fields.py +34 -0
  23. firefighter/incidents/migrations/0023_populate_incident_category_references.py +57 -0
  24. firefighter/incidents/migrations/0024_remove_component_fields_and_model.py +26 -0
  25. firefighter/incidents/migrations/0025_make_incident_category_required.py +24 -0
  26. firefighter/incidents/migrations/0026_alter_incidentcategory_options_and_more.py +39 -0
  27. firefighter/incidents/migrations/0027_add_closure_fields.py +40 -0
  28. firefighter/incidents/migrations/0028_add_closure_reason_constraint.py +33 -0
  29. firefighter/incidents/migrations/0029_add_custom_fields_to_incident.py +22 -0
  30. firefighter/incidents/models/__init__.py +1 -1
  31. firefighter/incidents/models/group.py +1 -1
  32. firefighter/incidents/models/incident.py +47 -20
  33. firefighter/incidents/models/{component.py → incident_category.py} +30 -29
  34. firefighter/incidents/models/incident_update.py +3 -3
  35. firefighter/incidents/static/css/main.min.css +1 -1
  36. firefighter/incidents/tables.py +9 -9
  37. firefighter/incidents/templates/layouts/partials/incident_card.html +1 -1
  38. firefighter/incidents/templates/layouts/partials/incident_timeline.html +2 -2
  39. firefighter/incidents/templates/layouts/partials/status_pill.html +1 -1
  40. firefighter/incidents/templates/pages/{component_detail.html → incident_category_detail.html} +13 -13
  41. firefighter/incidents/templates/pages/{component_list.html → incident_category_list.html} +2 -2
  42. firefighter/incidents/templates/pages/incident_detail.html +3 -3
  43. firefighter/incidents/urls.py +6 -6
  44. firefighter/incidents/views/components/details.py +9 -9
  45. firefighter/incidents/views/components/list.py +9 -9
  46. firefighter/incidents/views/reports.py +5 -5
  47. firefighter/incidents/views/users/details.py +2 -2
  48. firefighter/incidents/views/views.py +7 -7
  49. firefighter/jira_app/client.py +1 -1
  50. firefighter/logging/custom_json_formatter.py +2 -1
  51. firefighter/pagerduty/tasks/trigger_oncall.py +1 -1
  52. firefighter/raid/admin.py +0 -11
  53. firefighter/raid/apps.py +9 -26
  54. firefighter/raid/client.py +5 -5
  55. firefighter/raid/forms.py +84 -213
  56. firefighter/raid/migrations/0003_delete_raidarea.py +16 -0
  57. firefighter/raid/models.py +2 -21
  58. firefighter/raid/serializers.py +5 -4
  59. firefighter/raid/service.py +29 -27
  60. firefighter/raid/signals/incident_created.py +42 -15
  61. firefighter/raid/signals/incident_updated.py +3 -2
  62. firefighter/raid/utils.py +1 -1
  63. firefighter/raid/views/__init__.py +1 -1
  64. firefighter/slack/admin.py +8 -8
  65. firefighter/slack/management/commands/switch_test_users.py +272 -0
  66. firefighter/slack/messages/slack_messages.py +24 -9
  67. firefighter/slack/migrations/0005_add_incident_categories_fields.py +33 -0
  68. firefighter/slack/migrations/0006_copy_components_to_incident_categories.py +57 -0
  69. firefighter/slack/migrations/0007_remove_components_fields.py +22 -0
  70. firefighter/slack/migrations/0008_alter_conversation_incident_categories_and_more.py +33 -0
  71. firefighter/slack/models/conversation.py +3 -3
  72. firefighter/slack/models/incident_channel.py +1 -1
  73. firefighter/slack/models/user.py +1 -1
  74. firefighter/slack/models/user_group.py +3 -3
  75. firefighter/slack/rules.py +2 -2
  76. firefighter/slack/signals/create_incident_conversation.py +6 -0
  77. firefighter/slack/signals/get_users.py +2 -2
  78. firefighter/slack/signals/incident_updated.py +8 -2
  79. firefighter/slack/utils.py +2 -2
  80. firefighter/slack/views/events/home.py +2 -2
  81. firefighter/slack/views/modals/__init__.py +4 -0
  82. firefighter/slack/views/modals/base_modal/form_utils.py +78 -0
  83. firefighter/slack/views/modals/close.py +18 -5
  84. firefighter/slack/views/modals/closure_reason.py +193 -0
  85. firefighter/slack/views/modals/open.py +83 -12
  86. firefighter/slack/views/modals/opening/check_current_incidents.py +2 -2
  87. firefighter/slack/views/modals/opening/details/unified.py +203 -0
  88. firefighter/slack/views/modals/opening/select_impact.py +5 -2
  89. firefighter/slack/views/modals/opening/set_details.py +3 -2
  90. firefighter/slack/views/modals/postmortem.py +10 -2
  91. firefighter/slack/views/modals/update_status.py +32 -6
  92. firefighter/slack/views/modals/utils.py +51 -0
  93. firefighter_fixtures/incidents/{components.json → incident_categories.json} +52 -52
  94. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/METADATA +2 -2
  95. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/RECORD +133 -88
  96. firefighter_tests/conftest.py +4 -5
  97. firefighter_tests/test_api/test_api_landbot.py +1 -1
  98. firefighter_tests/test_firefighter/test_sso.py +146 -0
  99. firefighter_tests/test_incidents/test_enums.py +100 -0
  100. firefighter_tests/test_incidents/test_forms/conftest.py +179 -0
  101. firefighter_tests/test_incidents/test_forms/test_closure_reason.py +91 -0
  102. firefighter_tests/test_incidents/test_forms/test_form_utils.py +15 -15
  103. firefighter_tests/test_incidents/test_forms/test_unified_incident_form.py +570 -0
  104. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +581 -0
  105. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +410 -0
  106. firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +343 -0
  107. firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +167 -0
  108. firefighter_tests/test_incidents/test_incident_urls.py +3 -3
  109. firefighter_tests/test_incidents/test_models/test_incident_category.py +165 -0
  110. firefighter_tests/test_incidents/test_models/test_incident_model.py +70 -2
  111. firefighter_tests/test_raid/conftest.py +154 -0
  112. firefighter_tests/test_raid/test_p1_p3_jira_fields.py +372 -0
  113. firefighter_tests/test_raid/test_priority_mapping.py +267 -0
  114. firefighter_tests/test_raid/test_raid_client.py +580 -0
  115. firefighter_tests/test_raid/test_raid_forms.py +552 -0
  116. firefighter_tests/test_raid/test_raid_models.py +185 -0
  117. firefighter_tests/test_raid/test_raid_serializers.py +507 -0
  118. firefighter_tests/test_raid/test_raid_service.py +442 -0
  119. firefighter_tests/test_raid/test_raid_signals.py +187 -0
  120. firefighter_tests/test_raid/test_raid_views.py +196 -0
  121. firefighter_tests/test_slack/messages/__init__.py +0 -0
  122. firefighter_tests/test_slack/messages/test_slack_messages.py +367 -0
  123. firefighter_tests/test_slack/views/modals/conftest.py +140 -0
  124. firefighter_tests/test_slack/views/modals/test_close.py +71 -9
  125. firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +138 -0
  126. firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py +249 -0
  127. firefighter_tests/test_slack/views/modals/test_open.py +146 -2
  128. firefighter_tests/test_slack/views/modals/test_opening_unified.py +421 -0
  129. firefighter_tests/test_slack/views/modals/test_update_status.py +331 -7
  130. firefighter_tests/test_slack/views/modals/test_utils.py +135 -0
  131. firefighter/raid/views/open_normal.py +0 -139
  132. firefighter/slack/views/modals/opening/details/critical.py +0 -88
  133. firefighter_fixtures/raid/area.json +0 -1
  134. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/WHEEL +0 -0
  135. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/entry_points.txt +0 -0
  136. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/licenses/LICENSE +0 -0
@@ -40,18 +40,18 @@ logger = logging.getLogger(__name__)
40
40
  TZ = timezone.get_current_timezone()
41
41
 
42
42
 
43
- class ComponentManager(models.Manager["Component"]):
44
- model: type[Component]
43
+ class IncidentCategoryManager(models.Manager["IncidentCategory"]):
44
+ model: type[IncidentCategory]
45
45
 
46
46
  def queryset_with_mtbf(
47
47
  self,
48
48
  date_from: datetime,
49
49
  date_to: datetime,
50
- queryset: QuerySet[Component] | None = None,
50
+ queryset: QuerySet[IncidentCategory] | None = None,
51
51
  metric_type: str = "time_to_fix",
52
52
  field_name: str = "mtbf",
53
- ) -> QuerySet[Component]:
54
- """Returns a queryset of components with an additional `mtbf` field."""
53
+ ) -> QuerySet[IncidentCategory]:
54
+ """Returns a queryset of incident categories with an additional `mtbf` field."""
55
55
  date_to = min(date_to, datetime.now(tz=TZ))
56
56
 
57
57
  date_interval = date_to - date_from
@@ -62,12 +62,12 @@ class ComponentManager(models.Manager["Component"]):
62
62
  .annotate(
63
63
  metric_subquery=Subquery(
64
64
  IncidentMetric.objects.filter(
65
- incident__component=OuterRef("pk"),
65
+ incident__incident_category=OuterRef("pk"),
66
66
  metric_type__type=metric_type,
67
67
  incident__created_at__gte=date_from,
68
68
  incident__created_at__lte=date_to,
69
69
  )
70
- .values("incident__component")
70
+ .values("incident__incident_category")
71
71
  .annotate(sum_downtime=Sum("duration"))
72
72
  .values("sum_downtime")
73
73
  )
@@ -95,11 +95,11 @@ class ComponentManager(models.Manager["Component"]):
95
95
 
96
96
  @staticmethod
97
97
  def search(
98
- queryset: QuerySet[Component] | None, search_term: str
99
- ) -> tuple[QuerySet[Component], bool]:
98
+ queryset: QuerySet[IncidentCategory] | None, search_term: str
99
+ ) -> tuple[QuerySet[IncidentCategory], bool]:
100
100
  # XXX Common search method
101
101
  if queryset is None:
102
- queryset = Component.objects.all()
102
+ queryset = IncidentCategory.objects.all()
103
103
 
104
104
  # If not search, return the original queryset
105
105
  if search_term is None or search_term.strip() == "":
@@ -135,24 +135,24 @@ class ComponentManager(models.Manager["Component"]):
135
135
  return queryset, False
136
136
 
137
137
 
138
- class Component(models.Model):
139
- objects: ComponentManager = ComponentManager()
138
+ class IncidentCategory(models.Model):
139
+ objects: IncidentCategoryManager = IncidentCategoryManager()
140
140
 
141
141
  id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
142
142
  name = models.CharField(max_length=128)
143
143
  description = models.TextField(blank=True)
144
144
  order = models.IntegerField(
145
145
  default=0,
146
- help_text="Order of the component in the list. Should be unique per `Group`.",
146
+ help_text="Order of the incident category in the list. Should be unique per `Group`.",
147
147
  )
148
148
  group = models.ForeignKey(Group, on_delete=models.PROTECT)
149
149
  private = models.BooleanField(
150
150
  default=False,
151
- help_text="If true, incident created with this component won't be communicated, and conversations will be made private. This is useful for sensitive components. In the future, private incidents may be visible only to its members.",
151
+ help_text="If true, incident created with this incident category won't be communicated, and conversations will be made private. This is useful for sensitive incident categories. In the future, private incidents may be visible only to its members.",
152
152
  )
153
153
  deploy_warning = models.BooleanField(
154
154
  default=True,
155
- help_text="If true, a warning will be sent when creating an incident of high severity with this component.",
155
+ help_text="If true, a warning will be sent when creating an incident of high severity with this incident category.",
156
156
  )
157
157
 
158
158
  created_at = models.DateTimeField(auto_now_add=True)
@@ -169,16 +169,17 @@ class Component(models.Model):
169
169
 
170
170
  class Meta(TypedModelMeta):
171
171
  ordering = ["order"]
172
+ verbose_name_plural = "incident categories"
172
173
 
173
174
  def __str__(self) -> str:
174
175
  return f"{'🔒 ' if self.private else ''}{self.name}"
175
176
 
176
177
  def get_absolute_url(self) -> str:
177
- return reverse("incidents:component-detail", kwargs={"component_id": self.id})
178
+ return reverse("incidents:incident-category-detail", kwargs={"incident_category_id": self.id})
178
179
 
179
180
 
180
- class ComponentFilterSet(django_filters.FilterSet):
181
- """Set of filters for Component, share by Web UI and API."""
181
+ class IncidentCategoryFilterSet(django_filters.FilterSet):
182
+ """Set of filters for IncidentCategory, share by Web UI and API."""
182
183
 
183
184
  id = django_filters.CharFilter(lookup_expr="iexact")
184
185
  private = django_filters.BooleanFilter()
@@ -194,30 +195,30 @@ class ComponentFilterSet(django_filters.FilterSet):
194
195
  label="MTBF period",
195
196
  )
196
197
  search = django_filters.CharFilter(
197
- field_name="search", method="component_search", label="Search"
198
+ field_name="search", method="incident_category_search", label="Search"
198
199
  )
199
200
 
200
201
  @staticmethod
201
- def component_search(
202
- queryset: QuerySet[Component], _name: str, value: str
203
- ) -> QuerySet[Component]:
204
- """Search incidents by title, description, and ID.
202
+ def incident_category_search(
203
+ queryset: QuerySet[IncidentCategory], _name: str, value: str
204
+ ) -> QuerySet[IncidentCategory]:
205
+ """Search incident categories by title, description, and ID.
205
206
 
206
207
  Args:
207
- queryset (QuerySet[Component]): Queryset to search in.
208
+ queryset (QuerySet[IncidentCategory]): Queryset to search in.
208
209
  _name:
209
210
  value (str): Value to search for.
210
211
 
211
212
  Returns:
212
- QuerySet[Component]: Search results.
213
+ QuerySet[IncidentCategory]: Search results.
213
214
  """
214
- return Component.objects.search(queryset=queryset, search_term=value)[0]
215
+ return IncidentCategory.objects.search(queryset=queryset, search_term=value)[0]
215
216
 
216
217
  @staticmethod
217
218
  def metrics_period_filter(
218
- queryset: QuerySet[Component],
219
+ queryset: QuerySet[IncidentCategory],
219
220
  _name: str,
220
221
  value: tuple[datetime, datetime, Any, Any],
221
- ) -> QuerySet[Component]:
222
+ ) -> QuerySet[IncidentCategory]:
222
223
  gte, lte, _, _ = value
223
- return Component.objects.queryset_with_mtbf(gte, lte, queryset=queryset)
224
+ return IncidentCategory.objects.queryset_with_mtbf(gte, lte, queryset=queryset)
@@ -11,8 +11,8 @@ from django.utils import timezone
11
11
  from django_stubs_ext.db.models import TypedModelMeta
12
12
 
13
13
  from firefighter.incidents.enums import IncidentStatus
14
- from firefighter.incidents.models.component import Component
15
14
  from firefighter.incidents.models.environment import Environment
15
+ from firefighter.incidents.models.incident_category import IncidentCategory
16
16
  from firefighter.incidents.models.priority import Priority
17
17
  from firefighter.incidents.models.severity import Severity
18
18
  from firefighter.incidents.models.user import User
@@ -75,8 +75,8 @@ class IncidentUpdate(models.Model):
75
75
  incident = models.ForeignKey(
76
76
  "Incident", on_delete=models.CASCADE
77
77
  )
78
- component = models.ForeignKey(
79
- Component, null=True, blank=True, on_delete=models.SET_NULL
78
+ incident_category = models.ForeignKey(
79
+ IncidentCategory, null=True, blank=True, on_delete=models.SET_NULL
80
80
  )
81
81
  created_by = models.ForeignKey(
82
82
  User, null=True, blank=True, on_delete=models.SET_NULL