firefighter-incident 0.0.13__py3-none-any.whl → 0.0.14__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 +16 -3
- firefighter/api/serializers.py +8 -8
- firefighter/api/urls.py +8 -1
- firefighter/api/views/_base.py +1 -1
- firefighter/api/views/components.py +5 -5
- firefighter/api/views/incidents.py +9 -9
- firefighter/firefighter/settings/components/raid.py +3 -0
- firefighter/incidents/admin.py +24 -24
- firefighter/incidents/factories.py +14 -5
- firefighter/incidents/forms/close_incident.py +4 -4
- firefighter/incidents/forms/create_incident.py +4 -4
- firefighter/incidents/forms/update_status.py +4 -4
- firefighter/incidents/menus.py +2 -2
- firefighter/incidents/migrations/0005_enable_from_p1_to_p5_priority.py +7 -5
- firefighter/incidents/migrations/0009_update_sla.py +7 -5
- firefighter/incidents/migrations/0020_create_incident_category_model.py +64 -0
- firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py +57 -0
- firefighter/incidents/migrations/0022_add_incident_category_fields.py +34 -0
- firefighter/incidents/migrations/0023_populate_incident_category_references.py +57 -0
- firefighter/incidents/migrations/0024_remove_component_fields_and_model.py +26 -0
- firefighter/incidents/migrations/0025_make_incident_category_required.py +24 -0
- firefighter/incidents/migrations/0026_alter_incidentcategory_options_and_more.py +39 -0
- firefighter/incidents/models/__init__.py +1 -1
- firefighter/incidents/models/group.py +1 -1
- firefighter/incidents/models/incident.py +15 -15
- firefighter/incidents/models/{component.py → incident_category.py} +30 -29
- firefighter/incidents/models/incident_update.py +3 -3
- firefighter/incidents/tables.py +9 -9
- firefighter/incidents/templates/layouts/partials/incident_card.html +1 -1
- firefighter/incidents/templates/layouts/partials/incident_timeline.html +2 -2
- firefighter/incidents/templates/pages/{component_detail.html → incident_category_detail.html} +13 -13
- firefighter/incidents/templates/pages/{component_list.html → incident_category_list.html} +2 -2
- firefighter/incidents/templates/pages/incident_detail.html +3 -3
- firefighter/incidents/urls.py +6 -6
- firefighter/incidents/views/components/details.py +9 -9
- firefighter/incidents/views/components/list.py +9 -9
- firefighter/incidents/views/reports.py +2 -2
- firefighter/incidents/views/users/details.py +2 -2
- firefighter/incidents/views/views.py +7 -7
- firefighter/jira_app/client.py +1 -1
- firefighter/logging/custom_json_formatter.py +2 -1
- firefighter/pagerduty/tasks/trigger_oncall.py +1 -1
- firefighter/raid/admin.py +0 -11
- firefighter/raid/client.py +3 -3
- firefighter/raid/forms.py +53 -19
- firefighter/raid/migrations/0003_delete_raidarea.py +16 -0
- firefighter/raid/models.py +2 -21
- firefighter/raid/serializers.py +5 -4
- firefighter/raid/service.py +29 -27
- firefighter/raid/signals/incident_created.py +4 -2
- firefighter/raid/utils.py +1 -1
- firefighter/raid/views/__init__.py +1 -1
- firefighter/raid/views/open_normal.py +2 -2
- firefighter/slack/admin.py +8 -8
- firefighter/slack/management/commands/switch_test_users.py +272 -0
- firefighter/slack/messages/slack_messages.py +5 -5
- firefighter/slack/migrations/0005_add_incident_categories_fields.py +33 -0
- firefighter/slack/migrations/0006_copy_components_to_incident_categories.py +57 -0
- firefighter/slack/migrations/0007_remove_components_fields.py +22 -0
- firefighter/slack/migrations/0008_alter_conversation_incident_categories_and_more.py +33 -0
- firefighter/slack/models/conversation.py +3 -3
- firefighter/slack/models/incident_channel.py +1 -1
- firefighter/slack/models/user.py +1 -1
- firefighter/slack/models/user_group.py +3 -3
- firefighter/slack/rules.py +1 -1
- firefighter/slack/signals/get_users.py +2 -2
- firefighter/slack/signals/incident_updated.py +1 -1
- firefighter/slack/utils.py +2 -2
- firefighter/slack/views/events/home.py +2 -2
- firefighter/slack/views/modals/base_modal/form_utils.py +15 -0
- firefighter/slack/views/modals/close.py +3 -3
- firefighter/slack/views/modals/open.py +25 -1
- firefighter/slack/views/modals/opening/check_current_incidents.py +2 -2
- firefighter/slack/views/modals/opening/details/critical.py +1 -1
- firefighter/slack/views/modals/opening/select_impact.py +5 -2
- firefighter/slack/views/modals/update_status.py +4 -4
- firefighter_fixtures/incidents/{components.json → incident_categories.json} +52 -52
- {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.14.dist-info}/METADATA +2 -2
- {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.14.dist-info}/RECORD +98 -77
- firefighter_tests/conftest.py +4 -5
- firefighter_tests/test_api/test_api_landbot.py +1 -1
- firefighter_tests/test_firefighter/test_sso.py +146 -0
- firefighter_tests/test_incidents/test_forms/test_form_utils.py +15 -15
- firefighter_tests/test_incidents/test_incident_urls.py +3 -3
- firefighter_tests/test_incidents/test_models/test_incident_category.py +165 -0
- firefighter_tests/test_incidents/test_models/test_incident_model.py +2 -2
- firefighter_tests/test_raid/test_priority_mapping.py +267 -0
- firefighter_tests/test_raid/test_raid_client.py +580 -0
- firefighter_tests/test_raid/test_raid_forms.py +795 -0
- firefighter_tests/test_raid/test_raid_models.py +185 -0
- firefighter_tests/test_raid/test_raid_serializers.py +507 -0
- firefighter_tests/test_raid/test_raid_service.py +442 -0
- firefighter_tests/test_raid/test_raid_views.py +196 -0
- firefighter_tests/test_slack/views/modals/test_close.py +6 -6
- firefighter_tests/test_slack/views/modals/test_update_status.py +4 -4
- firefighter_fixtures/raid/area.json +0 -1
- {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.14.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.14.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.14.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Generated manually on 2025-08-19 - Copy component relationships to incident_categories
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def copy_components_to_incident_categories(apps, schema_editor):
|
|
7
|
+
"""Copy all component M2M relationships to incident_categories M2M relationships"""
|
|
8
|
+
Conversation = apps.get_model("slack", "Conversation")
|
|
9
|
+
UserGroup = apps.get_model("slack", "UserGroup")
|
|
10
|
+
|
|
11
|
+
# Copy conversation components to incident_categories
|
|
12
|
+
conversations_updated = 0
|
|
13
|
+
for conversation in Conversation.objects.prefetch_related("components").all():
|
|
14
|
+
# Copy all component relationships to incident_categories (same UUIDs)
|
|
15
|
+
incident_category_ids = list(conversation.components.values_list("id", flat=True))
|
|
16
|
+
conversation.incident_categories.set(incident_category_ids)
|
|
17
|
+
if incident_category_ids:
|
|
18
|
+
conversations_updated += 1
|
|
19
|
+
|
|
20
|
+
# Copy usergroup components to incident_categories
|
|
21
|
+
usergroups_updated = 0
|
|
22
|
+
for usergroup in UserGroup.objects.prefetch_related("components").all():
|
|
23
|
+
incident_category_ids = list(usergroup.components.values_list("id", flat=True))
|
|
24
|
+
usergroup.incident_categories.set(incident_category_ids)
|
|
25
|
+
if incident_category_ids:
|
|
26
|
+
usergroups_updated += 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def reverse_copy_components_to_incident_categories(apps, schema_editor):
|
|
30
|
+
"""Reverse: copy incident_categories relationships back to components"""
|
|
31
|
+
Conversation = apps.get_model("slack", "Conversation")
|
|
32
|
+
UserGroup = apps.get_model("slack", "UserGroup")
|
|
33
|
+
|
|
34
|
+
# Copy conversation incident_categories back to components
|
|
35
|
+
for conversation in Conversation.objects.prefetch_related("incident_categories").all():
|
|
36
|
+
component_ids = list(conversation.incident_categories.values_list("id", flat=True))
|
|
37
|
+
conversation.components.set(component_ids)
|
|
38
|
+
|
|
39
|
+
# Copy usergroup incident_categories back to components
|
|
40
|
+
for usergroup in UserGroup.objects.prefetch_related("incident_categories").all():
|
|
41
|
+
component_ids = list(usergroup.incident_categories.values_list("id", flat=True))
|
|
42
|
+
usergroup.components.set(component_ids)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Migration(migrations.Migration):
|
|
46
|
+
|
|
47
|
+
dependencies = [
|
|
48
|
+
("slack", "0005_add_incident_categories_fields"),
|
|
49
|
+
("incidents", "0023_populate_incident_category_references"),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
operations = [
|
|
53
|
+
migrations.RunPython(
|
|
54
|
+
copy_components_to_incident_categories,
|
|
55
|
+
reverse_copy_components_to_incident_categories,
|
|
56
|
+
),
|
|
57
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated manually on 2025-08-19 - Remove components fields from Slack models
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("slack", "0006_copy_components_to_incident_categories"),
|
|
10
|
+
("incidents", "0024_remove_component_fields_and_model"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.RemoveField(
|
|
15
|
+
model_name="conversation",
|
|
16
|
+
name="components",
|
|
17
|
+
),
|
|
18
|
+
migrations.RemoveField(
|
|
19
|
+
model_name="usergroup",
|
|
20
|
+
name="components",
|
|
21
|
+
),
|
|
22
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-09-17 17:49
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("incidents", "0026_alter_incidentcategory_options_and_more"),
|
|
10
|
+
("slack", "0007_remove_components_fields"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AlterField(
|
|
15
|
+
model_name="conversation",
|
|
16
|
+
name="incident_categories",
|
|
17
|
+
field=models.ManyToManyField(
|
|
18
|
+
blank=True,
|
|
19
|
+
related_name="conversations",
|
|
20
|
+
to="incidents.incidentcategory",
|
|
21
|
+
),
|
|
22
|
+
),
|
|
23
|
+
migrations.AlterField(
|
|
24
|
+
model_name="usergroup",
|
|
25
|
+
name="incident_categories",
|
|
26
|
+
field=models.ManyToManyField(
|
|
27
|
+
blank=True,
|
|
28
|
+
help_text="Incident created with this usergroup automatically add the group members to these issue categories.",
|
|
29
|
+
related_name="usergroups",
|
|
30
|
+
to="incidents.incidentcategory",
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
]
|
|
@@ -11,7 +11,7 @@ from django_stubs_ext.db.models import TypedModelMeta
|
|
|
11
11
|
from slack_sdk.errors import SlackApiError
|
|
12
12
|
|
|
13
13
|
from firefighter.firefighter.utils import get_in
|
|
14
|
-
from firefighter.incidents.models import
|
|
14
|
+
from firefighter.incidents.models import IncidentCategory, User
|
|
15
15
|
from firefighter.slack.messages.base import SlackMessageStrategy, SlackMessageSurface
|
|
16
16
|
from firefighter.slack.models.user import SlackUser
|
|
17
17
|
from firefighter.slack.slack_app import DefaultWebClient, SlackApp, slack_client
|
|
@@ -188,8 +188,8 @@ class Conversation(models.Model):
|
|
|
188
188
|
updated_at = models.DateTimeField(auto_now=True)
|
|
189
189
|
|
|
190
190
|
members = models.ManyToManyField(User, blank=True)
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
incident_categories = models.ManyToManyField["IncidentCategory", "IncidentCategory"](
|
|
192
|
+
IncidentCategory, related_name="conversations", blank=True
|
|
193
193
|
)
|
|
194
194
|
tag = models.CharField(
|
|
195
195
|
max_length=80,
|
|
@@ -100,7 +100,7 @@ class IncidentChannel(Conversation):
|
|
|
100
100
|
self, client: WebClient = DefaultWebClient
|
|
101
101
|
) -> SlackResponse | None:
|
|
102
102
|
incident: Incident = self.incident
|
|
103
|
-
topic = f"Incident - {incident.priority.emoji} {incident.priority.name} - {incident.status.label} - {incident.
|
|
103
|
+
topic = f"Incident - {incident.priority.emoji} {incident.priority.name} - {incident.status.label} - {incident.incident_category.group.name} - {incident.incident_category.name} - {SLACK_APP_EMOJI} <{incident.status_page_url + '?utm_medium=FireFighter+Slack&utm_source=Slack+Topic&utm_campaign=Slack+Topic+Link'}| {APP_DISPLAY_NAME} Status>"
|
|
104
104
|
|
|
105
105
|
if len(topic) > 250:
|
|
106
106
|
logger.warning(
|
firefighter/slack/models/user.py
CHANGED
|
@@ -147,7 +147,7 @@ class SlackUserManager(models.Manager["SlackUser"]):
|
|
|
147
147
|
# If we have no Slack User, let's go ahead and create a User and its associated SlackUser
|
|
148
148
|
user, _created = User.objects.get_or_create(
|
|
149
149
|
email=email,
|
|
150
|
-
username=email.split("@")[0],
|
|
150
|
+
username=email.split("@", maxsplit=1)[0],
|
|
151
151
|
defaults={
|
|
152
152
|
"name": clean_user_info["name"],
|
|
153
153
|
},
|
|
@@ -8,7 +8,7 @@ from django.db import models
|
|
|
8
8
|
from django_stubs_ext.db.models import TypedModelMeta
|
|
9
9
|
|
|
10
10
|
from firefighter.firefighter.utils import get_first_in, get_in
|
|
11
|
-
from firefighter.incidents.models import
|
|
11
|
+
from firefighter.incidents.models import IncidentCategory, User
|
|
12
12
|
from firefighter.slack.slack_app import DefaultWebClient, SlackApp, slack_client
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
@@ -167,8 +167,8 @@ class UserGroup(models.Model):
|
|
|
167
167
|
help_text="Is this an external group, from an external Slack Workspace? Corresponds to the `is_external` field in the Slack API.",
|
|
168
168
|
)
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
incident_categories = models.ManyToManyField["IncidentCategory", "IncidentCategory"](
|
|
171
|
+
IncidentCategory,
|
|
172
172
|
related_name="usergroups",
|
|
173
173
|
blank=True,
|
|
174
174
|
help_text="Incident created with this usergroup automatically add the group members to these issue categories.",
|
firefighter/slack/rules.py
CHANGED
|
@@ -50,5 +50,5 @@ def should_publish_in_it_deploy_channel(incident: Incident) -> bool:
|
|
|
50
50
|
incident.environment.value == "PRD"
|
|
51
51
|
and incident.priority.value <= 1
|
|
52
52
|
and not incident.private
|
|
53
|
-
and incident.
|
|
53
|
+
and incident.incident_category.deploy_warning
|
|
54
54
|
)
|
|
@@ -26,8 +26,8 @@ logger = logging.getLogger(__name__)
|
|
|
26
26
|
def get_invites_from_slack(incident: Incident, **_kwargs: Any) -> Iterable[User]:
|
|
27
27
|
"""New version using cached users instead of querying Slack API."""
|
|
28
28
|
# Prepare sub-queries
|
|
29
|
-
slack_usergroups: QuerySet[UserGroup] = incident.
|
|
30
|
-
slack_conversations: QuerySet[Conversation] = incident.
|
|
29
|
+
slack_usergroups: QuerySet[UserGroup] = incident.incident_category.usergroups.all()
|
|
30
|
+
slack_conversations: QuerySet[Conversation] = incident.incident_category.conversations.all()
|
|
31
31
|
|
|
32
32
|
# We make sure to exclude the bot user, and avoid duplicates with distinct()
|
|
33
33
|
# Also make sure that all users have related SlackUser and a slack_id
|
|
@@ -44,7 +44,7 @@ def incident_updated_update_status_handler(
|
|
|
44
44
|
# Update topic if needed
|
|
45
45
|
if (
|
|
46
46
|
"priority_id" in updated_fields
|
|
47
|
-
or "
|
|
47
|
+
or "incident_category_id" in updated_fields
|
|
48
48
|
or "_status" in updated_fields
|
|
49
49
|
):
|
|
50
50
|
incident.conversation.set_incident_channel_topic()
|
firefighter/slack/utils.py
CHANGED
|
@@ -97,9 +97,9 @@ def channel_name_from_incident(incident: Incident) -> str:
|
|
|
97
97
|
)
|
|
98
98
|
date_formatted = localtime(incident.created_at).strftime("%Y%m%d")
|
|
99
99
|
if incident.environment is not None and incident.environment.value != "PRD":
|
|
100
|
-
topic = f"{date_formatted}-{str(incident.id)[:8]}-{incident.environment.value}-{incident.
|
|
100
|
+
topic = f"{date_formatted}-{str(incident.id)[:8]}-{incident.environment.value}-{incident.incident_category.name}"
|
|
101
101
|
else:
|
|
102
|
-
topic = f"{date_formatted}-{str(incident.id)[:8]}-{incident.
|
|
102
|
+
topic = f"{date_formatted}-{str(incident.id)[:8]}-{incident.incident_category.name}"
|
|
103
103
|
|
|
104
104
|
# Strip non-alphanumeric characters, cut at 80 chars
|
|
105
105
|
# XXX django.utils.text.slugify should be used instead
|
|
@@ -57,7 +57,7 @@ def update_home_tab(
|
|
|
57
57
|
Incident.objects.filter(_status__lt=IncidentStatus.CLOSED.value)
|
|
58
58
|
.order_by("-id")
|
|
59
59
|
.select_related(
|
|
60
|
-
"priority", "
|
|
60
|
+
"priority", "incident_category", "environment", "incident_category__group", "conversation"
|
|
61
61
|
)[:30]
|
|
62
62
|
)
|
|
63
63
|
blocks: list[Block] = [
|
|
@@ -148,7 +148,7 @@ def _home_incident_element(
|
|
|
148
148
|
text=f":rotating_light: *Priority:* {incident.priority.emoji} {incident.priority.name}"
|
|
149
149
|
),
|
|
150
150
|
MarkdownTextObject(
|
|
151
|
-
text=f":package: *
|
|
151
|
+
text=f":package: *Incident category:* {incident.incident_category.group.name} - {incident.incident_category.name}"
|
|
152
152
|
),
|
|
153
153
|
MarkdownTextObject(
|
|
154
154
|
text=f":speaking_head_in_silhouette: *Last update:* {date_time(incident.updated_at)}"
|
|
@@ -336,6 +336,12 @@ class SlackForm(Generic[T]):
|
|
|
336
336
|
):
|
|
337
337
|
slack_input_kwargs["options"].append(slack_input_kwargs["initial_option"])
|
|
338
338
|
|
|
339
|
+
# Ensure we have at least one option for Slack API
|
|
340
|
+
if not slack_input_kwargs["options"]:
|
|
341
|
+
slack_input_kwargs["options"] = [
|
|
342
|
+
SafeOption(label="Please select an option", value="__placeholder__")
|
|
343
|
+
]
|
|
344
|
+
|
|
339
345
|
field_name = f"{field_name}___{f.initial}{datetime.now().timestamp()}" # noqa: DTZ005
|
|
340
346
|
field_name = field_name[:254]
|
|
341
347
|
return SelectElement(action_id=field_name, **slack_input_kwargs)
|
|
@@ -401,6 +407,15 @@ class SlackForm(Generic[T]):
|
|
|
401
407
|
OptionGroup(label=str(group_name), options=subgroup)
|
|
402
408
|
)
|
|
403
409
|
|
|
410
|
+
# Ensure we have at least one option group for Slack API
|
|
411
|
+
if not slack_input_kwargs["option_groups"]:
|
|
412
|
+
slack_input_kwargs["option_groups"] = [
|
|
413
|
+
OptionGroup(
|
|
414
|
+
label="No options available",
|
|
415
|
+
options=[SafeOption(label="Please select an option", value="__placeholder__")]
|
|
416
|
+
)
|
|
417
|
+
]
|
|
418
|
+
|
|
404
419
|
return SelectElement(action_id=field_name, **slack_input_kwargs)
|
|
405
420
|
|
|
406
421
|
@classmethod
|
|
@@ -229,8 +229,8 @@ class CloseModal(
|
|
|
229
229
|
# If fields haven't changed, don't include them in the update.
|
|
230
230
|
update_kwargs = {}
|
|
231
231
|
for changed_key in form.changed_data:
|
|
232
|
-
if changed_key == "
|
|
233
|
-
update_kwargs["
|
|
232
|
+
if changed_key == "incident_category":
|
|
233
|
+
update_kwargs["incident_category_id"] = form.cleaned_data[changed_key].id
|
|
234
234
|
if changed_key in {"description", "title", "message"}:
|
|
235
235
|
update_kwargs[changed_key] = form.cleaned_data[changed_key]
|
|
236
236
|
# Check can close
|
|
@@ -258,7 +258,7 @@ class CloseModal(
|
|
|
258
258
|
return {
|
|
259
259
|
"title": incident.title,
|
|
260
260
|
"description": incident.description,
|
|
261
|
-
"
|
|
261
|
+
"incident_category": incident.incident_category,
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
@staticmethod
|
|
@@ -56,6 +56,12 @@ INCIDENT_TYPES: dict[ResponseType, dict[str, dict[str, Any]]] = {
|
|
|
56
56
|
"slack_form": OpeningCriticalModal,
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
|
+
"normal": {
|
|
60
|
+
"normal": {
|
|
61
|
+
"label": "Normal",
|
|
62
|
+
"slack_form": OpeningCriticalModal,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
|
|
@@ -224,6 +230,20 @@ class OpenModal(SlackModal):
|
|
|
224
230
|
) -> list[Block]:
|
|
225
231
|
if details_form_modal_class is None:
|
|
226
232
|
return []
|
|
233
|
+
|
|
234
|
+
# Check if we need incident type selection and if it's been done
|
|
235
|
+
response_type = open_incident_context.get("response_type")
|
|
236
|
+
incident_type_value = open_incident_context.get("incident_type")
|
|
237
|
+
|
|
238
|
+
# If there are multiple incident types and none is selected, don't show details button yet
|
|
239
|
+
if (
|
|
240
|
+
response_type
|
|
241
|
+
and response_type in INCIDENT_TYPES
|
|
242
|
+
and len(INCIDENT_TYPES[response_type]) > 1
|
|
243
|
+
and incident_type_value is None
|
|
244
|
+
):
|
|
245
|
+
return []
|
|
246
|
+
|
|
227
247
|
return [
|
|
228
248
|
SectionBlock(
|
|
229
249
|
text=f"{'✅' if details_form_done else '📝'} Finally, add incident details",
|
|
@@ -510,7 +530,8 @@ class OpenModal(SlackModal):
|
|
|
510
530
|
"""Format a single impact value into description text."""
|
|
511
531
|
# Handle object with name and description attributes (impact levels)
|
|
512
532
|
if hasattr(value, "name") and hasattr(value, "description"):
|
|
513
|
-
|
|
533
|
+
# Filter out "no impact" levels using the value field instead of name
|
|
534
|
+
if (hasattr(value, "value") and value.value == "NO") or value.name == "NO" or not value.description:
|
|
514
535
|
return ""
|
|
515
536
|
|
|
516
537
|
description = ""
|
|
@@ -548,6 +569,9 @@ class OpenModal(SlackModal):
|
|
|
548
569
|
return incident_types[next(iter(incident_types.keys()))].get("slack_form")
|
|
549
570
|
if incident_types and incident_type_value is not None:
|
|
550
571
|
return incident_types[incident_type_value].get("slack_form")
|
|
572
|
+
# Fallback for "normal" response type when no specific incident type is selected
|
|
573
|
+
if response_type == "normal" and incident_types:
|
|
574
|
+
return incident_types[next(iter(incident_types.keys()))].get("slack_form")
|
|
551
575
|
logger.debug(
|
|
552
576
|
f"No incident type found for {open_incident_context}. No fallback."
|
|
553
577
|
)
|
|
@@ -58,7 +58,7 @@ class CreateIncidentFormSlack(CreateIncidentForm):
|
|
|
58
58
|
"label_from_instance": lambda obj: f"{obj.emoji} {obj.name} - {obj.description}",
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
|
-
"
|
|
61
|
+
"incident_category": {
|
|
62
62
|
"input": {
|
|
63
63
|
"placeholder": "Select affected issue category",
|
|
64
64
|
},
|
|
@@ -139,13 +139,16 @@ class SelectImpactModal(
|
|
|
139
139
|
impact_descriptions = ""
|
|
140
140
|
if impact_form_data:
|
|
141
141
|
for value in impact_form_data.values():
|
|
142
|
-
|
|
142
|
+
# Filter out "no impact" levels using the value field instead of name
|
|
143
|
+
if not ((hasattr(value, "value") and value.value == "NO") or value.name == "NO" or not value.description):
|
|
143
144
|
if hasattr(value, "impact_type_id") and value.impact_type_id:
|
|
144
145
|
impact_type = ImpactType.objects.get(pk=value.impact_type_id)
|
|
145
146
|
if impact_type:
|
|
146
147
|
impact_descriptions += f"\u00A0\u00A0 :exclamation: {impact_type} - {value}\n"
|
|
147
148
|
for line in str(value.description).splitlines():
|
|
148
|
-
|
|
149
|
+
# Skip empty lines or lines with only dashes/whitespace
|
|
150
|
+
if line.strip() and line.strip() != "-":
|
|
151
|
+
impact_descriptions += f"\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0 • {line}\n"
|
|
149
152
|
return impact_descriptions
|
|
150
153
|
|
|
151
154
|
def handle_modal_fn( # type: ignore
|
|
@@ -51,7 +51,7 @@ class UpdateStatusFormSlack(UpdateStatusForm):
|
|
|
51
51
|
"label_from_instance": priority_label,
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
|
-
"
|
|
54
|
+
"incident_category": {
|
|
55
55
|
"input": {
|
|
56
56
|
"placeholder": "Select affected issue category",
|
|
57
57
|
}
|
|
@@ -73,7 +73,7 @@ class UpdateStatusModal(ModalForm[UpdateStatusFormSlack]):
|
|
|
73
73
|
initial={
|
|
74
74
|
"status": incident.status,
|
|
75
75
|
"priority": incident.priority,
|
|
76
|
-
"
|
|
76
|
+
"incident_category": incident.incident_category,
|
|
77
77
|
}
|
|
78
78
|
).slack_blocks()
|
|
79
79
|
blocks.append(slack_block_separator())
|
|
@@ -97,7 +97,7 @@ class UpdateStatusModal(ModalForm[UpdateStatusFormSlack]):
|
|
|
97
97
|
"initial": {
|
|
98
98
|
"status": incident.status,
|
|
99
99
|
"priority": incident.priority,
|
|
100
|
-
"
|
|
100
|
+
"incident_category": incident.incident_category,
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
103
|
)
|
|
@@ -109,7 +109,7 @@ class UpdateStatusModal(ModalForm[UpdateStatusFormSlack]):
|
|
|
109
109
|
return
|
|
110
110
|
update_kwargs: dict[str, Any] = {}
|
|
111
111
|
for changed_key in form.changed_data:
|
|
112
|
-
if changed_key in {"
|
|
112
|
+
if changed_key in {"incident_category", "priority"}:
|
|
113
113
|
update_kwargs[f"{changed_key}_id"] = form.cleaned_data[changed_key].id
|
|
114
114
|
if changed_key in {"description", "title", "message", "status"}:
|
|
115
115
|
update_kwargs[changed_key] = form.cleaned_data[changed_key]
|