oncall-engine/engine/apps/oss_installation/usage_stats.py

54 lines
1.6 KiB
Python
Raw Permalink Normal View History

import logging
import platform
from dataclasses import asdict, dataclass
import requests
from django.conf import settings
from django.db.models import Sum
from apps.alerts.models import AlertGroupCounter
from apps.oss_installation.utils import active_oss_users_count
USAGE_STATS_URL = "https://stats.grafana.org/oncall-usage-report"
USAGE_STATS_HTTP_TIMEOUT = 500
logger = logging.getLogger(__name__)
@dataclass
class UsageStatsReport:
version: str
os: str
arch: str
usage_stats_id: str
metrics: dict
class UsageStatsService:
def get_usage_stats_report(self):
`apps.get_model` -> `import` (#2619) # What this PR does Remove [`apps.get_model`](https://docs.djangoproject.com/en/3.2/ref/applications/#django.apps.apps.get_model) invocations and use inline `import` statements in places where models are imported within functions/methods to avoid circular imports. I believe `import` statements are more appropriate for most use cases as they allow for better static code analysis & formatting, and solve the issue of circular imports without being unnecessarily dynamic as `apps.get_model`. With `import` statements, it's possible to: - Jump to model definitions in most IDEs - Automatically sort inline imports with `isort` - Find import errors faster/easier (most IDEs highlight broken imports) - Have more consistency across regular & inline imports when importing models This PR also adds a flake8 rule to ban imports of `django.apps.apps`, so it's harder to use `apps.get_model` by mistake (it's possible to ignore this rule by using `# noqa: I251`). The rule is not enforced on directories with migration files, because `apps.get_model` is often used to get a historical state of a model, which is useful when writing migrations ([see this SO answer for more details](https://stackoverflow.com/a/37769213)). So `apps.get_model` is considered OK in migrations (even necessary in some cases). ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required)
2023-07-25 10:43:23 +01:00
from apps.oss_installation.models import OssInstallation
metrics = {}
metrics["active_users_count"] = active_oss_users_count()
total_alert_groups = AlertGroupCounter.objects.aggregate(Sum("value")).get("value__sum", None)
if total_alert_groups is None:
total_alert_groups = 0
metrics["alert_groups_count"] = total_alert_groups
usage_stats_id = OssInstallation.objects.get_or_create()[0].installation_id
return UsageStatsReport(
usage_stats_id=str(usage_stats_id),
os=platform.system(),
arch=platform.machine(),
version=settings.VERSION,
metrics=metrics,
)
def send_usage_stats_report(self):
report = self.get_usage_stats_report()
try:
requests.post(url=USAGE_STATS_URL, json=asdict(report), timeout=USAGE_STATS_HTTP_TIMEOUT)
except requests.exceptions.RequestException as e:
logging.info(f"Failed to send_usage_stats_report. msg={str(e)}")