oncall-engine/engine/apps/api/serializers/alert_group.py
Michael Derynck 6b40f95033 World, meet OnCall!
Co-authored-by: Eve832 <eve.meelan@grafana.com>
    Co-authored-by: Francisco Montes de Oca <nevermind89x@gmail.com>
    Co-authored-by: Ildar Iskhakov <ildar.iskhakov@grafana.com>
    Co-authored-by: Innokentii Konstantinov <innokenty.konstantinov@grafana.com>
    Co-authored-by: Julia <ferril.darkdiver@gmail.com>
    Co-authored-by: maskin25 <kengurek@gmail.com>
    Co-authored-by: Matias Bordese <mbordese@gmail.com>
    Co-authored-by: Matvey Kukuy <motakuk@gmail.com>
    Co-authored-by: Michael Derynck <michael.derynck@grafana.com>
    Co-authored-by: Richard Hartmann <richih@richih.org>
    Co-authored-by: Robby Milo <robbymilo@fastmail.com>
    Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com>
    Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
    Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2022-06-03 08:09:47 -06:00

195 lines
7.1 KiB
Python

import logging
from datetime import datetime
import humanize
from rest_framework import serializers
from apps.alerts.incident_appearance.renderers.web_renderer import AlertGroupWebRenderer
from apps.alerts.models import AlertGroup
from common.api_helpers.mixins import EagerLoadingMixin
from .alert import AlertSerializer
from .alert_receive_channel import FastAlertReceiveChannelSerializer
from .user import FastUserSerializer
logger = logging.getLogger(__name__)
class ShortAlertGroupSerializer(serializers.ModelSerializer):
pk = serializers.CharField(read_only=True, source="public_primary_key")
alert_receive_channel = FastAlertReceiveChannelSerializer(source="channel")
render_for_web = serializers.SerializerMethodField()
class Meta:
model = AlertGroup
fields = ["pk", "render_for_web", "alert_receive_channel", "inside_organization_number"]
def get_render_for_web(self, obj):
return AlertGroupWebRenderer(obj).render()
class AlertGroupSerializer(EagerLoadingMixin, serializers.ModelSerializer):
"""
Attention: It's heavily cached. Make sure to invalidate alertgroup's web cache if you update the format!
"""
pk = serializers.CharField(read_only=True, source="public_primary_key")
alert_receive_channel = FastAlertReceiveChannelSerializer(source="channel")
alerts = serializers.SerializerMethodField("get_limited_alerts")
resolved_by_verbose = serializers.CharField(source="get_resolved_by_display")
resolved_by_user = FastUserSerializer(required=False)
acknowledged_by_user = FastUserSerializer(required=False)
silenced_by_user = FastUserSerializer(required=False)
related_users = serializers.SerializerMethodField()
last_alert_at = serializers.SerializerMethodField()
started_at_verbose = serializers.SerializerMethodField()
acknowledged_at_verbose = serializers.SerializerMethodField()
resolved_at_verbose = serializers.SerializerMethodField()
silenced_at_verbose = serializers.SerializerMethodField()
dependent_alert_groups = ShortAlertGroupSerializer(many=True)
root_alert_group = ShortAlertGroupSerializer()
alerts_count = serializers.ReadOnlyField()
status = serializers.ReadOnlyField()
render_for_web = serializers.SerializerMethodField()
PREFETCH_RELATED = [
"alerts",
"dependent_alert_groups",
"log_records",
"log_records__author",
"log_records__escalation_policy",
"log_records__invitation__invitee",
]
SELECT_RELATED = [
"slack_message",
"channel__organization",
"slack_message___slack_team_identity",
"acknowledged_by_user",
"resolved_by_user",
"silenced_by_user",
]
class Meta:
model = AlertGroup
fields = [
"pk",
"alerts_count",
"inside_organization_number",
"verbose_name",
"alert_receive_channel",
"resolved",
"resolved_by",
"resolved_by_verbose",
"resolved_by_user",
"resolved_at",
"acknowledged_at",
"acknowledged",
"acknowledged_on_source",
"acknowledged_at",
"acknowledged_by_user",
"silenced",
"silenced_by_user",
"silenced_at",
"silenced_at_verbose",
"silenced_until",
"started_at",
"last_alert_at",
"silenced_until",
"permalink",
"alerts",
"related_users",
"started_at_verbose",
"acknowledged_at_verbose",
"resolved_at_verbose",
"render_for_web",
"render_after_resolve_report_json",
"dependent_alert_groups",
"root_alert_group",
"status",
]
def get_last_alert_at(self, obj):
last_alert = obj.alerts.last()
# TODO: This is a Hotfix for 0.0.27
if last_alert is None:
logger.warning(f"obj {obj} doesn't have last_alert!")
return ""
return str(last_alert.created_at)
def get_limited_alerts(self, obj):
"""
Overriding default alerts because there are alert_groups with thousands of them.
It's just too slow, we need to cut here.
"""
alerts = obj.alerts.all()[:100]
if len(alerts) > 90:
for alert in alerts:
alert.title = str(alert.title) + " Only last 100 alerts are shown. Use Amixr API to fetch all of them."
return AlertSerializer(alerts, many=True).data
def get_related_users(self, obj):
users_ids = set()
users = []
# add resolved and acknowledged by_user explicitly because logs are already prefetched
# when def acknowledge/resolve are called in view.
if obj.resolved_by_user:
users_ids.add(obj.resolved_by_user.public_primary_key)
users.append(obj.resolved_by_user.short())
if obj.acknowledged_by_user and obj.acknowledged_by_user.public_primary_key not in users_ids:
users_ids.add(obj.acknowledged_by_user.public_primary_key)
users.append(obj.acknowledged_by_user.short())
if obj.silenced_by_user and obj.silenced_by_user.public_primary_key not in users_ids:
users_ids.add(obj.silenced_by_user.public_primary_key)
users.append(obj.silenced_by_user.short())
for log_record in obj.log_records.all():
if log_record.author is not None and log_record.author.public_primary_key not in users_ids:
users.append(log_record.author.short())
users_ids.add(log_record.author.public_primary_key)
return users
def get_started_at_verbose(self, obj):
started_at_verbose = None
if obj.started_at is not None:
started_at_verbose = humanize.naturaltime(
datetime.now().replace(tzinfo=None) - obj.started_at.replace(tzinfo=None)
)
return started_at_verbose
def get_acknowledged_at_verbose(self, obj):
acknowledged_at_verbose = None
if obj.acknowledged_at is not None:
acknowledged_at_verbose = humanize.naturaltime(
datetime.now().replace(tzinfo=None) - obj.acknowledged_at.replace(tzinfo=None)
) # TODO: Deal with timezones
return acknowledged_at_verbose
def get_resolved_at_verbose(self, obj):
resolved_at_verbose = None
if obj.resolved_at is not None:
resolved_at_verbose = humanize.naturaltime(
datetime.now().replace(tzinfo=None) - obj.resolved_at.replace(tzinfo=None)
) # TODO: Deal with timezones
return resolved_at_verbose
def get_silenced_at_verbose(self, obj):
silenced_at_verbose = None
if obj.silenced_at is not None:
silenced_at_verbose = humanize.naturaltime(
datetime.now().replace(tzinfo=None) - obj.silenced_at.replace(tzinfo=None)
) # TODO: Deal with timezones
return silenced_at_verbose
def get_render_for_web(self, obj):
return AlertGroupWebRenderer(obj).render()