Incidentsยค
The incidents module is the core of FireFighter, managing incident lifecycle, priorities, and integrations.
Priority Systemยค
FireFighter uses a 5-level priority system (P1-P5) to categorize incident severity:
Priority Levelsยค
| Priority | Emoji | Level | Description | Example |
|---|---|---|---|---|
| P1 | ๐ฅ | Critical | Complete service outage, system-wide failure | Payment system down, entire website offline |
| P2 | ๐จ | High | Major functionality impaired, significant user impact | Core feature broken, major performance degradation |
| P3 | โ ๏ธ | Medium | Minor functionality affected, moderate impact | Non-critical feature issue, isolated component problem |
| P4 | ๐ข | Low | Small issues, minimal impact | UI glitches, minor bugs in secondary features |
| P5 | ๐ก | Lowest | Cosmetic issues, enhancement requests | Typos, improvement suggestions, non-urgent requests |
Priority Usageยค
Incident Creation: When creating an incident, the priority determines: - Notification urgency and channels - Automatic escalations (PagerDuty for P1/P2) - Jira ticket priority mapping - SLA expectations
Integration Mapping: - Jira: P1-P5 maps directly to Jira priorities 1-5 - PagerDuty: P1-P2 typically trigger immediate escalation - Slack: Higher priorities get broader notification reach
Priority Assignment Guidelinesยค
P1 (Critical) - Use when: - Complete service unavailability - Data loss or corruption - Security breaches - Payment processing failures
P2 (High) - Use when: - Major feature completely broken - Significant performance degradation - Multiple users affected by the same issue
P3 (Medium) - Use when: - Single feature partially broken - Workaround exists - Limited user impact
P4 (Low) - Use when: - Minor UI issues - Edge case bugs - Enhancement requests with business value
P5 (Lowest) - Use when: - Cosmetic improvements - Documentation updates - Nice-to-have features
Priority Validation
Invalid priority values automatically fallback to P1 to ensure critical handling.
Incident Creation by Priorityยค
When an incident is created, different resources are provisioned based on priority:
| Resource | P1 | P2 | P3 | P4 | P5 |
|---|---|---|---|---|---|
| Incident object | โ | โ | โ | โ | โ |
| Jira ticket | โ | โ | โ | โ | โ |
| Slack channel | โ | โ | โ | โ | โ |
| Post-mortem required | โ | โ | โ | โ | โ |
| PagerDuty escalation | โ | โ | โ | โ | โ |
Key Points: - All priorities (P1-P5) create both an Incident object and a Jira ticket - Slack channels are only created for P1-P3 (critical/urgent incidents) - Post-mortems are mandatory for P1-P2 incidents only, must be completed before closure - PagerDuty escalation is automatic for P1-P2 incidents to alert on-call staff
API Referenceยค
incidents ยค
Modules:
-
adminโ -
enumsโ -
factoriesโ -
formsโ -
managementโ -
menusโ -
migrationsโ -
modelsโ -
observabilityโObservability signals: emit structured log events for Datadog metrics and dashboards.
-
signalsโ -
tasksโ -
timelineโThe incident timeline: its canonical steps, and the checks they must satisfy.
-
viewsโ
admin ยค
Classes:
-
EnvironmentAdminโ -
IncidentAdminโ -
IncidentRoleTypeAdminโIncidentRoleTypes are fixed at the moment (only commander and communication lead).
EnvironmentAdmin ยค
Bases: ModelAdmin[Environment]
Methods:
-
get_readonly_fieldsโDeny changing the value of an existing object.
IncidentAdmin ยค
Bases: ModelAdmin[Incident]
Methods:
-
compute_and_purge_metricsโWill compute metrics for selected incidents and delete metrics that can no longer be computed.
-
send_messageโAction to send a message in selected channels.
compute_and_purge_metrics ยค
Will compute metrics for selected incidents and delete metrics that can no longer be computed.
Source code in src/firefighter/incidents/admin.py
send_message ยค
send_message(request: HttpRequest, queryset: QuerySet[Incident]) -> TemplateResponse | None
Action to send a message in selected channels. This action first displays a confirmation page to enter the message. Next, it sends the message on all selected objects and redirects back to the change list (other fn).
Source code in src/firefighter/incidents/admin.py
IncidentRoleTypeAdmin ยค
Bases: ModelAdmin[IncidentRoleType]
IncidentRoleTypes are fixed at the moment (only commander and communication lead).
enums ยค
Classes:
-
ClosureReasonโReasons for direct incident closure bypassing normal workflow.
-
IncidentStatusโ
ClosureReason ยค
Bases: TextChoices
Reasons for direct incident closure bypassing normal workflow.
IncidentStatus ยค
Bases: IntegerChoices
Methods:
-
choices_lte_skip_postmortemโReturn choices up to val but excluding POST_MORTEM (for P3+ incidents).
choices_lte_skip_postmortem staticmethod ยค
Return choices up to val but excluding POST_MORTEM (for P3+ incidents).
Source code in src/firefighter/incidents/enums.py
factories ยค
Classes:
-
PriorityFactoryโFactory for creating Priority instances in tests.
forms ยค
Modules:
-
closure_reasonโForm for incident closure with reason when closing from early statuses.
-
editโ -
select_impactโ -
timelineโOne editable form for the whole incident timeline, shared by both Slack surfaces.
-
unified_incidentโ -
update_key_eventsโ -
update_rolesโ -
update_statusโ -
utilsโ
closure_reason ยค
Form for incident closure with reason when closing from early statuses.
Classes:
-
IncidentClosureReasonFormโForm for closing an incident with a mandatory reason from early statuses.
IncidentClosureReasonForm ยค
Bases: Form
Form for closing an incident with a mandatory reason from early statuses.
Source code in src/firefighter/incidents/forms/closure_reason.py
edit ยค
Functions:
-
initial_environmentsโGet default environments for the form.
select_impact ยค
Classes:
-
SelectImpactFormโ
SelectImpactForm ยค
Bases: Form
Methods:
-
saveโSave the impact choices to the incident.
-
suggest_priority_from_impactโSuggest a priority from 1 (highest) to 5 (lowest) based on the impact choices.
Attributes:
-
business_impact_new(str | None) โGet business impact. Will return N/A, Lowest, Low, Medium, High or Highest.
Source code in src/firefighter/incidents/forms/select_impact.py
business_impact_new property ยค
business_impact_new: str | None
Get business impact. Will return N/A, Lowest, Low, Medium, High or Highest.
save ยค
Save the impact choices to the incident.
Source code in src/firefighter/incidents/forms/select_impact.py
suggest_priority_from_impact ยค
suggest_priority_from_impact() -> int
Suggest a priority from 1 (highest) to 5 (lowest) based on the impact choices.
Source code in src/firefighter/incidents/forms/select_impact.py
timeline ยค
One editable form for the whole incident timeline, shared by both Slack surfaces.
Milestones (Key Events) and status timestamps used to be edited in two different places, with two different field sets: the Key Events message could not touch Investigating/Mitigating/Mitigated, and the correction message only offered the statuses the incident had actually been through - so a step that was never recorded could not be filled in at all. Both surfaces now render this form, which covers every key event that can be corrected, whether or not it was recorded.
The two surfaces keep their own action ids through field_prefix, so a field edited in one message routes back to that message and not to the other.
Classes:
-
IncidentTimelineFormโEvery correctable key event of an incident, in canonical order.
-
KeyEventsTimelineFormโThe same timeline, under the Key Events message's own action ids.
IncidentTimelineForm ยค
Bases: Form
Every correctable key event of an incident, in canonical order.
Two kinds of fields, both keyed to survive round-tripping through Slack's view submission parsing (which strips block/action ids at the first "___" - see slack_view_submission_to_dict):
milestone_<event_type>: same update_or_create/delete semantics asIncidentUpdateKeyEventsForm._save_key_event- these rows aren't unique per status, so "which one to touch" is never ambiguous. Only milestones flaggeduser_editableare offered;Declaredis not one of them, since it is the incident's declaration time.status_<status value>: the timestamp of that status. When the incident went through it, the definitive occurrence is edited in place - the one the post-mortem timeline shows (DEFINITIVE_OCCURRENCE: investigation started at its first occurrence, the mitigation that held is the last one). Earlier occurrences stay as recorded - they are the history of the failed attempts, not a mistake to fix. When it never happened, the field is offered empty and filling it records that step. Such a field is optional; one that already has a time is required, since a transition that happened has to keep some time (drop it from the admin instead).OPENandPOST_MORTEMare never correction targets - see_NON_EDITABLE_STATUSES.
Methods:
-
saveโSave each changed field in place.
Attributes:
-
field_prefix(str) โPrefixed to every field name, so each Slack surface owns its action ids.
Source code in src/firefighter/incidents/forms/timeline.py
field_prefix class-attribute instance-attribute ยค
field_prefix: str = ''
Prefixed to every field name, so each Slack surface owns its action ids.
save ยค
Save each changed field in place.
Source code in src/firefighter/incidents/forms/timeline.py
KeyEventsTimelineForm ยค
Bases: IncidentTimelineForm
The same timeline, under the Key Events message's own action ids.
Methods:
-
saveโSave each changed field in place.
Source code in src/firefighter/incidents/forms/timeline.py
save ยค
Save each changed field in place.
Source code in src/firefighter/incidents/forms/timeline.py
unified_incident ยค
Classes:
-
PlatformChoicesโPlatform choices for incidents.
-
UnifiedIncidentFormโUnified form for all incident types and priorities (P1-P5).
Functions:
-
initial_environmentsโGet default environments.
-
initial_platformโGet default platform.
-
initial_priorityโGet default priority.
PlatformChoices ยค
Bases: TextChoices
Platform choices for incidents.
UnifiedIncidentForm ยค
Bases: CreateIncidentFormBase
Unified form for all incident types and priorities (P1-P5).
This form dynamically shows/hides fields based on: - Priority/response_type (critical vs normal) - Selected impacts (customer, seller, employee)
Common fields (always shown): - title, description, incident_category - environment (multiple choice) - platform (multiple choice, default ALL) - priority (hidden)
Conditional fields: - suggested_team_routing (P4-P5 only) - zendesk_ticket_id (if customer impact selected) - seller_contract_id, is_key_account, etc. (if seller impact selected)
Methods:
-
cleanโCustom validation based on response type and impacts.
-
get_visible_fields_for_impactsโDetermine which fields should be visible based on impacts and response type.
-
trigger_incident_workflowโTrigger unified incident workflow for all priorities.
Source code in src/firefighter/incidents/forms/unified_incident.py
clean ยค
Custom validation based on response type and impacts.
Source code in src/firefighter/incidents/forms/unified_incident.py
get_visible_fields_for_impacts ยค
get_visible_fields_for_impacts(impacts_data: dict[str, ImpactLevel | str], response_type: str) -> list[str]
Determine which fields should be visible based on impacts and response type.
Parameters:
-
impacts_data(dict[str, ImpactLevel | str]) โDictionary of impact type โ impact level (ImpactLevel object or UUID string)
-
response_type(str) โ"critical" or "normal"
Returns:
Source code in src/firefighter/incidents/forms/unified_incident.py
trigger_incident_workflow ยค
trigger_incident_workflow(
creator: User, impacts_data: dict[str, ImpactLevel], response_type: str = "critical", *args: Any, **kwargs: Any
) -> Incident
Trigger unified incident workflow for all priorities.
This unified workflow: 1. Always creates an Incident in the database (P1-P5) 2. Conditionally creates a Slack channel (P1-P3 only) 3. Always creates a Jira ticket linked to the Incident (P1-P5)
Parameters:
-
creator(User) โUser creating the incident
-
impacts_data(dict[str, ImpactLevel]) โDictionary of impact data
-
response_type(str, default:'critical') โ"critical" (P1-P3) or "normal" (P4-P5)
-
*args(Any, default:()) โAdditional positional arguments (unused)
-
**kwargs(Any, default:{}) โAdditional keyword arguments (unused)
Returns:
-
IncidentโThe created Incident object
Source code in src/firefighter/incidents/forms/unified_incident.py
update_key_events ยค
Classes:
IncidentUpdateKeyEventsForm ยค
Bases: Form
Methods:
-
get_milestones_with_dataโGet each firefighter.incidents.models.milestone_type.MilestoneType with its
event_tsfrom the IncidentUpdate. -
saveโCustom save method to save each changed key event.
Source code in src/firefighter/incidents/forms/update_key_events.py
get_milestones_with_data ยค
get_milestones_with_data(milestones_definitions: Iterable[MilestoneType]) -> list[MilestoneTypeData]
Get each firefighter.incidents.models.milestone_type.MilestoneType with its event_ts from the IncidentUpdate.
Source code in src/firefighter/incidents/forms/update_key_events.py
save ยค
Custom save method to save each changed key event.
Source code in src/firefighter/incidents/forms/update_key_events.py
update_roles ยค
Classes:
IncidentUpdateRolesForm ยค
Bases: Form
Methods:
-
saveโCustom save method to save the updated roles.
Source code in src/firefighter/incidents/forms/update_roles.py
save ยค
Custom save method to save the updated roles.
Source code in src/firefighter/incidents/forms/update_roles.py
update_status ยค
Classes:
-
UpdateStatusFormโ
UpdateStatusForm ยค
Bases: Form
Methods:
-
clean_messageโValidate message field, ensuring reopening from MITIGATED has sufficient justification.
-
requires_closure_reasonโCheck if closing this incident to the target status requires a closure reason.
Source code in src/firefighter/incidents/forms/update_status.py
clean_message ยค
clean_message() -> str
Validate message field, ensuring reopening from MITIGATED has sufficient justification.
Source code in src/firefighter/incidents/forms/update_status.py
requires_closure_reason staticmethod ยค
requires_closure_reason(incident: Incident, target_status: IncidentStatus) -> bool
Check if closing this incident to the target status requires a closure reason.
Based on the workflow diagram: - P1/P2 and P3/P4/P5: require reason when closing from Opened or Investigating
Source code in src/firefighter/incidents/forms/update_status.py
utils ยค
Classes:
-
EnumChoiceFieldโ
EnumChoiceField ยค
Bases: TypedChoiceField
Methods:
Source code in src/firefighter/incidents/forms/utils.py
to_python ยค
Return a value from the enum class.
Source code in src/firefighter/incidents/forms/utils.py
management ยค
Modules:
-
commandsโ
commands ยค
Modules:
-
backdate_incident_mitigatedโDjango management command to backdate an incident's mitigated_at timestamp for testing.
-
test_postmortem_remindersโDjango management command to test the incident process reminders.
backdate_incident_mitigated ยค
Django management command to backdate an incident's mitigated_at timestamp for testing.
Classes:
-
CommandโBackdate an incident's mitigated_at timestamp for testing post-mortem reminders.
Command ยค
Bases: BaseCommand
Backdate an incident's mitigated_at timestamp for testing post-mortem reminders.
menus ยค
Functions:
-
user_details_urlโReturn a personalized title for our profile menu item.
migrations ยค
Modules:
-
0006_update_group_namesโ -
0007_update_component_nameโ -
0010_update_componentsโ -
0013_add_missing_componentโ -
0019_set_security_components_privateโ -
0021_copy_component_data_to_incident_categoryโ -
0023_populate_incident_category_referencesโ -
0032_disable_confluence_periodic_tasksโDisable Confluence periodic tasks when ENABLE_CONFLUENCE is False.
-
0036_add_foundations_incident_categoryโMerge the three Marketplace front-end categories into a single "Foundations".
-
0037_link_foundations_slack_usergroupโLink the
Foundationsincident category to its Slack usergroup.
0006_update_group_names ยค
Functions:
-
get_group_mappingsโReturns the mapping table for updating existing groups.
-
get_new_groupsโReturns a dictionary of new groups to be created.
get_group_mappings ยค
get_group_mappings() -> dict
Returns the mapping table for updating existing groups.
Source code in src/firefighter/incidents/migrations/0006_update_group_names.py
0007_update_component_name ยค
Functions:
-
get_component_mappingsโReturns a list of tuples for updating existing component names and their attributes.
-
get_new_componentsโReturns a dictionary of new components to be created.
get_component_mappings ยค
get_component_mappings() -> list
Returns a list of tuples for updating existing component names and their attributes.
Each tuple contains
- old_name (str): The current name of the component.
- new_name (str): The new name to assign to the component.
- slack_channel (str): The associated Slack channel for the component.
- group_name (str): The name of the group to which the component belongs.
Returns:
-
list(list) โA list of tuples, each representing the details for a component update.
Source code in src/firefighter/incidents/migrations/0007_update_component_name.py
get_new_components ยค
get_new_components() -> dict
Returns a dictionary of new components to be created.
Each entry in the dictionary maps a component name to a tuple containing: - group_name: The name of the group the component belongs to. - slack_channel: The associated Slack channel for the component.
Returns:
-
dict(dict) โA mapping of component names to (group name, slack channel) tuples.
Source code in src/firefighter/incidents/migrations/0007_update_component_name.py
0010_update_components ยค
Functions:
-
get_component_mappingsโReturns a list of tuples for updating existing component names and their attributes.
get_component_mappings ยค
get_component_mappings() -> list
Returns a list of tuples for updating existing component names and their attributes.
Each tuple contains
- old_name (str): The current name of the component.
- new_name (str): The new name to assign to the component.
- slack_channel (str): The associated Slack channel for the component.
- group_name (str): The name of the group to which the component belongs.
Returns:
-
list(list) โA list of tuples, each representing the details for a component update.
Source code in src/firefighter/incidents/migrations/0010_update_components.py
0013_add_missing_component ยค
Functions:
-
get_new_componentsโReturns a dictionary of new components to be created.
get_new_components ยค
get_new_components() -> dict
Returns a dictionary of new components to be created.
Each entry in the dictionary maps a component name to a tuple containing: - group_name: The name of the group the component belongs to. - slack_channel: The associated Slack channel for the component.
Returns:
-
dict(dict) โA mapping of component names to (group name, slack channel) tuples.
Source code in src/firefighter/incidents/migrations/0013_add_missing_component.py
0019_set_security_components_private ยค
Functions:
-
revert_security_components_to_publicโRevert all components belonging to the Security group to public, except 'Bot management & rate limiting & WAF'.
-
set_security_components_to_privateโSet all components belonging to the Security group as private, except 'Bot management & rate limiting & WAF'.
revert_security_components_to_public ยค
Revert all components belonging to the Security group to public, except 'Bot management & rate limiting & WAF'.
Source code in src/firefighter/incidents/migrations/0019_set_security_components_private.py
set_security_components_to_private ยค
Set all components belonging to the Security group as private, except 'Bot management & rate limiting & WAF'.
Source code in src/firefighter/incidents/migrations/0019_set_security_components_private.py
0021_copy_component_data_to_incident_category ยค
Functions:
-
copy_component_data_to_incident_categoryโCopy all data from Component to IncidentCategory
-
reverse_copy_component_data_to_incident_categoryโReverse operation - copy IncidentCategory back to Component if needed
copy_component_data_to_incident_category ยค
Copy all data from Component to IncidentCategory
Source code in src/firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py
reverse_copy_component_data_to_incident_category ยค
Reverse operation - copy IncidentCategory back to Component if needed
Source code in src/firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py
0023_populate_incident_category_references ยค
Functions:
-
populate_incident_category_referencesโCopy component references to incident_category references
-
reverse_populate_incident_category_referencesโReverse: copy incident_category references back to component references
populate_incident_category_references ยค
Copy component references to incident_category references
Source code in src/firefighter/incidents/migrations/0023_populate_incident_category_references.py
reverse_populate_incident_category_references ยค
Reverse: copy incident_category references back to component references
Source code in src/firefighter/incidents/migrations/0023_populate_incident_category_references.py
0032_disable_confluence_periodic_tasks ยค
Disable Confluence periodic tasks when ENABLE_CONFLUENCE is False.
Confluence tasks were previously added manually via Django admin. When the Confluence app is disabled, the worker doesn't register these tasks, but Celery Beat (DatabaseScheduler) still dispatches them, causing "Received unregistered task" errors.
This migration disables all Confluence periodic tasks when the app is not enabled, and re-enables them on rollback (assuming Confluence would be re-enabled).
0036_add_foundations_incident_category ยค
Merge the three Marketplace front-end categories into a single "Foundations".
The three teams behind Mobile Apps, Spartacux Foundations and Web performance are now one team, tracked in the FOUN Jira project.
This migration is deliberately NON-DESTRUCTIVE, unlike 0011_update_incidents: the old categories keep every incident already filed against them, so historical per-category counts and IncidentCategoryManager.queryset_with_mtbf stay reproducible. They are only retired from the create pickers via enabled_create=False; the update-status and close forms still offer them so existing incidents keep rendering their own category.
The Slack usergroups and conversations of the three old categories are copied onto Foundations. This is required, not cosmetic: slack.signals.get_users resolves responders through incident.incident_category.usergroups / .conversations, so a category with empty M2Ms would page nobody.
Functions:
-
copy_slack_links_to_foundationsโCopy the usergroup/conversation links of the retired categories onto
-
remove_foundations_categoryโDelete only the row this migration created. Incidents are never repointed by
copy_slack_links_to_foundations ยค
Copy the usergroup/conversation links of the retired categories onto Foundations, so incidents filed against it page the same people.
Source code in src/firefighter/incidents/migrations/0036_add_foundations_incident_category.py
remove_foundations_category ยค
Delete only the row this migration created. Incidents are never repointed by the forward migration, so nothing can be left dangling here.
Source code in src/firefighter/incidents/migrations/0036_add_foundations_incident_category.py
0037_link_foundations_slack_usergroup ยค
Link the Foundations incident category to its Slack usergroup.
0036 created the Foundations category and copied the usergroup/conversation links of the three categories it supersedes, so incidents filed against it already page the right people. This migration points it at the single consolidated usergroup instead.
The Slack usergroup is created by MagicDesk on request (we hold no Slack admin token), so its ID is hardcoded here โ the same approach as 0014_update_components_slack_groups, which pins 60+ usergroup IDs. It is deliberately NOT declared in infra/terraform/groups.yaml: terraform does not manage this group, and claiming it there would make the next terraform apply fail on an already-existing handle.
The usergroups copied by 0036 are left linked. Unlinking them is a separate, reversible decision to take only once this group is confirmed to page the right people.
Functions:
-
unlink_foundations_usergroupโUnlink and delete only the usergroup this migration created.
unlink_foundations_usergroup ยค
Unlink and delete only the usergroup this migration created.
The usergroups copied by 0036 are untouched, so reversing this leaves Foundations paging the superseded teams exactly as it did before.
Source code in src/firefighter/incidents/migrations/0037_link_foundations_slack_usergroup.py
models ยค
Modules:
-
groupโ -
impactโ -
incidentโ -
incident_categoryโ -
incident_costโ -
incident_cost_typeโ -
incident_role_typeโ -
incident_updateโ -
milestone_typeโMilestoneType model.
-
priorityโ -
userโ
group ยค
impact ยค
Classes:
-
Impactโ -
LevelChoicesโ
incident ยค
Classes:
-
Incidentโ -
IncidentFilterSetโSet of filters for incidents, shared by Web UI and API.
-
IncidentManagerโ
Functions:
-
incident_category_filter_choices_querysetโQueryset for choices of IncidentCategories in IncidentFilterSet.
Incident ยค
Bases: Model
Methods:
-
build_invite_listโSend a Django Signal to get the list of users to invite from different integrations (Slack, Confluence, PagerDuty...).
-
compute_metricsโCompute all metrics (time to fix, ...) from events.
-
missing_milestonesโReturns all required Milestones still needed to compute the metrics.
-
update_rolesโUpdate the roles related to an incident, and create an IncidentUpdate.
Attributes:
-
commander(IncidentRole | None) โThe
IncidentRoleholding command, or None if nobody does. -
needs_postmortem(bool) โCheck if incident requires a post-mortem based on priority and environment.
-
status_page_url(str) โSimilar with
get_absolute_urlbut with full domain, to be used out of the website.
commander property ยค
The IncidentRole holding command, or None if nobody does.
Reads from roles_set, so a caller iterating over incidents should prefetch_related("roles_set__role_type", "roles_set__user__slack_user") to avoid a query per incident.
needs_postmortem property ยค
needs_postmortem: bool
Check if incident requires a post-mortem based on priority and environment.
Post-mortem is required if: - Priority requires it (P1/P2) - Environment is PRD - At least one post-mortem system is enabled (Confluence OR Jira)
status_page_url property ยค
status_page_url: str
Similar with get_absolute_url but with full domain, to be used out of the website.
build_invite_list ยค
Send a Django Signal to get the list of users to invite from different integrations (Slack, Confluence, PagerDuty...).
Returns:
Source code in src/firefighter/incidents/models/incident.py
compute_metrics ยค
compute_metrics(*, purge: bool = False) -> None
Compute all metrics (time to fix, ...) from events.
Source code in src/firefighter/incidents/models/incident.py
missing_milestones ยค
Returns all required Milestones still needed to compute the metrics.
Source code in src/firefighter/incidents/models/incident.py
update_roles ยค
update_roles(updater: User, roles_mapping: dict[str, User | None] | dict[str, User]) -> IncidentUpdate
Update the roles related to an incident, and create an IncidentUpdate. For each role, provide a User or None.
This function will update the incident, create an IncidentUpdate, and trigger the incident_updated signal, with update_roles sender.
Parameters:
-
updater(User) โThe user who is updating the roles.
-
roles_mapping(dict[str, User | None]) โA dict of roles to update, with the new User or None. Defaults to None.
Returns:
-
IncidentUpdate(IncidentUpdate) โThe created IncidentUpdate with the updated roles.
Source code in src/firefighter/incidents/models/incident.py
IncidentFilterSet ยค
Bases: FilterSet
Set of filters for incidents, shared by Web UI and API.
Methods:
-
incident_searchโSearch incidents by title, description, and ID.
incident_search staticmethod ยค
Search incidents by title, description, and ID.
Parameters:
-
queryset(QuerySet[Incident]) โQueryset to search in.
-
_name(str) โ -
value(str) โValue to search for.
Returns:
Source code in src/firefighter/incidents/models/incident.py
IncidentManager ยค
Bases: Manager['Incident']
Methods:
-
declareโCreate an Incident and its first IncidentUpdate.
-
searchโSearch for incidents using a search term, on the title and description fields.
declare ยค
Create an Incident and its first IncidentUpdate. Send the incident_created signal. Returns the saved incident, no need to .save().
Source code in src/firefighter/incidents/models/incident.py
search staticmethod ยค
Search for incidents using a search term, on the title and description fields.
Parameters:
-
queryset(QuerySet[Incident] | None) โQueryset to search in. If None, search in all incidents. The Queryset allows to search on a subset of incidents (already filtered).
-
search_term(str) โSearch term.
Returns:
-
tuple[QuerySet[Incident], bool]โtuple[QuerySet[Incident], bool]: Queryset of incidents matching the search term, and a boolean indicating if the search may contain duplicates objects.
Source code in src/firefighter/incidents/models/incident.py
incident_category_filter_choices_queryset ยค
Queryset for choices of IncidentCategories in IncidentFilterSet. Moved it as a function because models are not loaded when creating filters.
Source code in src/firefighter/incidents/models/incident.py
incident_category ยค
Classes:
-
IncidentCategoryFilterSetโSet of filters for IncidentCategory, share by Web UI and API.
-
IncidentCategoryManagerโ
IncidentCategoryFilterSet ยค
Bases: FilterSet
Set of filters for IncidentCategory, share by Web UI and API.
Methods:
-
incident_category_searchโSearch incident categories by title, description, and ID.
incident_category_search staticmethod ยค
incident_category_search(queryset: QuerySet[IncidentCategory], _name: str, value: str) -> QuerySet[IncidentCategory]
Search incident categories by title, description, and ID.
Parameters:
-
queryset(QuerySet[IncidentCategory]) โQueryset to search in.
-
_name(str) โ -
value(str) โValue to search for.
Returns:
-
QuerySet[IncidentCategory]โQuerySet[IncidentCategory]: Search results.
Source code in src/firefighter/incidents/models/incident_category.py
IncidentCategoryManager ยค
Bases: Manager['IncidentCategory']
Methods:
-
queryset_with_mtbfโReturns a queryset of incident categories with an additional
mtbffield.
queryset_with_mtbf ยค
queryset_with_mtbf(
date_from: datetime,
date_to: datetime,
queryset: QuerySet[IncidentCategory] | None = None,
metric_type: str = "time_to_fix",
field_name: str = "mtbf",
) -> QuerySet[IncidentCategory]
Returns a queryset of incident categories with an additional mtbf field.
Source code in src/firefighter/incidents/models/incident_category.py
incident_cost ยค
Classes:
-
IncidentCostโIncident Cost is inspired from dispatch.
incident_cost_type ยค
Classes:
-
IncidentCostTypeโIncident Cost Type is inspired from dispatch.
incident_role_type ยค
Attributes:
-
COMMANDER_ROLE_SLUGโSlug of the role that owns driving the incident process through to closure.
COMMANDER_ROLE_SLUG module-attribute ยค
Slug of the role that owns driving the incident process through to closure.
incident_update ยค
Classes:
-
IncidentUpdateโIncidentUpdate represents a single update to an incident.
Functions:
-
set_event_tsโAdd a timestamp on every IncidentUpdate.
IncidentUpdate ยค
Bases: Model
IncidentUpdate represents a single update to an incident. One incident can have many incident updates. Only updated fields are stored in the incident update.
set_event_ts ยค
set_event_ts(sender: Any, instance: IncidentUpdate, **kwargs: Any) -> None
Add a timestamp on every IncidentUpdate. Not implemented using Django ORM auto_add as it would not make them user editable.
Source code in src/firefighter/incidents/models/incident_update.py
milestone_type ยค
MilestoneType model.
Classes:
-
MilestoneTypeโRepresents a milestone type, also known as a key event time or event time.
priority ยค
user ยค
observability ยค
Observability signals: emit structured log events for Datadog metrics and dashboards.
Listens to incident lifecycle signals and emits logs with consistent attributes that Datadog can use for log-based metrics, facets, and dashboards.
Log attributes follow Datadog naming conventions: - ff.metric: metric name (for log-based metric creation) - ff.incident_id: incident ID - ff.priority: priority value (1-5) - ff.status: incident status - ff.integration: integration name (jira, slack, confluence, pagerduty)
signals ยค
Attributes:
-
create_incident_conversationโSignal sent to create a conversation for an incident.
-
get_invitesโSignal sent to retrieve the list of users to invite for an incident.
-
incident_closedโSignal sent when an incident is closed
-
incident_createdโSignal sent when an incident is created
-
incident_key_events_updatedโSignal sent when an incident's key events are updated.
-
incident_updatedโSignal sent when an incident is updated.
-
postmortem_createdโSignal sent when a postmortem is created.
create_incident_conversation module-attribute ยค
Signal sent to create a conversation for an incident.
Parameters:
-
incident(Incident) โThe incident for which to create a conversation.
get_invites module-attribute ยค
incident_closed module-attribute ยค
Signal sent when an incident is closed
Parameters:
-
sender(Any) โThe sender of the signal name
-
incident(Incident) โThe incident that was closed
incident_created module-attribute ยค
Signal sent when an incident is created
Parameters:
-
incident(Incident) โThe incident that was created
incident_key_events_updated module-attribute ยค
Signal sent when an incident's key events are updated.
Parameters:
-
incident(Incident) โThe incident that was updated
incident_updated module-attribute ยค
Signal sent when an incident is updated.
Parameters:
-
sender(str | Any) โThe sender of the signal name
-
incident(Incident) โThe incident that was updated
-
incident_update(IncidentUpdate) โThe incident update that was created
-
update_fields(list[str]) โThe fields that were updated
-
old_priority(Priority) โThe old priority of the incident (optional kwarg)
tasks ยค
Modules:
-
updateoncallโ
updateoncall ยค
Functions:
-
update_oncallโFetch current on-calls and update the on-call Slack topic and Confluence page.
-
update_oncall_slack_topicโTODO(gab) Move in the Slack app.
-
update_oncall_viewsโUpdates the on-call Slack topic and Confluence page containing the info for the on-call personnel.
update_oncall ยค
Fetch current on-calls and update the on-call Slack topic and Confluence page.
Source code in src/firefighter/incidents/tasks/updateoncall.py
update_oncall_slack_topic ยค
TODO(gab) Move in the Slack app.
Source code in src/firefighter/incidents/tasks/updateoncall.py
update_oncall_views ยค
Updates the on-call Slack topic and Confluence page containing the info for the on-call personnel.
Source code in src/firefighter/incidents/tasks/updateoncall.py
timeline ยค
The incident timeline: its canonical steps, and the checks they must satisfy.
Kept out of the Slack layer because three surfaces render the same seven steps (the Post-mortem review checkpoint, the Key Events message and the timeline correction message) and the Jira post-mortem is built from the same data. The rendering differs; the definition of "the timeline" must not.
Classes:
-
TimelineEntryโ
Functions:
-
find_timeline_issuesโChecks the recorded steps against the expected chronology.
-
format_deltaโHuman-readable duration, keeping the two most significant units.
-
get_canonical_stepsโOne step per
EXPECTED_STEPSentry, collapsing reopen cycles. -
get_incident_timelineโStarted/Detected milestones (if recorded), followed by the status timeline.
-
get_status_timelineโEvery status transition, in chronological order, including reopen cycles.
-
milestone_timestampsโThe recorded
event_tsof each milestone, newest row winning per type.
Attributes:
-
CANONICAL_MILESTONE_EVENT_TYPES(tuple[str, ...]) โEvery milestone of the canonical timeline, wherever it sits in the sequence.
-
MILESTONE_EVENT_TYPES(tuple[tuple[str, str], ...]) โThe milestones that precede the declaration, in
get_incident_timeline.
CANONICAL_MILESTONE_EVENT_TYPES module-attribute ยค
CANONICAL_MILESTONE_EVENT_TYPES: tuple[str, ...] = tuple(
source for _label, _emoji, source, _required in EXPECTED_STEPS if isinstance(source, str)
)
Every milestone of the canonical timeline, wherever it sits in the sequence.
MILESTONE_EVENT_TYPES module-attribute ยค
MILESTONE_EVENT_TYPES: tuple[tuple[str, str], ...] = (('started', 'Started'), ('detected', 'Detected'))
The milestones that precede the declaration, in get_incident_timeline.
TimelineEntry ยค
Bases: NamedTuple
Attributes:
find_timeline_issues ยค
Checks the recorded steps against the expected chronology.
Reports steps that break the order, required steps with no recorded time, and times in the future - the three ways a hand-typed timeline goes wrong.
Source code in src/firefighter/incidents/timeline.py
format_delta ยค
Human-readable duration, keeping the two most significant units.
Source code in src/firefighter/incidents/timeline.py
get_canonical_steps ยค
One step per EXPECTED_STEPS entry, collapsing reopen cycles.
A reopened incident goes through Investigating/Mitigating/Mitigated more than once. Listing every occurrence makes the sequence impossible to check against the expected order and buries the definitive times, so each step keeps a single timestamp and carries its occurrence count instead.
Source code in src/firefighter/incidents/timeline.py
get_incident_timeline ยค
get_incident_timeline(incident: Incident) -> list[TimelineEntry]
Started/Detected milestones (if recorded), followed by the status timeline.
Milestones missing a recorded event_ts are still included, with event_ts=None, so the reviewer notices the gap rather than the milestone silently disappearing from the list. The milestone group itself is sorted chronologically (Detected can legitimately be recorded before Started, e.g. an automated alert fires before the actual start is pinpointed) - unrecorded milestones sort last within the group, since there's no time to place them by. The group as a whole always leads the status timeline, since milestones routinely predate the incident being declared in FireFighter. Only the first OPEN entry (the one anchored to incident.created_at) is relabeled "Declared" - a later OPEN entry is a genuine reopen and keeps reading "Open".
Unlike get_canonical_steps, this keeps every occurrence: it is the full audit trail, not the seven-step summary.
Source code in src/firefighter/incidents/timeline.py
get_status_timeline ยค
get_status_timeline(incident: Incident) -> list[tuple[IncidentStatus, datetime]]
Every status transition, in chronological order, including reopen cycles.
The declaration OPEN row always anchors to incident.created_at (the incident's true start), never to whichever OPEN row happens to be latest - so it's added explicitly rather than trusted from the DB row. An incident can legitimately go back to OPEN later (a real reopen); that row has a different event_ts than created_at and is kept, so it shows up as its own entry rather than being silently dropped. Every other status contributes one entry per row - a status revisited after a reopen (MITIGATED -> INVESTIGATING/MITIGATING) shows every visit, matching the full audit trail rendered in the Jira post-mortem timeline.
Source code in src/firefighter/incidents/timeline.py
milestone_timestamps ยค
The recorded event_ts of each milestone, newest row winning per type.
Explicit ordering: without it Meta.ordering ("-event_ts") applies and dict() would keep the oldest row per type, so two surfaces reading the same milestone would disagree.
Source code in src/firefighter/incidents/timeline.py
views ยค
Modules:
-
componentsโ -
date_filterโ -
date_utilsโ -
errorsโFrom Django default errors views.
-
usersโ -
viewsโ
components ยค
Modules:
-
listโ
list ยค
Classes:
IncidentCategoriesViewList ยค
Bases: SingleTableMixin, FilterView
Methods:
-
get_context_dataโNo *args to pass.
get_context_data ยค
No *args to pass.
Source code in src/firefighter/incidents/views/components/list.py
date_filter ยค
Functions:
-
get_date_range_from_special_dateโTODO Specify the year parameter (ISO or calendar).
-
parse_momentโReturns a :class:
slicewith the :func:slice.startand :func:slice.stopof the given moment.
get_date_range_from_special_date ยค
get_date_range_from_special_date(unparsed_date: str) -> tuple[datetime | None, datetime | None, str | None, str | None]
TODO Specify the year parameter (ISO or calendar).
Source code in src/firefighter/incidents/views/date_filter.py
parse_moment ยค
Returns a :class:slice with the :func:slice.start and :func:slice.stop of the given moment. If the moment is not a valid moment, returns None. If the moment is a point in time, returns a slice with the same start and stop.
Source code in src/firefighter/incidents/views/date_filter.py
date_utils ยค
Functions:
-
get_bounds_from_calendar_yearโCalendar Year.
-
get_bounds_from_yearโISO Year.
get_bounds_from_calendar_year ยค
Calendar Year.
Source code in src/firefighter/incidents/views/date_utils.py
get_bounds_from_year ยค
ISO Year.
Source code in src/firefighter/incidents/views/date_utils.py
errors ยค
From Django default errors views. https://github.com/django/django/blob/0dd29209091280ccf34e07c9468746c396b7778e/django/views/defaults.py.
Functions:
-
bad_requestโ400 error handler.
-
page_not_foundโDefault 404 handler.
-
permission_deniedโPermission denied (403) handler.
-
server_errorโ500 error handler.
bad_request ยค
bad_request(request: HttpRequest, exception: Exception, template_name: str = ERROR_400_TEMPLATE_NAME) -> HttpResponse
400 error handler.
Templates: :template:400.html Context: None
Source code in src/firefighter/incidents/views/errors.py
page_not_found ยค
page_not_found(request: HttpRequest, exception: Exception, template_name: str = ERROR_404_TEMPLATE_NAME) -> HttpResponse
Default 404 handler.
Templates: :template:404.html Context: request_path The path of the requested URL (e.g., '/app/pages/bad_page/'). It's quoted to prevent a content injection attack. exception The message from the exception which triggered the 404 (if one was supplied), or the exception class name
Source code in src/firefighter/incidents/views/errors.py
permission_denied ยค
permission_denied(request: HttpRequest, exception: Exception, template_name: str = ERROR_403_TEMPLATE_NAME) -> HttpResponse
Permission denied (403) handler.
Templates: :template:403.html Context: exception The message from the exception which triggered the 403 (if one was supplied).
If the template does not exist, an HTTP 403 response containing the text "403 Forbidden" (as per RFC 7231) will be returned.
Source code in src/firefighter/incidents/views/errors.py
server_error ยค
server_error(request: HttpRequest, template_name: str = ERROR_500_TEMPLATE_NAME) -> HttpResponse
500 error handler.
Templates: :template:500.html Context: None
Source code in src/firefighter/incidents/views/errors.py
users ยค
Modules:
-
detailsโ
details ยค
Classes:
-
UserDetailViewโIn this view, be extra careful.
UserDetailView ยค
Bases: CustomDetailView[User]
In this view, be extra careful.
In this context, user is the logged_in user and target_user is the user of the profile being viewed.
views ยค
Classes:
-
DashboardViewโ -
IncidentListViewโ -
IncidentStatisticsViewโ -
IncidentUpdateKeyEventsViewโ -
ProcessAfterResponseโCustom Response, to trigger the Slack workflow after creating the incident and returning HTTP 201.
Functions:
-
incident_export_presetsโExtra entries for the incident export dropdown, with pinned column order.
DashboardView ยค
Bases: ListView[Incident]
Methods:
-
get_context_dataโNo *args to pass.
get_context_data ยค
No *args to pass.
Source code in src/firefighter/incidents/views/views.py
IncidentListView ยค
Bases: SingleTableMixin, FilterView
Methods:
-
get_context_dataโNo *args to pass.
get_context_data ยค
No *args to pass.
Source code in src/firefighter/incidents/views/views.py
IncidentStatisticsView ยค
Bases: FilterView
Methods:
-
get_context_dataโNo *args to pass.
get_context_data ยค
No *args to pass.
Source code in src/firefighter/incidents/views/views.py
IncidentUpdateKeyEventsView ยค
Bases: SingleObjectMixin[Incident], LoginRequiredMixin, FormView[IncidentUpdateKeyEventsForm]
Methods:
-
get_formโReplace Markdown bold syntax with HTML bold syntax in form labels.
get_form ยค
get_form(form_class: type[IncidentUpdateKeyEventsForm] | None = None) -> IncidentUpdateKeyEventsForm
Replace Markdown bold syntax with HTML bold syntax in form labels.
Source code in src/firefighter/incidents/views/views.py
ProcessAfterResponse ยค
Bases: HttpResponseRedirect
Custom Response, to trigger the Slack workflow after creating the incident and returning HTTP 201.
TODO This does not work, the workflow is triggered before the response is sent. We need to do a celery task! TODO We need to redirect to the incident page or Slack conversation.
Source code in src/firefighter/incidents/views/views.py
incident_export_presets ยค
Extra entries for the incident export dropdown, with pinned column order.
The (Full) options use ?fields=__all__, which sorts the serializer's fields alphabetically โ so adding a field shifts every column after it and silently breaks consumers that map by position. These presets pass an explicit field list instead, which the renderer emits verbatim.
Imported lazily: firefighter.api imports firefighter.incidents, so a module-level import here would create a cycle.