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 +2 -2
- firefighter/incidents/tasks/updateoncall.py +3 -3
- firefighter/incidents/templates/pages/incident_detail.html +15 -2
- firefighter/incidents/views/views.py +28 -0
- {firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/METADATA +1 -1
- {firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/RECORD +10 -9
- firefighter_tests/test_incidents/test_views/test_incident_detail_jira_postmortem.py +63 -0
- {firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/licenses/LICENSE +0 -0
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
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
|
|
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
|
-
|
|
31
|
+
task_chain: Any = (
|
|
32
32
|
fetch_oncalls.s() # pyright: ignore[reportUnboundVariable]
|
|
33
33
|
| update_oncall_views.s()
|
|
34
34
|
)
|
|
35
|
-
|
|
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
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
532
|
-
firefighter_incident-0.0.
|
|
533
|
-
firefighter_incident-0.0.
|
|
534
|
-
firefighter_incident-0.0.
|
|
535
|
-
firefighter_incident-0.0.
|
|
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
|
|
File without changes
|
{firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{firefighter_incident-0.0.34.dist-info → firefighter_incident-0.0.36.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|