"""Models."""
from functools import partial
from typing import Any
from uuid import uuid4
from django.conf import settings
from django.db import models
from django.utils import timezone
from django_better_admin_arrayfield.models.fields import ArrayField
from controller.sentry.choices import EventType, MetricType
from controller.sentry.metrics import celery_merger, wsgi_merger
[docs]def settings_default_value(key: str) -> Any:
"""This function returns the default value from settings.
Args:
key (str): The key to retrieve from settings.
Returns:
Any: The settings.
"""
return getattr(settings, key)
MERGER = {MetricType.WSGI: wsgi_merger, MetricType.CELERY: celery_merger}
[docs]class Project(models.Model):
"""Project Models."""
sentry_id = models.CharField(primary_key=True, max_length=50)
sentry_project_slug = models.CharField(max_length=50, db_index=True, null=True, blank=True)
detection_param = models.JSONField(default=partial(settings_default_value, "DEFAULT_SPIKE_DETECTION_PARAM"))
detection_result = models.JSONField(blank=True, null=True)
last_event = models.ForeignKey("Event", null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
def __str__(self) -> str:
"""Return Project string."""
return f"Project({self.sentry_project_slug if self.sentry_project_slug else 'Pending'})"
[docs]class Event(models.Model):
"""Event Models."""
reference = models.UUIDField(primary_key=True, default=uuid4)
type = models.CharField(choices=EventType.choices, max_length=10)
timestamp = models.DateTimeField()
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="events")
class Meta:
"""Meta Class of Event."""
ordering = ["timestamp"]
[docs]class App(models.Model):
"""App Models."""
reference = models.CharField(primary_key=True, max_length=256)
last_seen = models.DateTimeField(null=True, blank=True)
default_sample_rate = models.FloatField(default=settings.DEFAULT_SAMPLE_RATE)
active_sample_rate = models.FloatField(default=settings.DEFAULT_SAMPLE_RATE)
active_window_end = models.DateTimeField(null=True, blank=True)
# Sentry Api
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.SET_NULL, related_name="apps")
env = models.CharField(max_length=50, null=True, blank=True)
command = models.CharField(max_length=50, null=True, blank=True)
# WSGI
wsgi_ignore_path = ArrayField(
models.CharField(max_length=50, blank=True),
blank=True,
default=partial(settings_default_value, "DEFAULT_WSGI_IGNORE_PATHS"),
help_text="A list of path to ignore, matched using fill match ==",
)
wsgi_ignore_user_agent = ArrayField(
models.CharField(max_length=50, blank=True),
blank=True,
default=partial(
settings_default_value,
"DEFAULT_WSGI_IGNORE_USER_AGENT",
),
help_text="A list of user agent to ignore, matched with startswith",
)
wsgi_collect_metrics = models.BooleanField(default=False, verbose_name="wsgi metrics")
wsgi_metrics = models.JSONField(null=True, blank=True)
# celery
celery_ignore_task = ArrayField(
models.CharField(max_length=50, blank=True),
blank=True,
default=partial(settings_default_value, "DEFAULT_CELERY_IGNORE_TASKS"),
)
celery_collect_metrics = models.BooleanField(default=False, verbose_name="celery metrics")
celery_metrics = models.JSONField(null=True, blank=True)
def __str__(self) -> str:
"""Return App as a string."""
return f"App<{self.reference}>"
[docs] def merge(self, validated_data: dict):
"""Merge Metrics.
Args:
validated_data (dict) : new data
"""
merger = MERGER[validated_data["type"]]
merger(self, validated_data["data"])
self.last_seen = timezone.now()
[docs] def get_metric(self, metric_type: MetricType) -> tuple[bool, dict]:
"""Get metrics from :class:`controller.sentry.choices.MetricType`.
Args:
metric_type (MetricType): Metric type
Returns:
bool: Is collecting metrics
dict: The metric value
"""
prefix = metric_type.value.lower()
return getattr(self, f"{prefix}_collect_metrics"), getattr(self, f"{prefix}_metrics")
[docs] def set_metric(self, metric_type: MetricType, metric_state: bool):
"""Set metrics.
Args:
metric_type (MetricType): Metric type
metric_state (bool): Should collect metrics
"""
prefix = metric_type.value.lower()
setattr(self, f"{prefix}_collect_metrics", metric_state)
class Meta:
"""Meta Class of App."""
permissions = [
("bump_sample_rate_app", "Can bump sample rate"),
("panic_app", "Panic! Set all sample rate to 0"),
("enable_disable_metrics_app", "Can Enable/Disable metrics"),
]