firefighter-incident 0.0.34__py3-none-any.whl → 0.0.36__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.34'
32
- __version_tuple__ = version_tuple = (0, 0, 34)
31
+ __version__ = version = '0.0.36'
32
+ __version_tuple__ = version_tuple = (0, 0, 36)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
- from celery import Signature, shared_task
6
+ from celery import shared_task
7
7
  from django.conf import settings
8
8
 
9
9
  from firefighter.slack.slack_templating import user_slack_handle_or_name
@@ -28,11 +28,11 @@ BASE_URL: str = settings.BASE_URL
28
28
  @shared_task(name="incidents.update_oncall")
29
29
  def update_oncall() -> None:
30
30
  """Fetch current on-calls and update the on-call Slack topic and Confluence page."""
31
- chain: Signature[bool] = (
31
+ task_chain: Any = (
32
32
  fetch_oncalls.s() # pyright: ignore[reportUnboundVariable]
33
33
  | update_oncall_views.s()
34
34
  )
35
- chain()
35
+ task_chain()
36
36
 
37
37
 
38
38
  @shared_task(name="incidents.update_oncall_views")
@@ -102,7 +102,7 @@
102
102
  <div class="lg:col-start-8 lg:col-span-5 space-y-6 ">
103
103
  {% component "card" card_title="External resources" id="incident-integrations" %}
104
104
  {% fill "card_content" %}
105
- {% if not incident.conversation and not incident.postmortem_for and pagerduty_incident_set.count == 0 %}
105
+ {% if not incident.conversation and not has_confluence_pm and not has_jira_pm and not has_jira_ticket and pagerduty_incident_set.count == 0 %}
106
106
  <div class="px-4 py-5 sm:px-6 col-span-2">
107
107
  <h4 class="text-center font-medium text-sm text-neutral-500 dark:text-neutral-100">
108
108
  No external resources for this incident.
@@ -144,7 +144,7 @@
144
144
  </div>
145
145
  </li>
146
146
  {% endif %}
147
- {% if incident.postmortem_for %}
147
+ {% if has_confluence_app and incident.postmortem_for %}
148
148
  <li class="py-4 flex items-center">
149
149
  <div class="h-10 w-10 flex shrink-0">
150
150
  <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 m-auto" viewBox="0 0 20 20" fill="currentColor">
@@ -157,6 +157,19 @@
157
157
  </div>
158
158
  </li>
159
159
  {% endif %}
160
+ {% if has_jira_app and incident.jira_postmortem_for %}
161
+ <li class="py-4 flex items-center">
162
+ <div class="h-10 w-10 flex shrink-0">
163
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 m-auto" viewBox="0 0 20 20" fill="currentColor">
164
+ <path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd" />
165
+ </svg>
166
+ </div>
167
+ <div class="ml-3">
168
+ <p class="text-sm font-medium text-neutral-900 dark:text-neutral-100">Post-mortem</p>
169
+ <p class="text-sm text-neutral-500 dark:text-neutral-300"> <a href="{{ incident.jira_postmortem_for.issue_url }}" target="_blank" rel="noopener noreferrer" class="underline" >{{ incident.jira_postmortem_for.jira_issue_key|truncatechars:70 }}</a></p>
170
+ </div>
171
+ </li>
172
+ {% endif %}
160
173
  {% if incident.jira_ticket %}
161
174
  <li class="py-4 flex items-center">
162
175
  <div class="h-10 w-10 flex shrink-0">
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import re
5
+ from contextlib import suppress
5
6
  from typing import TYPE_CHECKING, Any, cast
6
7
 
7
8
  from django.conf import settings
@@ -194,8 +195,35 @@ class IncidentDetailView(CustomDetailView[Incident]):
194
195
 
195
196
  incident: Incident = context["incident"]
196
197
 
198
+ # Check if external resources exist
199
+ has_confluence_app = "firefighter.confluence" in settings.INSTALLED_APPS
200
+ has_jira_app = "firefighter.jira_app" in settings.INSTALLED_APPS
201
+
202
+ # Safely check for post-mortem existence
203
+ # We suppress exceptions because accessing OneToOne relations can fail
204
+ # in various ways if the related app is not installed or tables don't exist
205
+ has_confluence_pm = False
206
+ if has_confluence_app:
207
+ with suppress(AttributeError, ImportError, LookupError):
208
+ has_confluence_pm = bool(incident.postmortem_for)
209
+
210
+ has_jira_pm = False
211
+ if has_jira_app:
212
+ with suppress(AttributeError, ImportError, LookupError):
213
+ has_jira_pm = bool(incident.jira_postmortem_for)
214
+
215
+ has_jira_ticket = False
216
+ if has_jira_app:
217
+ with suppress(AttributeError, ImportError, LookupError):
218
+ has_jira_ticket = bool(incident.jira_ticket)
219
+
197
220
  additional_context = {
198
221
  "page_title": f"Incident #{incident.id}",
222
+ "has_confluence_app": has_confluence_app,
223
+ "has_jira_app": has_jira_app,
224
+ "has_confluence_pm": has_confluence_pm,
225
+ "has_jira_pm": has_jira_pm,
226
+ "has_jira_ticket": has_jira_ticket,
199
227
  }
200
228
 
201
229
  return {**context, **additional_context}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firefighter-incident
3
- Version: 0.0.34
3
+ Version: 0.0.36
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=Brj7IANCvq7zHGT7mm_VDO1_vV7OFwt6Zpt4gUwP4pM,1532
7
7
  manage.py,sha256=5ivHGD13C6nJ8QvltKsJ9T9akA5he8da70HLWaEP3k8,689
8
8
  firefighter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- firefighter/_version.py,sha256=lALZlyVk-nrQoV2VLZzOsgzhfEll5JRUW4TJRJXnR_o,706
9
+ firefighter/_version.py,sha256=0ktbVh2xRI7xnrojV_sDPVhrN753EK8vWN68JQalwWk,706
10
10
  firefighter/api/__init__.py,sha256=JQW0Bv6xwGqy7ioxx3h6UGMzkkJ4DntDpbvV1Ncgi8k,136
11
11
  firefighter/api/admin.py,sha256=Q6f37xwf-i0xypFx6zU7r6bYxsSvLm66naZSHUK13JM,4621
12
12
  firefighter/api/apps.py,sha256=P5uU1_gMrDfzurdMbfqw1Bnb2uNKKcMq17WBPg2sLhc,204
@@ -219,7 +219,7 @@ firefighter/incidents/static/img/favicon/site.webmanifest,sha256=NJW-gRQeeWpPkSi
219
219
  firefighter/incidents/static/js/main.js,sha256=T7FTIT_JMAMIPJmAvAQSjmxvzpUgkiCjzRqHEKBEl1M,558
220
220
  firefighter/incidents/static/js/main.min.js,sha256=BfqPI2rnZBm7Gk82rXZRf_A-Ihv76aAtQi8eA-rW8kA,235569
221
221
  firefighter/incidents/tasks/__init__.py,sha256=VVOIAmKi-im5UnQwZokuQmm3FcqRbBwJsxGV3cH2SO0,89
222
- firefighter/incidents/tasks/updateoncall.py,sha256=dLfhajkEjlniAz3NGZ0YCXIe2GmYPBkXfvoK1MwVZGc,4028
222
+ firefighter/incidents/tasks/updateoncall.py,sha256=9p0neb0mOm165UAA6JAr7kXrkX4gh2ZIjvdV6l3TuVc,4015
223
223
  firefighter/incidents/templates/incidents/filter.html,sha256=ZZu7FIclnwhtOCYAm_DBUgZ-cdxwFQPUXW8lATBF1mM,4401
224
224
  firefighter/incidents/templates/incidents/table.html,sha256=cNy6gLIE4IW3czOPQzKwtQrc5rbjo9XP7IyA7uUQc0g,2360
225
225
  firefighter/incidents/templates/incidents/errors/base.html,sha256=BAu35yyBSO-r6GXP0AyH6hbNOpwXsbkSCRr2AnWYzKQ,942
@@ -251,7 +251,7 @@ firefighter/incidents/templates/pages/docs_metrics.html,sha256=q10CCPwjujuj9_h7M
251
251
  firefighter/incidents/templates/pages/incident_category_detail.html,sha256=65vhQ3l3cWlLUYilfKHPuMn7lEfRJFjokiOeYUpeTTY,5425
252
252
  firefighter/incidents/templates/pages/incident_category_list.html,sha256=gXLPk9N7R585xqmA6JpL06J7hsxAXQvnsShesK2HR9o,1731
253
253
  firefighter/incidents/templates/pages/incident_create.html,sha256=syDS8EqmdcCf6z2dJox5gSPTOsyNdq0jFiDdzVc_vmU,2280
254
- firefighter/incidents/templates/pages/incident_detail.html,sha256=HhwpMg1nAaUqbYDi2RrgK5IkPhiKzHKcoSmk98O-rng,15058
254
+ firefighter/incidents/templates/pages/incident_detail.html,sha256=7KbrQMymcPUw0qbF4m08V9afsxrWxml6iGzdk-q5qPk,16238
255
255
  firefighter/incidents/templates/pages/incident_list.html,sha256=IWTZyuIeG1RVCZGNLQ_N3Jg7a_Ush6YbQXWUy0xVuV8,2339
256
256
  firefighter/incidents/templates/pages/incident_role_types_detail.html,sha256=LVoFbW9rrP4Yln_Ld-Jwe1JvhhvgwpxZgpdNzbrLWsA,4099
257
257
  firefighter/incidents/templates/pages/incident_role_types_list.html,sha256=SRBhDIfne-bX4C-EhYqyCktIksR4Rd5GPMkYfE6El1o,1556
@@ -264,7 +264,7 @@ firefighter/incidents/views/date_filter.py,sha256=fUhTjkBulMokI5tAHuqNDVv1dyspjm
264
264
  firefighter/incidents/views/date_utils.py,sha256=Q2i-84hXm1vuXy7-1x1SSteWXzXUzh4b0a79nmTEfWA,5733
265
265
  firefighter/incidents/views/errors.py,sha256=yDuH0YOdGf-voVNEC51yR9Ie3OU-az7g2EqWs_uV1Kk,7855
266
266
  firefighter/incidents/views/reports.py,sha256=1Iegx04w-oHw4cj7u9w2_s7T_e9FH5I6RRPTwDZwZhg,20973
267
- firefighter/incidents/views/views.py,sha256=4AR3i9SiC9AnrzZCRIhx147rus4_dXS0IRWsQ8ilkjg,11031
267
+ firefighter/incidents/views/views.py,sha256=ynMYwEi4vKHpYwrH6kEAiWKY91NVF8QSZfKy5thQuXg,12287
268
268
  firefighter/incidents/views/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
269
269
  firefighter/incidents/views/components/details.py,sha256=gFEezmL1TcVYnM_ryLNNMaynuIdjYV31Qzx_GfzrQiA,1040
270
270
  firefighter/incidents/views/components/list.py,sha256=u8HfXetmdL59h_4AZIhiHmKcmrPRZXgekPfnucB4Rek,2207
@@ -482,6 +482,7 @@ firefighter_tests/test_incidents/test_models/test_incident_category.py,sha256=aR
482
482
  firefighter_tests/test_incidents/test_models/test_incident_model.py,sha256=AWyWfQYcHNP9GPizIo0wRxNGTJTEJnAwNSd4UmRq-dk,8626
483
483
  firefighter_tests/test_incidents/test_models/test_migrations/test_incident_migrations.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
484
484
  firefighter_tests/test_incidents/test_utils/test_date_utils.py,sha256=ogP7qOEwItL4YGI5gbQPVssOS9ilwiuZC8OrT2qngBY,6568
485
+ firefighter_tests/test_incidents/test_views/test_incident_detail_jira_postmortem.py,sha256=WM1nr3XLGMqOEyr4e6XsWcU01aH4spiGnXd9YsVoVZY,1925
485
486
  firefighter_tests/test_incidents/test_views/test_incident_detail_view.py,sha256=lkCIRfz99Ea0o0Id08LFWrjXLDmHv6XvezaSsjg-eYQ,871
486
487
  firefighter_tests/test_incidents/test_views/test_index_view.py,sha256=InpxbaWOFwRn4YWeIKZhj17vMymrQQf2p2LFhe2Bcdw,816
487
488
  firefighter_tests/test_jira_app/__init__.py,sha256=JxZ3v-0kiHOoO-N3kR8NHTmD8tEvuEYKW1GX_S1ZLMY,33
@@ -528,8 +529,8 @@ firefighter_tests/test_slack/views/modals/test_send_sos.py,sha256=_rE6jD-gOzcGyh
528
529
  firefighter_tests/test_slack/views/modals/test_status.py,sha256=oQzPfwdg2tkbo9nfkO1GfS3WydxqSC6vy1AZjZDKT30,2226
529
530
  firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=vbHGx6dkM_0swE1vJ0HrkhI1oJzD_WHZuIQ-_arAxXo,55686
530
531
  firefighter_tests/test_slack/views/modals/test_utils.py,sha256=DJd2n9q6fFu8UuCRdiq9U_Cn19MdnC5c-ydLLrk6rkc,5218
531
- firefighter_incident-0.0.34.dist-info/METADATA,sha256=Ap1ovJI4n2CB3VlE_e-GPUyxmDm5PWrACG8e2a5kOFg,5570
532
- firefighter_incident-0.0.34.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
533
- firefighter_incident-0.0.34.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
534
- firefighter_incident-0.0.34.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
535
- firefighter_incident-0.0.34.dist-info/RECORD,,
532
+ firefighter_incident-0.0.36.dist-info/METADATA,sha256=lTBD-i5dhhYhEu9Jy1GK31W1V_HbnE1bHiNP0fItrjM,5570
533
+ firefighter_incident-0.0.36.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
534
+ firefighter_incident-0.0.36.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
535
+ firefighter_incident-0.0.36.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
536
+ firefighter_incident-0.0.36.dist-info/RECORD,,
@@ -0,0 +1,63 @@
1
+ """Test incident detail view displays Jira post-mortem links."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from django.apps import apps
7
+ from django.test import Client
8
+ from django.urls import reverse
9
+
10
+ from firefighter.incidents.models import Incident
11
+ from firefighter.incidents.models.user import User
12
+
13
+ pytestmark = pytest.mark.skipif(
14
+ not apps.is_installed("firefighter.jira_app"),
15
+ reason="Jira app not installed",
16
+ )
17
+
18
+
19
+ @pytest.mark.django_db
20
+ def test_incident_detail_view_with_jira_postmortem(
21
+ client: Client, incident_saved: Incident, admin_user: User
22
+ ) -> None:
23
+ """Test that Jira post-mortem link is displayed when it exists."""
24
+ from firefighter.jira_app.models import JiraPostMortem
25
+
26
+ # Create a Jira post-mortem for the incident
27
+ JiraPostMortem.objects.create(
28
+ incident=incident_saved,
29
+ jira_issue_key="INCIDENT-123",
30
+ jira_issue_id="10001",
31
+ created_by=admin_user,
32
+ )
33
+
34
+ client.force_login(admin_user)
35
+ response = client.get(
36
+ reverse("incidents:incident-detail", args=[incident_saved.id])
37
+ )
38
+
39
+ assert response.status_code == 200
40
+ response_content = str(response.content)
41
+
42
+ # Check that the post-mortem section is present
43
+ assert "Post-mortem" in response_content
44
+
45
+ # Check that the Jira issue key is displayed
46
+ assert "INCIDENT-123" in response_content
47
+
48
+ # Check that the URL is correctly formed
49
+ assert "browse/INCIDENT-123" in response_content
50
+
51
+
52
+ @pytest.mark.django_db
53
+ def test_incident_detail_view_without_jira_postmortem(
54
+ client: Client, incident_saved: Incident, admin_user: User
55
+ ) -> None:
56
+ """Test that incident detail view works without Jira post-mortem."""
57
+ client.force_login(admin_user)
58
+ response = client.get(
59
+ reverse("incidents:incident-detail", args=[incident_saved.id])
60
+ )
61
+
62
+ assert response.status_code == 200
63
+ # Page should load successfully even without post-mortem