firefighter-incident 0.0.2__py3-none-any.whl → 0.0.3__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/firefighter/settings/components/common.py +6 -3
- firefighter/incidents/forms/select_impact.py +4 -11
- firefighter/incidents/migrations/0005_enable_from_p1_to_p5_priority.py +30 -0
- firefighter/incidents/migrations/0006_update_group_names.py +102 -0
- firefighter/incidents/migrations/0007_update_component_name.py +148 -0
- firefighter/incidents/migrations/0008_impact_level.py +273 -0
- firefighter/incidents/models/impact.py +34 -4
- firefighter/incidents/static/css/main.min.css +1 -1
- firefighter/incidents/templates/pages/incident_create.html +1 -1
- firefighter/slack/migrations/0003_alter_usergroup_tag.py +22 -0
- firefighter/slack/views/modals/open.py +6 -6
- firefighter_fixtures/incidents/components.json +619 -491
- firefighter_fixtures/incidents/groups.json +64 -119
- firefighter_fixtures/incidents/impact_level.json +124 -47
- {firefighter_incident-0.0.2.dist-info → firefighter_incident-0.0.3.dist-info}/METADATA +1 -1
- {firefighter_incident-0.0.2.dist-info → firefighter_incident-0.0.3.dist-info}/RECORD +22 -17
- firefighter_tests/test_incidents/test_forms/test_form_select_impact.py +24 -24
- firefighter_tests/test_slack/views/modals/test_open.py +1 -1
- {firefighter_incident-0.0.2.dist-info → firefighter_incident-0.0.3.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.2.dist-info → firefighter_incident-0.0.3.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.2.dist-info → firefighter_incident-0.0.3.dist-info}/licenses/LICENSE +0 -0
firefighter/_version.py
CHANGED
|
@@ -88,6 +88,11 @@ WSGI_APPLICATION = "firefighter.firefighter.wsgi.application"
|
|
|
88
88
|
# Database
|
|
89
89
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
|
90
90
|
|
|
91
|
+
db_schema = config("POSTGRES_SCHEMA", default="")
|
|
92
|
+
db_options = {"options": "-c statement_timeout=30000"}
|
|
93
|
+
if db_schema:
|
|
94
|
+
db_options["options"] += f" -c search_path={db_schema}"
|
|
95
|
+
|
|
91
96
|
DATABASES: dict[str, dict[str, Any]] = {
|
|
92
97
|
"default": {
|
|
93
98
|
"ENGINE": "django.db.backends.postgresql",
|
|
@@ -96,9 +101,7 @@ DATABASES: dict[str, dict[str, Any]] = {
|
|
|
96
101
|
"PASSWORD": config("POSTGRES_PASSWORD"),
|
|
97
102
|
"HOST": config("POSTGRES_HOST"),
|
|
98
103
|
"PORT": config("POSTGRES_PORT"),
|
|
99
|
-
"OPTIONS":
|
|
100
|
-
"options": "-c statement_timeout=30000",
|
|
101
|
-
},
|
|
104
|
+
"OPTIONS": db_options,
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -45,7 +45,7 @@ class SelectImpactForm(forms.Form):
|
|
|
45
45
|
label=impact_type.emoji + " " + impact_type.name,
|
|
46
46
|
queryset=impact_type.levels.all().order_by("-order"),
|
|
47
47
|
help_text=impact_type.help_text,
|
|
48
|
-
initial=impact_type.levels.get(value=LevelChoices.
|
|
48
|
+
initial=impact_type.levels.get(value=LevelChoices.LOWEST.value),
|
|
49
49
|
)
|
|
50
50
|
self.fields[field_name].label_from_instance = ( # type: ignore[attr-defined]
|
|
51
51
|
lambda obj: obj.emoji + " " + obj.name
|
|
@@ -57,16 +57,9 @@ class SelectImpactForm(forms.Form):
|
|
|
57
57
|
impact: dict[str, ImpactLevel] = self.cleaned_data
|
|
58
58
|
|
|
59
59
|
impact_values = [impact_type.value for impact_type in impact.values()]
|
|
60
|
-
if
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return 2
|
|
64
|
-
if impact_values.count("LO") > 2:
|
|
65
|
-
return 3
|
|
66
|
-
if "LO" in impact_values or "NO" in impact_values:
|
|
67
|
-
return 4
|
|
68
|
-
return 4
|
|
69
|
-
return 4
|
|
60
|
+
priorities = [level.priority for level in LevelChoices if level in impact_values]
|
|
61
|
+
return min(priorities) if priorities else LevelChoices.LOWEST.priority
|
|
62
|
+
return LevelChoices.LOWEST.priority
|
|
70
63
|
|
|
71
64
|
@property
|
|
72
65
|
def business_impact_new(self) -> str | None:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from django.db import migrations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def update_priority_settings(apps, schema_editor):
|
|
5
|
+
Priority = apps.get_model("incidents", "Priority")
|
|
6
|
+
priorities_to_update = {
|
|
7
|
+
"P1": ("01:00:00", True, True),
|
|
8
|
+
"P2": ("04:00:00", True, True),
|
|
9
|
+
"P3": ("1 day, 00:00:00", True, True),
|
|
10
|
+
"P4": ("5 days, 00:00:00", True, True),
|
|
11
|
+
"P5": ("10 days, 00:00:00", True, True),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
for name, (sla, enabled_create, enabled_update) in priorities_to_update.items():
|
|
15
|
+
Priority.objects.filter(name=name).update(
|
|
16
|
+
sla=sla,
|
|
17
|
+
enabled_create=enabled_create,
|
|
18
|
+
enabled_update=enabled_update,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Migration(migrations.Migration):
|
|
23
|
+
|
|
24
|
+
dependencies = [
|
|
25
|
+
("incidents", "0004_incidentupdate_environment"),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
operations = [
|
|
29
|
+
migrations.RunPython(update_priority_settings),
|
|
30
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_group_mappings() -> dict:
|
|
9
|
+
"""Returns the mapping table for updating existing groups."""
|
|
10
|
+
return {
|
|
11
|
+
"Platform": ("Platform", 8),
|
|
12
|
+
"Visitors": ("Marketplace", 1),
|
|
13
|
+
"Specialist Offer (Catalog/Seller)": ("Catalog", 4),
|
|
14
|
+
"Operations": ("Operations", 6),
|
|
15
|
+
"Money": ("Payment Operations", 5),
|
|
16
|
+
"Security": ("Security", 10),
|
|
17
|
+
"Corporate IT": ("Corporate IT", 11),
|
|
18
|
+
"Other": ("Other", 12),
|
|
19
|
+
"Data": ("Data", 9),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_new_groups() -> dict:
|
|
24
|
+
"""Returns a dictionary of new groups to be created."""
|
|
25
|
+
return {
|
|
26
|
+
"Marketing & Communication": 2,
|
|
27
|
+
"Seller": 3,
|
|
28
|
+
"Finance": 7,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def add_new_groups(apps, _schema_editor):
|
|
33
|
+
Group = apps.get_model("incidents", "Group")
|
|
34
|
+
new_groups = get_new_groups()
|
|
35
|
+
|
|
36
|
+
for name, position in new_groups.items():
|
|
37
|
+
try:
|
|
38
|
+
logger.info(f"Creating new group: '{name}' with order {position}")
|
|
39
|
+
new_group = Group(name=name, order=position)
|
|
40
|
+
new_group.save()
|
|
41
|
+
except Exception:
|
|
42
|
+
logger.exception(f"Failed to create new group '{name}'.")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def remove_new_groups(apps, _schema_editor):
|
|
46
|
+
Group = apps.get_model("incidents", "Group")
|
|
47
|
+
new_group_names = get_new_groups().keys()
|
|
48
|
+
|
|
49
|
+
for name in new_group_names:
|
|
50
|
+
try:
|
|
51
|
+
logger.info(f"Removing group: '{name}'")
|
|
52
|
+
group = Group.objects.get(name=name)
|
|
53
|
+
group.delete()
|
|
54
|
+
except Exception:
|
|
55
|
+
logger.exception(f"Group '{name}' does not exist, skipping removal.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def update_groups(apps, _schema_editor):
|
|
59
|
+
Group = apps.get_model("incidents", "Group")
|
|
60
|
+
group_mappings = get_group_mappings()
|
|
61
|
+
|
|
62
|
+
updated_count = 0
|
|
63
|
+
|
|
64
|
+
for old_name, (new_name, position) in group_mappings.items():
|
|
65
|
+
try:
|
|
66
|
+
logger.info(f"Updating group: '{old_name}' to '{new_name}'")
|
|
67
|
+
group = Group.objects.get(name=old_name)
|
|
68
|
+
group.name = new_name
|
|
69
|
+
group.order = position
|
|
70
|
+
group.save()
|
|
71
|
+
updated_count += 1
|
|
72
|
+
except Exception:
|
|
73
|
+
logger.exception(f"Group '{old_name}' does not exist, cannot proceed with updates.")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def revert_group_names(apps, _schema_editor):
|
|
77
|
+
Group = apps.get_model("incidents", "Group")
|
|
78
|
+
reverse_mappings = {new_name: old_name for old_name, (new_name, _) in get_group_mappings().items()}
|
|
79
|
+
|
|
80
|
+
updated_count = 0
|
|
81
|
+
|
|
82
|
+
for new_name, old_name in reverse_mappings.items():
|
|
83
|
+
try:
|
|
84
|
+
group = Group.objects.get(name=new_name)
|
|
85
|
+
logger.info(f"Restoring group '{new_name}' back to '{old_name}'")
|
|
86
|
+
group.name = old_name
|
|
87
|
+
group.save()
|
|
88
|
+
updated_count += 1
|
|
89
|
+
except Exception:
|
|
90
|
+
logger.exception(f"Group '{new_name}' does not exist, skipping restoration.")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Migration(migrations.Migration):
|
|
94
|
+
|
|
95
|
+
dependencies = [
|
|
96
|
+
("incidents", "0005_enable_from_p1_to_p5_priority"),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
operations = [
|
|
100
|
+
migrations.RunPython(add_new_groups, remove_new_groups),
|
|
101
|
+
migrations.RunPython(update_groups, revert_group_names),
|
|
102
|
+
]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_component_mappings() -> list:
|
|
9
|
+
"""
|
|
10
|
+
Returns a list of tuples for updating existing component names and their attributes.
|
|
11
|
+
|
|
12
|
+
Each tuple contains:
|
|
13
|
+
- old_name (str): The current name of the component.
|
|
14
|
+
- new_name (str): The new name to assign to the component.
|
|
15
|
+
- slack_channel (str): The associated Slack channel for the component.
|
|
16
|
+
- group_name (str): The name of the group to which the component belongs.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
list: A list of tuples, each representing the details for a component update.
|
|
20
|
+
"""
|
|
21
|
+
return [
|
|
22
|
+
("Commercial Animation (Mabaya cat. integration & ad request, ...)", "Commercial Animation", "impact-commercial-animation", "Marketplace"),
|
|
23
|
+
("Mobile Apps", "Mobile Apps", "impact-mobile-apps", "Marketplace"),
|
|
24
|
+
("Spartacux Foundations", "Spartacux Foundations", "impact-spartacux-foundations", "Marketplace"),
|
|
25
|
+
("Tracking", "Tracking", "impact-tracking", "Marketplace"),
|
|
26
|
+
("HUB Integrators", "HUB Integrators", "impact-hub-integrators", "Seller"),
|
|
27
|
+
("Seller Account and Feeds", "Seller Catalog and Offer Management", "impact-seller-catalog-offer-management", "Seller"),
|
|
28
|
+
("Toolbox", "Seller Admin and Experience", "impact-seller-admin-experience", "Seller"),
|
|
29
|
+
("Seller Services (Mabaya BO, Subscriptions, MF)", "Seller Services", "impact-seller-services", "Seller"),
|
|
30
|
+
("Catalog Performance", "Catalog Performance", "impact-catalog-performance", "Catalog"),
|
|
31
|
+
("Offer (Price, Stock)", "Offer (Price & Stock)", "impact-offer-price-stock", "Catalog"),
|
|
32
|
+
("Back Office", "BO Catalog - Master Experience", "impact-bo-catalog-master-experience", "Catalog"),
|
|
33
|
+
("Order Lifecycle", "Order management", "impact-order-management", "Operations"),
|
|
34
|
+
("Delivery Experience", "Delivery experience", "impact-delivery-experience", "Operations"),
|
|
35
|
+
("Cloud Infrastructure", "Cloud Infrastructure", "impact-cloud-infrastructure", "Platform"),
|
|
36
|
+
("Spinak", "Spinak", "impact-spinak", "Platform"),
|
|
37
|
+
("CDN", "CDN", "impact-cdn", "Platform"),
|
|
38
|
+
("Gitlab", "Gitlab", "impact-gitlab", "Platform"),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_new_components() -> dict:
|
|
43
|
+
"""
|
|
44
|
+
Returns a dictionary of new components to be created.
|
|
45
|
+
|
|
46
|
+
Each entry in the dictionary maps a component name to a tuple containing:
|
|
47
|
+
- group_name: The name of the group the component belongs to.
|
|
48
|
+
- slack_channel: The associated Slack channel for the component.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
dict: A mapping of component names to (group name, slack channel) tuples.
|
|
52
|
+
"""
|
|
53
|
+
return {
|
|
54
|
+
"Traffic acquisition": ("Marketing & Communication", "impact-traffic-acquisition"),
|
|
55
|
+
"Company reputation": ("Marketing & Communication", "impact-company-reputation"),
|
|
56
|
+
"Loyalty and coupons": ("Payment Operations", "impact-loyalty-coupons"),
|
|
57
|
+
"Payouts to seller": ("Payment Operations", "impact-payouts-to-seller"),
|
|
58
|
+
"Refunds": ("Payment Operations", "impact-refunds"),
|
|
59
|
+
"Returns": ("Operations", "impact-returns"),
|
|
60
|
+
"Customer service": ("Operations", "impact-customer-service"),
|
|
61
|
+
"Inventory": ("Operations", "impact-inventory"),
|
|
62
|
+
"VAT": ("Finance", "impact-vat"),
|
|
63
|
+
"Seller's invoices": ("Finance", "impact-sellers-invoices"),
|
|
64
|
+
"Customer's invoices": ("Finance", "impact-customers-invoices"),
|
|
65
|
+
"Accounting": ("Finance", "impact-accounting"),
|
|
66
|
+
"Revenue": ("Finance", "impact-revenue"),
|
|
67
|
+
"Compromised laptop / server": ("Security", "impact-compromised-laptop-server"),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def add_new_components(apps, schema_editor):
|
|
72
|
+
Component = apps.get_model("incidents", "Component")
|
|
73
|
+
Group = apps.get_model("incidents", "Group")
|
|
74
|
+
new_components = get_new_components()
|
|
75
|
+
|
|
76
|
+
for name, (group_name, _slack) in new_components.items():
|
|
77
|
+
logger.info(f"Creating new component: '{name}' belonging to group '{group_name}'")
|
|
78
|
+
try:
|
|
79
|
+
group_instance = Group.objects.get(name=group_name)
|
|
80
|
+
new_component = Component(name=name, group=group_instance)
|
|
81
|
+
new_component.save()
|
|
82
|
+
except Exception:
|
|
83
|
+
logger.exception(f"Failed to create new group: '{group_name}'.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def remove_new_components(apps, schema_editor):
|
|
87
|
+
Component = apps.get_model("incidents", "Component")
|
|
88
|
+
new_component_names = get_new_components().keys()
|
|
89
|
+
|
|
90
|
+
for name in new_component_names:
|
|
91
|
+
try:
|
|
92
|
+
component = Component.objects.get(name=name)
|
|
93
|
+
logger.info(f"Removing component: '{name}'")
|
|
94
|
+
component.delete()
|
|
95
|
+
except Exception:
|
|
96
|
+
logger.exception(f"Component '{name}' does not exist, skipping removal.")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def update_component_names(apps, schema_editor):
|
|
100
|
+
Component = apps.get_model("incidents", "Component")
|
|
101
|
+
Group = apps.get_model("incidents", "Group")
|
|
102
|
+
component_mappings = get_component_mappings()
|
|
103
|
+
|
|
104
|
+
updated_count = 0
|
|
105
|
+
|
|
106
|
+
for old_name, new_name, _slack_channel, group_name in component_mappings:
|
|
107
|
+
try:
|
|
108
|
+
component = Component.objects.get(name=old_name)
|
|
109
|
+
logger.info(f"Updating: '{old_name}' to '{new_name}'")
|
|
110
|
+
component.name = new_name
|
|
111
|
+
|
|
112
|
+
# WARN: this operaration is impossible to revert
|
|
113
|
+
group_instance = Group.objects.get(name=group_name)
|
|
114
|
+
component.group = group_instance
|
|
115
|
+
component.save()
|
|
116
|
+
updated_count += 1
|
|
117
|
+
except Exception:
|
|
118
|
+
logger.exception(f"Component '{old_name}' does not exist, cannot proceed with updates.")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def revert_component_names(apps, schema_editor):
|
|
122
|
+
Component = apps.get_model("incidents", "Component")
|
|
123
|
+
reverse_mappings = {new_name: old_name for old_name, new_name, _, _ in get_component_mappings()}
|
|
124
|
+
|
|
125
|
+
updated_count = 0
|
|
126
|
+
|
|
127
|
+
for new_name, old_name in reverse_mappings.items():
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
component = Component.objects.get(name=new_name)
|
|
131
|
+
logger.info(f"Restoring '{new_name}' back to '{old_name}'")
|
|
132
|
+
component.name = old_name
|
|
133
|
+
component.save()
|
|
134
|
+
updated_count += 1
|
|
135
|
+
except Exception:
|
|
136
|
+
logger.exception(f"Component '{new_name}' does not exist, skipping restoration.")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class Migration(migrations.Migration):
|
|
140
|
+
|
|
141
|
+
dependencies = [
|
|
142
|
+
("incidents", "0006_update_group_names"),
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
operations = [
|
|
146
|
+
migrations.RunPython(update_component_names, revert_component_names),
|
|
147
|
+
migrations.RunPython(add_new_components, remove_new_components),
|
|
148
|
+
]
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Generated by Django 4.2.7 on 2023-12-04 11:09
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from django.db import migrations, models
|
|
8
|
+
|
|
9
|
+
from firefighter.incidents.models.impact import LevelChoices
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def remap_incidents(apps, schema_editor):
|
|
15
|
+
ImpactLevel = apps.get_model("incidents", "ImpactLevel")
|
|
16
|
+
Impact = apps.get_model("incidents", "Impact")
|
|
17
|
+
|
|
18
|
+
to_delete_name = "Few employees with minor issues"
|
|
19
|
+
to_delete_value = LevelChoices.LOW.value
|
|
20
|
+
impactlevel_to_delete = ImpactLevel.objects.filter(name=to_delete_name).first()
|
|
21
|
+
|
|
22
|
+
new_name = "Significant issues for some employees"
|
|
23
|
+
new_value = LevelChoices.MEDIUM.value # before remapping of impact level because after remapping "Significant issues for some employees" will refer to "LO"
|
|
24
|
+
impactlevel = ImpactLevel.objects.filter(name=new_name).first()
|
|
25
|
+
|
|
26
|
+
if impactlevel_to_delete is None:
|
|
27
|
+
logger.error(f"Failed to find ImpactLevel to delete {to_delete_value} {to_delete_name}.")
|
|
28
|
+
return
|
|
29
|
+
if impactlevel is None:
|
|
30
|
+
logger.error(f"Failed to find ImpactLevel {new_value} {new_name}.")
|
|
31
|
+
return
|
|
32
|
+
impacts = Impact.objects.filter(impact_level=impactlevel_to_delete)
|
|
33
|
+
|
|
34
|
+
for impact in impacts:
|
|
35
|
+
try:
|
|
36
|
+
impact.impact_level = impactlevel
|
|
37
|
+
impact.save()
|
|
38
|
+
except Exception:
|
|
39
|
+
logger.exception(f"Failed to find ImpactLevel {new_value} {new_name}.")
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
impactlevel_to_delete.delete()
|
|
43
|
+
except Exception:
|
|
44
|
+
logger.exception(f"Failed to delete impact level {to_delete_value} {to_delete_name}.")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def update_impact_levels(apps, schema_editor):
|
|
48
|
+
ImpactLevel = apps.get_model("incidents", "ImpactLevel")
|
|
49
|
+
|
|
50
|
+
emoji_mapping = {
|
|
51
|
+
"HT": "⏫",
|
|
52
|
+
"HI": "🔼",
|
|
53
|
+
"MD": "▶️",
|
|
54
|
+
"LO": "🔽",
|
|
55
|
+
"LT": "⏬",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
updates = [
|
|
59
|
+
{
|
|
60
|
+
"old_name": "Critical issue for many customers",
|
|
61
|
+
"old_value": "HI",
|
|
62
|
+
"new_name": None,
|
|
63
|
+
"new_value": "HT",
|
|
64
|
+
"new_order": 20,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"old_name": "Some customers have issues",
|
|
68
|
+
"old_value": "MD",
|
|
69
|
+
"new_name": "Some customers with major issues",
|
|
70
|
+
"new_value": "HI",
|
|
71
|
+
"new_order": 15,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"old_name": "Few customers with minor issues",
|
|
75
|
+
"old_value": "LO",
|
|
76
|
+
"new_name": "Some customers with significant issues",
|
|
77
|
+
"new_value": "MD",
|
|
78
|
+
"new_order": 10,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"old_name": "No impact on customers",
|
|
82
|
+
"old_value": "N/A",
|
|
83
|
+
"new_name": None,
|
|
84
|
+
"new_value": "LT",
|
|
85
|
+
"new_order": 0,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"old_name": "Key services inaccessible for most",
|
|
89
|
+
"old_value": "HI",
|
|
90
|
+
"new_name": "Critical issues for many sellers",
|
|
91
|
+
"new_value": "HT",
|
|
92
|
+
"new_order": 20,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"old_name": "Some sellers have significant issues",
|
|
96
|
+
"old_value": "MD",
|
|
97
|
+
"new_name": "Some sellers with major issues",
|
|
98
|
+
"new_value": "HI",
|
|
99
|
+
"new_order": 15,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"old_name": "Few sellers with minor issues",
|
|
103
|
+
"old_value": "LO",
|
|
104
|
+
"new_name": "Some sellers with significant issues",
|
|
105
|
+
"new_value": "MD",
|
|
106
|
+
"new_order": 10,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"old_name": "No impact on sellers",
|
|
110
|
+
"old_value": "N/A",
|
|
111
|
+
"new_name": None,
|
|
112
|
+
"new_value": "LT",
|
|
113
|
+
"new_order": 0,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"old_name": "Significant issues for some employees",
|
|
117
|
+
"old_value": "MD",
|
|
118
|
+
"new_name": "Major issues for internal users",
|
|
119
|
+
"new_value": "LO",
|
|
120
|
+
"new_order": 10,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"old_name": "Critical internal tools down",
|
|
124
|
+
"old_value": "HI",
|
|
125
|
+
"new_name": "Critical issues for internal users",
|
|
126
|
+
"new_value": "MD",
|
|
127
|
+
"new_order": 15,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"old_name": "No impact on employees",
|
|
131
|
+
"old_value": "N/A",
|
|
132
|
+
"new_name": "Minor issues for internal users",
|
|
133
|
+
"new_value": "LT",
|
|
134
|
+
"new_order": 0,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"old_name": "Whole business or revenue at risk",
|
|
138
|
+
"old_value": "HI",
|
|
139
|
+
"new_name": "Critical business impact",
|
|
140
|
+
"new_value": "HT",
|
|
141
|
+
"new_order": 20,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"old_name": "Significant business or revenue loss",
|
|
145
|
+
"old_value": "MD",
|
|
146
|
+
"new_name": "Major business impact",
|
|
147
|
+
"new_value": "HI",
|
|
148
|
+
"new_order": 15,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"old_name": "Minor impact on business or revenue",
|
|
152
|
+
"old_value": "LO",
|
|
153
|
+
"new_name": "Significant business impact",
|
|
154
|
+
"new_value": "MD",
|
|
155
|
+
"new_order": 10,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"old_name": "No impact on business or revenue",
|
|
159
|
+
"old_value": "N/A",
|
|
160
|
+
"new_name": None,
|
|
161
|
+
"new_value": "LT",
|
|
162
|
+
"new_order": 0,
|
|
163
|
+
},
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
for update in updates:
|
|
167
|
+
old_name = update["old_name"]
|
|
168
|
+
try:
|
|
169
|
+
impact_level = ImpactLevel.objects.filter(name=old_name).first()
|
|
170
|
+
if not impact_level:
|
|
171
|
+
logger.error(f"Failed to find ImpactLevel with old name:'{old_name}'.")
|
|
172
|
+
continue
|
|
173
|
+
if update["new_name"] is not None:
|
|
174
|
+
impact_level.name = old_name
|
|
175
|
+
impact_level.value = update["new_value"]
|
|
176
|
+
impact_level.order = update["new_order"]
|
|
177
|
+
impact_level.emoji = emoji_mapping.get(update["new_value"], impact_level.emoji)
|
|
178
|
+
impact_level.save()
|
|
179
|
+
except Exception:
|
|
180
|
+
logger.exception(f"Failed to update ImpactLevel '{old_name}'.")
|
|
181
|
+
|
|
182
|
+
logger.info("Successfully updated new ImpactLevels.")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def add_impact_levels(apps, schema_editor):
|
|
186
|
+
|
|
187
|
+
ImpactLevel = apps.get_model("incidents", "ImpactLevel")
|
|
188
|
+
ImpactType = apps.get_model("incidents", "ImpactType")
|
|
189
|
+
|
|
190
|
+
adds = [
|
|
191
|
+
{
|
|
192
|
+
|
|
193
|
+
"name": "Some customers with minor issues",
|
|
194
|
+
"value": LevelChoices.LOW.value,
|
|
195
|
+
"order": 5,
|
|
196
|
+
"impact_type_id": 3,
|
|
197
|
+
"emoji": LevelChoices.LOW.emoji
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "Some sellers with minor issues",
|
|
201
|
+
"value": LevelChoices.LOW.value,
|
|
202
|
+
"order": 5,
|
|
203
|
+
"impact_type_id": 2,
|
|
204
|
+
"emoji": LevelChoices.LOW.emoji
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "Minor business impact",
|
|
208
|
+
"value": LevelChoices.LOW.value,
|
|
209
|
+
"order": 5,
|
|
210
|
+
"impact_type_id": 1,
|
|
211
|
+
"emoji": LevelChoices.LOW.emoji
|
|
212
|
+
},
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
for add in adds:
|
|
216
|
+
name = add["name"]
|
|
217
|
+
try:
|
|
218
|
+
impact_level = ImpactLevel.objects.filter(name=name).first()
|
|
219
|
+
|
|
220
|
+
impact_type_id = add["impact_type_id"]
|
|
221
|
+
impact_type = ImpactType.objects.filter(id=impact_type_id).first()
|
|
222
|
+
if not impact_type:
|
|
223
|
+
logger.error(f"There is no impact type id:'{impact_type_id}'.")
|
|
224
|
+
continue
|
|
225
|
+
if impact_level:
|
|
226
|
+
logger.error(f"There is already an impact level with name:'{name}'.")
|
|
227
|
+
continue
|
|
228
|
+
new_impact_level = ImpactLevel(
|
|
229
|
+
name=name,
|
|
230
|
+
value=add["value"],
|
|
231
|
+
order=add["order"],
|
|
232
|
+
emoji=add["emoji"],
|
|
233
|
+
impact_type=impact_type,
|
|
234
|
+
)
|
|
235
|
+
new_impact_level.save()
|
|
236
|
+
except Exception:
|
|
237
|
+
logger.exception(f"Failed to create new ImpactLevel {name}.")
|
|
238
|
+
|
|
239
|
+
logger.info("Successfully created new ImpactLevels.")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class Migration(migrations.Migration):
|
|
243
|
+
|
|
244
|
+
dependencies = [
|
|
245
|
+
("incidents", "0007_update_component_name"),
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
operations = [
|
|
249
|
+
migrations.AlterField(
|
|
250
|
+
model_name="impactlevel",
|
|
251
|
+
name="emoji",
|
|
252
|
+
field=models.CharField(default="▶", max_length=5),
|
|
253
|
+
),
|
|
254
|
+
migrations.RemoveConstraint(
|
|
255
|
+
model_name="impactlevel",
|
|
256
|
+
name="incidents_impactlevel_value_valid",
|
|
257
|
+
),
|
|
258
|
+
migrations.AddConstraint(
|
|
259
|
+
model_name="impactlevel",
|
|
260
|
+
constraint=models.CheckConstraint(
|
|
261
|
+
check=models.Q(("value__in", ["HT", "HI", "MD", "LO", "LT", "NO"])),
|
|
262
|
+
name="incidents_impactlevel_value_valid",
|
|
263
|
+
),
|
|
264
|
+
),
|
|
265
|
+
migrations.AlterField(
|
|
266
|
+
model_name="impactlevel",
|
|
267
|
+
name="id",
|
|
268
|
+
field=models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False),
|
|
269
|
+
),
|
|
270
|
+
migrations.RunPython(remap_incidents),
|
|
271
|
+
migrations.RunPython(update_impact_levels),
|
|
272
|
+
migrations.RunPython(add_impact_levels),
|
|
273
|
+
]
|