firefighter-incident 0.0.24__py3-none-any.whl → 0.0.26__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.
firefighter/_version.py CHANGED
@@ -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.24'
32
- __version_tuple__ = version_tuple = (0, 0, 24)
31
+ __version__ = version = '0.0.26'
32
+ __version_tuple__ = version_tuple = (0, 0, 26)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -40,29 +40,29 @@ class LevelChoices(models.TextChoices):
40
40
  @property
41
41
  def priority(self) -> int:
42
42
  """Send level choice priority ."""
43
- priority_mapping = {
44
- self.HIGHEST: 1,
45
- self.HIGH: 2,
46
- self.MEDIUM: 3,
47
- self.LOW: 4,
48
- self.LOWEST: 5,
49
- self.NONE: 6,
43
+ priority_mapping: dict[str, int] = {
44
+ LevelChoices.HIGHEST: 1,
45
+ LevelChoices.HIGH: 2,
46
+ LevelChoices.MEDIUM: 3,
47
+ LevelChoices.LOW: 4,
48
+ LevelChoices.LOWEST: 5,
49
+ LevelChoices.NONE: 6,
50
50
  }
51
- return priority_mapping.get(self, 6) # type: ignore[call-overload]
51
+ return priority_mapping.get(self.value, 6)
52
52
 
53
53
  @property
54
54
  def emoji(self) -> str:
55
55
  """Send emoji un function of priority."""
56
56
  none_emoji = ""
57
- emoji_mapping = {
58
- self.HIGHEST: "⏫",
59
- self.HIGH: "🔼",
60
- self.MEDIUM: "➡️",
61
- self.LOW: "🔽",
62
- self.LOWEST: "⏬",
63
- self.NONE: none_emoji,
57
+ emoji_mapping: dict[str, str] = {
58
+ LevelChoices.HIGHEST: "⏫",
59
+ LevelChoices.HIGH: "🔼",
60
+ LevelChoices.MEDIUM: "➡️",
61
+ LevelChoices.LOW: "🔽",
62
+ LevelChoices.LOWEST: "⏬",
63
+ LevelChoices.NONE: none_emoji,
64
64
  }
65
- return emoji_mapping.get(self, none_emoji) # type: ignore[call-overload]
65
+ return emoji_mapping.get(self.value, none_emoji)
66
66
 
67
67
 
68
68
  class ImpactLevel(models.Model):
@@ -229,7 +229,7 @@ class JiraPostMortemService:
229
229
  - Zoho desk ticket (customfield_10896)
230
230
  - Zendesk ticket (customfield_10895)
231
231
  - Seller Contract ID (customfield_10908)
232
- - Platform (customfield_10201)
232
+ - Platforms (customfield_10201) - first platform from list
233
233
  - Business Impact (customfield_10936)
234
234
 
235
235
  Args:
@@ -264,9 +264,10 @@ class JiraPostMortemService:
264
264
  fields["customfield_10908"] = str(seller_contract_id)
265
265
 
266
266
  # Platform - customfield_10201 (option field)
267
- platform = custom_fields.get("platform")
268
- if platform:
269
- # Remove "platform-" prefix if present
267
+ platforms = custom_fields.get("platforms", [])
268
+ if platforms:
269
+ # Extract first platform and remove "platform-" prefix if present
270
+ platform = platforms[0] if isinstance(platforms, list) else platforms
270
271
  platform_value = platform.replace("platform-", "") if isinstance(platform, str) else platform
271
272
  fields["customfield_10201"] = {"value": platform_value}
272
273
 
@@ -331,13 +331,13 @@ class PagerDutySchedule(models.Model):
331
331
  pagerduty_url = models.URLField(max_length=256, null=True, blank=True)
332
332
  # TODO: Add more fields
333
333
 
334
- escalation_policies = models.ManyToManyField(
334
+ escalation_policies: models.ManyToManyField[PagerDutyEscalationPolicy, PagerDutyEscalationPolicy] = models.ManyToManyField(
335
335
  PagerDutyEscalationPolicy, related_name="pagerduty_schedule_set", blank=True
336
336
  )
337
- users = models.ManyToManyField(
337
+ users: models.ManyToManyField[PagerDutyUser, PagerDutyUser] = models.ManyToManyField(
338
338
  PagerDutyUser, related_name="pagerduty_schedule_set", blank=True
339
339
  )
340
- teams = models.ManyToManyField(
340
+ teams: models.ManyToManyField[PagerDutyTeam, PagerDutyTeam] = models.ManyToManyField(
341
341
  PagerDutyTeam, related_name="pagerduty_schedule_set", blank=True
342
342
  )
343
343
 
@@ -24,6 +24,10 @@ error_jira_ticket_creation = "Could not create Jira ticket"
24
24
 
25
25
 
26
26
  def check_issue_id(issue: JiraObject, title: str, reporter: str) -> int | str:
27
+ """Check and return the issue ID from a JiraObject.
28
+
29
+ Raises JiraAPIError if the issue ID is missing or invalid.
30
+ """
27
31
  issue_id = issue.get("id")
28
32
  if issue_id is None:
29
33
  logger.error(
firefighter/raid/types.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TypedDict
3
+ from typing import NotRequired, TypedDict
4
4
 
5
5
  _JiraId = int | str
6
6
  _Key = str
@@ -12,8 +12,8 @@ _BusinessImpact = str
12
12
 
13
13
 
14
14
  class JiraObject(TypedDict):
15
- id: _JiraId
16
- key: _Key
15
+ id: NotRequired[_JiraId]
16
+ key: NotRequired[_Key]
17
17
  assignee_id: _AssigneeId
18
18
  reporter_id: _ReporterId
19
19
  description: _Description
@@ -115,20 +115,25 @@ class ClosureReasonModal(IncidentSelectableModalMixin, SlackModal):
115
115
  self, ack: Ack, body: dict[str, Any], incident: Incident, user: User
116
116
  ) -> bool | None:
117
117
  """Handle the closure reason modal submission."""
118
- # Early validation: Check if incident can be closed BEFORE calling ack()
119
- # This validation happens BEFORE ack() so we can display errors in the modal
120
- can_close, reasons = incident.can_be_closed
121
- if not can_close:
122
- # Build error message from reasons
123
- error_messages = [reason[1] for reason in reasons]
124
- error_text = "\n".join([f"• {msg}" for msg in error_messages])
125
- ack(
126
- response_action="errors",
127
- errors={
128
- "closure_message": f"Cannot close this incident:\n{error_text}"
129
- }
130
- )
131
- return False
118
+ # For early closure (OPEN/INVESTIGATING), we bypass normal workflow checks
119
+ # For normal closure (MITIGATED/POST_MORTEM), we must validate key events
120
+ current_status = incident.status
121
+ is_early_closure = current_status.value in {IncidentStatus.OPEN, IncidentStatus.INVESTIGATING}
122
+
123
+ if not is_early_closure:
124
+ # Normal closure path - validate that incident can be closed
125
+ can_close, reasons = incident.can_be_closed
126
+ if not can_close:
127
+ # Build error message from reasons
128
+ error_messages = [reason[1] for reason in reasons]
129
+ error_text = "\n".join([f"• {msg}" for msg in error_messages])
130
+ ack(
131
+ response_action="errors",
132
+ errors={
133
+ "closure_message": f"Cannot close this incident:\n{error_text}"
134
+ }
135
+ )
136
+ return False
132
137
 
133
138
  # Clear ALL modals in the stack (not just this one)
134
139
  # This ensures the underlying "Update Status" modal is also closed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firefighter-incident
3
- Version: 0.0.24
3
+ Version: 0.0.26
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/
@@ -6,7 +6,7 @@ gunicorn.conf.py,sha256=vHsTGjaKOr8FDMp6fTKYTX4AtokmPgYvvt5Mr0Q6APc,273
6
6
  main.py,sha256=CsbprHoOYhjCLpTJmq9Z_aRYFoFgWxoz2pDLuwm8Eqg,1558
7
7
  manage.py,sha256=5ivHGD13C6nJ8QvltKsJ9T9akA5he8da70HLWaEP3k8,689
8
8
  firefighter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- firefighter/_version.py,sha256=XrY1zhka91SvYFuoqczo63946VCS-B_2e6wTaL8sPYQ,706
9
+ firefighter/_version.py,sha256=Fnf259POysYSI-aH6DhnxuHUxCVOFWsQLs4I5Ur_KMA,706
10
10
  firefighter/api/__init__.py,sha256=JQW0Bv6xwGqy7ioxx3h6UGMzkkJ4DntDpbvV1Ncgi8k,136
11
11
  firefighter/api/admin.py,sha256=x9Ysy-GiYjb0rynmFdS9g56e6n24fkN0ouGy5QD9Yrc,4629
12
12
  firefighter/api/apps.py,sha256=P5uU1_gMrDfzurdMbfqw1Bnb2uNKKcMq17WBPg2sLhc,204
@@ -180,7 +180,7 @@ firefighter/incidents/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
180
180
  firefighter/incidents/models/__init__.py,sha256=FLVyBwIdyxLdgSvXRAKC3fry9YwwqlqhitTIuG0vWrk,877
181
181
  firefighter/incidents/models/environment.py,sha256=51txwua3dCrWZ1iSG3ZA8rbDn9c00pyMAZujl9gwE5c,827
182
182
  firefighter/incidents/models/group.py,sha256=VrVL315VFUvKW69AZuRUBg1h0jZJvn8zWeMxMOWec1Y,700
183
- firefighter/incidents/models/impact.py,sha256=bzTTW79FmclLcAKN0wmk739cXBiYIY_YHpkkASGn5Rw,4776
183
+ firefighter/incidents/models/impact.py,sha256=D9NngMtg4XdDWnMgdVYaWCoUZ-fMXTvfL0eTEk9sc7M,4854
184
184
  firefighter/incidents/models/incident.py,sha256=UdL9bc-3Ou-OS8P-nHO4YF6-Hk0mDkGNq1zjOq-TNFk,28022
185
185
  firefighter/incidents/models/incident_category.py,sha256=g4OHv_XQhWcH6dvkqkyCgjlruo_1eih_CdtAPgPhaW4,7744
186
186
  firefighter/incidents/models/incident_cost.py,sha256=juwOfJKRaNQpOHkRUCHShDDba0FU98YjRPkU4I0ofAU,1346
@@ -273,7 +273,7 @@ firefighter/jira_app/admin.py,sha256=ZHAAbhy0hm_DcklK59KMmid_ZiPn8n5V6g7cZCSNrpc
273
273
  firefighter/jira_app/apps.py,sha256=T6vHrQuMZHJoTth-xjy3CbNfPv6DyXgcR3PSMju2JS4,504
274
274
  firefighter/jira_app/client.py,sha256=qpMqNTjJUq5OqAxmwvVOE20uJe7kp737HSdsiqUu1G4,21982
275
275
  firefighter/jira_app/models.py,sha256=2zKy5VaKkhiHYA8Dukz8g0NTG82Qy5UHAHY9eMv67NE,3097
276
- firefighter/jira_app/service_postmortem.py,sha256=b76f1uv-zUo4jUWXTul4nv8UnaqQXHzuoTYEDrEpcMA,10620
276
+ firefighter/jira_app/service_postmortem.py,sha256=7VQwtVGCVZaVGfRxQXL19IM4hHrgHmItUbrsyy2gZlM,10764
277
277
  firefighter/jira_app/types.py,sha256=Ukak1U1EhcH2jQPN-UoEL6AMZ-kzPsQ8c7FUr7GmahE,956
278
278
  firefighter/jira_app/utils.py,sha256=3xuzr8viZCBm6j2J9oFzA4bUvVW8TN1DOdlpbruJ_TE,3443
279
279
  firefighter/jira_app/management/__init__.py,sha256=wy4qMZb7_K-INwwGGEhMtEeI0XTLqgUw4P8_-VEnrEw,40
@@ -298,7 +298,7 @@ firefighter/pagerduty/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
298
298
  firefighter/pagerduty/admin.py,sha256=PmC227877OM-dq86953aQppu8TMWPnCZy4xJw5NST6E,3216
299
299
  firefighter/pagerduty/apps.py,sha256=l9cVqMhntdtvWBhM6vRTJPUhYB6p1YNQaAvssVBQsdY,451
300
300
  firefighter/pagerduty/client.py,sha256=wdS_rDBrMkuuQs_2bEOoz2OmjMBJLjMrBAMdoPK4rHE,2205
301
- firefighter/pagerduty/models.py,sha256=wIAT2-BELi30blhYQKOlAxO5qV4kFv3ILVI7i0PKaJk,19448
301
+ firefighter/pagerduty/models.py,sha256=zlTaIr65qME3ZFJL7iMxhFq5O2ILKLbteolrARIRAew,19634
302
302
  firefighter/pagerduty/service.py,sha256=kZDBkTe2NZ8bZw8_9nKSvzaM3fv9k4iekpkWV-62fhw,2943
303
303
  firefighter/pagerduty/urls.py,sha256=2eOf-fZnXg3uW4pH68yE-0xJpSGLCXbKCrdShfbJHvM,455
304
304
  firefighter/pagerduty/forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -329,8 +329,8 @@ firefighter/raid/messages.py,sha256=e75kwi0hCe5ChwU4t-_6Q3Rcy22MLLdVSsYyjvG2SCM,
329
329
  firefighter/raid/models.py,sha256=29Smci739K1ZdcMu7uXYvoVEhgDpwLQoCzBbc5wvwhs,2211
330
330
  firefighter/raid/resources.py,sha256=39GhITs3OAWA1eSPZme-rLd818kuz7gwYzdN38zNz8Y,436
331
331
  firefighter/raid/serializers.py,sha256=4U6UFUNVR68lBYDzjYp8j1JzmfSftASOp1nQifCRXYQ,11941
332
- firefighter/raid/service.py,sha256=rxRjEpA5wg4JRe4UrGW-y8C8quHvGlJzQ0U8rdy-DyM,8490
333
- firefighter/raid/types.py,sha256=0lFy_Wl3Ekyy_sxTHPRxMn3kjEJVWCTPjmefDlrPqMQ,438
332
+ firefighter/raid/service.py,sha256=Njk8mXSTzrSdDyvFcPWqcWqJoA2lsSRIWjK8bgIAYpA,8618
333
+ firefighter/raid/types.py,sha256=E0ZUjBCMwCgr0eSX3CfRTpmq0n-jMans1WxoYSnj7xg,477
334
334
  firefighter/raid/urls.py,sha256=oESkDY2tfZcnPGUgULqixvbV3Z7YsZfeI10RX3A5tZY,924
335
335
  firefighter/raid/utils.py,sha256=OhsuFb46lokVxjyfeBFLMcMIGzjkTrrdd6DCXUJym0E,1111
336
336
  firefighter/raid/migrations/0001_initial_oss.py,sha256=oZQ44dHboLeHbvVyziHX_hhdis8H_Buv2W3jHiDzQhA,6259
@@ -411,7 +411,7 @@ firefighter/slack/views/events/message_deleted.py,sha256=tyA1-sAlG9ImcKIhqSn6Egu
411
411
  firefighter/slack/views/events/reaction_added.py,sha256=AipwBnrU5B35D97YIZCXdSW8W7-9QTIIQqUcrLTLQ5c,4241
412
412
  firefighter/slack/views/modals/__init__.py,sha256=U9PapAIlpuYqBonOUmBGWT8_HjQa35ilMQJXGaFLgd0,1945
413
413
  firefighter/slack/views/modals/close.py,sha256=4j5iA-lmIFuCz7B9pgDmjxrqmfWFysqWEn1YIsE75zc,12161
414
- firefighter/slack/views/modals/closure_reason.py,sha256=3ot1cb4Wzvwe-TAuR16ZopnWGInUmoeDUaKzOQ838kw,7600
414
+ firefighter/slack/views/modals/closure_reason.py,sha256=N-gp0E6W8Z1d4aH1-8BxizNTlZ4syNIF4l5B9WZHFFA,7898
415
415
  firefighter/slack/views/modals/downgrade_workflow.py,sha256=cRWsm3DmKRRI1-Jpjprb5xeY2U7HvRo6eZlUbGuzr1A,3192
416
416
  firefighter/slack/views/modals/edit.py,sha256=1N0OBSxsDuN6lJoH-djbEljy7f0LcDEpJF-U5YoEFXA,5895
417
417
  firefighter/slack/views/modals/key_event_message.py,sha256=C6yhQLQ6jBuhIr-YAoAyt-qZKu0V6nJMGZ_t3DLtUbo,5943
@@ -482,7 +482,7 @@ firefighter_tests/test_jira_app/test_incident_key_events_sync.py,sha256=ytXHzlwc
482
482
  firefighter_tests/test_jira_app/test_jira_client_watchers.py,sha256=IBrFjwhOP0rfm58BBq339CySxjdJkPYjGmISC4oQhZc,4803
483
483
  firefighter_tests/test_jira_app/test_models.py,sha256=QfMLrJVXSWqu29XEbJO6xXpOkugcdWti5_bmCEX2m3Y,4626
484
484
  firefighter_tests/test_jira_app/test_postmortem_issue_link.py,sha256=CF0UDmHOY08Oj3MYtDKNtkVDq_nbQ0nesvFWF8Rv944,7750
485
- firefighter_tests/test_jira_app/test_postmortem_service.py,sha256=DAlQxlPlB_Fd038hLeSu0PCO-Izo0z1dBgLXv4twvTE,15114
485
+ firefighter_tests/test_jira_app/test_postmortem_service.py,sha256=gUimkgf3NNIxHFDeZ5GNNzLHdaPKQCZDrTPdHmbyqDc,15120
486
486
  firefighter_tests/test_jira_app/test_timeline_template.py,sha256=_PtFnIib2HfjyylNRQXcjvdhrsoAJICOKauIDsYFQRk,4902
487
487
  firefighter_tests/test_raid/conftest.py,sha256=i_TOquYIMLDyVQ97uqxTqPJszVz4qq7L_Q7YJxTuS1o,4090
488
488
  firefighter_tests/test_raid/test_raid_alert_p4_p5.py,sha256=rz9orbt1E1vJ5POQyVZ6-SEPvqB55-xhwIWHicdfgDg,9356
@@ -518,8 +518,8 @@ firefighter_tests/test_slack/views/modals/test_send_sos.py,sha256=_rE6jD-gOzcGyh
518
518
  firefighter_tests/test_slack/views/modals/test_status.py,sha256=oQzPfwdg2tkbo9nfkO1GfS3WydxqSC6vy1AZjZDKT30,2226
519
519
  firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=3ARHZPs22FTx7IjgOldzEpVxxWeHqEbe4kQphUuSp34,55928
520
520
  firefighter_tests/test_slack/views/modals/test_utils.py,sha256=DJd2n9q6fFu8UuCRdiq9U_Cn19MdnC5c-ydLLrk6rkc,5218
521
- firefighter_incident-0.0.24.dist-info/METADATA,sha256=w-Gv8lFlwRJYusYzUdvM8oWJR5k--NyHgGNtPqmaMgQ,5570
522
- firefighter_incident-0.0.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
523
- firefighter_incident-0.0.24.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
524
- firefighter_incident-0.0.24.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
525
- firefighter_incident-0.0.24.dist-info/RECORD,,
521
+ firefighter_incident-0.0.26.dist-info/METADATA,sha256=6maVl16hOTcZXnEYTy1PhaP4ligQTcK5QVZesD6C5a0,5570
522
+ firefighter_incident-0.0.26.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
523
+ firefighter_incident-0.0.26.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
524
+ firefighter_incident-0.0.26.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
525
+ firefighter_incident-0.0.26.dist-info/RECORD,,
@@ -261,7 +261,7 @@ class TestJiraPostMortemService:
261
261
  "zendesk_ticket_id": "12345",
262
262
  "zoho_desk_ticket_id": "67890",
263
263
  "seller_contract_id": "98765",
264
- "platform": "platform-FR",
264
+ "platforms": ["platform-FR"],
265
265
  "environments": ["PRD", "STG"],
266
266
  },
267
267
  )
@@ -404,7 +404,7 @@ class TestJiraPostMortemService:
404
404
  _status=IncidentStatus.POST_MORTEM,
405
405
  created_by=user,
406
406
  custom_fields={
407
- "platform": "platform-DE",
407
+ "platforms": ["platform-DE"],
408
408
  },
409
409
  )
410
410