diff --git a/reports/constants.py b/reports/constants.py index 4549caf83..06501eeeb 100644 --- a/reports/constants.py +++ b/reports/constants.py @@ -4,3 +4,23 @@ f"https://plausible.io/api/stats/{WEB_ANALYTICS_DOMAIN}/top-stats/?period=custom" "&from={:%Y-%m-%d}&to={:%Y-%m-%d}" ) +WEB_ANALYTICS_API_URL_V2 = "https://plausible.io/api/v2/query" +WEB_ANALYSTICS_API_TOP_STATS_PAYLOAD = { + "site_id": WEB_ANALYTICS_DOMAIN, + "metrics": [ + "visitors", + "pageviews", + "bounce_rate", + "visit_duration", + "views_per_visit", + "visits", + ], +} +WEB_ANALYTICS_CODENAME_MAPPING = { + "visitors": "Unique Visitors", + "pageviews": "Total Page pageviews", + "bounce_rate": "Bounce Rate", + "visit_duration": "Visit Duration", + "views_per_visit": "Views per visit", + "visits": "Total visits", +} diff --git a/reports/models.py b/reports/models.py index af5f302e7..8470edca6 100644 --- a/reports/models.py +++ b/reports/models.py @@ -1,16 +1,24 @@ from datetime import timedelta import requests +import structlog from django.contrib.postgres.fields import DateRangeField +from django.conf import settings 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 reports.constants import ( + WEB_ANALYTICS_API_URL_V2, + WEB_ANALYSTICS_API_TOP_STATS_PAYLOAD, + WEB_ANALYTICS_CODENAME_MAPPING, +) from versions.models import Version INCLUSIVE = "[]" +logger = structlog.get_logger() + class WebsiteStatReport(TimeStampedModel): version = models.OneToOneField(Version, on_delete=models.CASCADE) @@ -36,16 +44,30 @@ def save(self, **kwargs): super().save(**kwargs) @property - def analytics_api_url(self) -> str: - return WEB_ANALYTICS_API_URL.format(self.period.lower, self.period.upper) + def analytics_api_payload(self): + base_payload = WEB_ANALYSTICS_API_TOP_STATS_PAYLOAD + base_payload["date_range"] = [str(self.period.lower), str(self.period.upper)] + return base_payload def populate_from_api(self): """Fetch stats from API and generate child WebsiteStatItem instances.""" - response = requests.get(self.analytics_api_url) + if not settings.PLAUSIBLE_STATS_KEY: + logger.info("Plausible API key not set, skipping") + return + + headers = { + "Content-Type": "application/json; charset=utf-8", + "Authorization": f"Bearer {settings.PLAUSIBLE_STATS_KEY}", + } + response = requests.post( + url=WEB_ANALYTICS_API_URL_V2, + json=self.analytics_api_payload, + headers=headers, + ) data = response.json() - if not data or "top_stats" not in data: + if not data or "results" not in data or "query" not in data: raise ValueError(f"Invalid Plausible API response: {data}") # Clear existing stat items @@ -53,12 +75,16 @@ def populate_from_api(self): stat_items = [] - for stat_data in data["top_stats"]: + for i, value in enumerate(data["query"]["metrics"]): + code_name = value + name = WEB_ANALYTICS_CODENAME_MAPPING.get(code_name, "") + met_value = data["results"][0]["metrics"][i] + stat = WebsiteStatItem( report=self, - name=stat_data["name"], - value=stat_data["value"], - code_name=stat_data["graph_metric"], + name=name, + value=met_value, + code_name=code_name, ) stat_items.append(stat)