mirror of
https://github.com/boostorg/website-v2.git
synced 2026-01-19 04:42:17 +00:00
97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
from datetime import timedelta
|
|
|
|
import requests
|
|
from django.contrib.postgres.fields import DateRangeField
|
|
from django.db import models
|
|
from django.db.backends.postgresql.psycopg_any import DateRange
|
|
from django_extensions.db.models import TimeStampedModel
|
|
|
|
from reports.constants import WEB_ANALYTICS_API_URL
|
|
from versions.models import Version
|
|
|
|
INCLUSIVE = "[]"
|
|
|
|
|
|
class WebsiteStatReport(TimeStampedModel):
|
|
version = models.OneToOneField(Version, on_delete=models.CASCADE)
|
|
period = DateRangeField()
|
|
|
|
def __str__(self):
|
|
return f"Stat report for {self.version}"
|
|
|
|
def save(self, **kwargs):
|
|
"""Allow creation of reports while omitting period and/or version"""
|
|
if self.version_id is None:
|
|
self.version = Version.objects.most_recent()
|
|
if not self.period:
|
|
previous_version = (
|
|
Version.objects.filter(
|
|
beta=False, release_date__lt=self.version.release_date
|
|
)
|
|
.order_by("-release_date")
|
|
.first()
|
|
)
|
|
start_date = previous_version.release_date + timedelta(days=1)
|
|
self.period = DateRange(start_date, self.version.release_date, INCLUSIVE)
|
|
super().save(**kwargs)
|
|
|
|
@property
|
|
def analytics_api_url(self) -> str:
|
|
return WEB_ANALYTICS_API_URL.format(self.period.lower, self.period.upper)
|
|
|
|
def populate_from_api(self):
|
|
"""Fetch stats from API and generate child WebsiteStatItem instances."""
|
|
|
|
response = requests.get(self.analytics_api_url)
|
|
data = response.json()
|
|
|
|
if not data or "top_stats" not in data:
|
|
raise ValueError(f"Invalid Plausible API response: {data}")
|
|
|
|
# Clear existing stat items
|
|
WebsiteStatItem.objects.filter(report=self).delete()
|
|
|
|
stat_items = []
|
|
|
|
for stat_data in data["top_stats"]:
|
|
stat = WebsiteStatItem(
|
|
report=self,
|
|
name=stat_data["name"],
|
|
value=stat_data["value"],
|
|
code_name=stat_data["graph_metric"],
|
|
)
|
|
stat_items.append(stat)
|
|
|
|
WebsiteStatItem.objects.bulk_create(stat_items)
|
|
|
|
|
|
class WebsiteStatItem(TimeStampedModel):
|
|
"""Individual stat item (e.g. unique visitors)"""
|
|
|
|
report = models.ForeignKey(
|
|
WebsiteStatReport, on_delete=models.CASCADE, related_name="stats"
|
|
)
|
|
name = models.CharField()
|
|
code_name = models.CharField()
|
|
value = models.FloatField()
|
|
|
|
def __str__(self):
|
|
return f"{self.report.version} {self.name}"
|
|
|
|
@property
|
|
def formatted_value(self) -> str:
|
|
"""Format value based on metric type"""
|
|
if self.code_name == "visit_duration":
|
|
minutes, seconds = divmod(int(self.value), 60)
|
|
return f"{minutes}m {seconds}s"
|
|
elif self.code_name == "bounce_rate":
|
|
return f"{self.value}%"
|
|
return str(self.value)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=["report", "code_name"], name="unique_report_code_name"
|
|
)
|
|
]
|