diff --git a/src/onegov/town6/boardlets.py b/src/onegov/town6/boardlets.py index a4f8987147..a5c379e490 100644 --- a/src/onegov/town6/boardlets.py +++ b/src/onegov/town6/boardlets.py @@ -1,6 +1,10 @@ from __future__ import annotations + from datetime import timedelta from functools import cached_property + +import requests +from requests import HTTPError from sedate import utcnow from typing import TYPE_CHECKING @@ -72,6 +76,10 @@ def facts(self) -> Iterator[BoardletFact]: icon='fa-check-circle' ) + # TODO: add average ticket lead time from opening to closing + # TODO: add average ticket lead time from pending/in progress to + # closing + def get_icon_for_visibility(visibility: str) -> str: visibility_icons = { @@ -95,6 +103,50 @@ def get_icon_title(request: TownRequest, visibility: str) -> str: mapping={'visibility': visibility})) +def plausible_stats( + metrics: list[str], + date_range: str, + filters: list[str] | None = None, + dimensions: list[str] | None = None +) -> dict[str, list[dict[str, str]]]: + api_key = ( + 'eR9snr0RzrglMLrKqVPNQ_IYL3dD6hyOX0-2gyRMlxSSSTk5bg6NjORWtbNEMoHU') + site_id = 'wil.onegovcloud.ch' + + if filters is None: + filters = [] + if dimensions is None: + dimensions = [] + + url = 'https://analytics.seantis.ch/api/v2/query' + headers = { + 'Authorization': f'Bearer {api_key}', + 'Content-Type': 'application/json' + } + data = { + 'site_id': site_id, + 'metrics': metrics, + 'date_range': date_range, + 'filters': filters, + 'dimensions': dimensions + } + + try: + response = requests.post(url, headers=headers, json=data, timeout=30) + response.raise_for_status() # Raise an error for bad status codes + except HTTPError as e: + if response.status_code == 401: + print('Unauthorized: Invalid API key or insufficient ' + 'permissions', e) + else: + print('HTTP error', e) + except Exception as e: + print('Plausible error occurred:', e) + + print('*** tschupre get plausible stats response', response.json()) + return response.json() + + @TownApp.boardlet(name='pages', order=(1, 2), icon='fa-edit') class EditedPagesBoardlet(TownBoardlet): @@ -134,3 +186,73 @@ def facts(self) -> Iterator[BoardletFact]: icon=get_icon_for_visibility(n.access), icon_title=get_icon_title(self.request, n.access) ) + + +@TownApp.boardlet(name='plausible-stats', order=(2, 1)) +class PlausibleStats(TownBoardlet): + + @property + def title(self) -> str: + return 'Plausible Stats' + + @property + def facts(self) -> Iterator[BoardletFact]: + + data = plausible_stats( + ['visitors', 'pageviews', 'views_per_visit', + 'visit_duration'], + '7d', + ) + + values = data['results'][0]['metrics'] + + yield BoardletFact( + text='Unique Visitors in the Last Week', + number=values[0] or '-', + ) + + yield BoardletFact( + text='Total Page Views in the Last Week', + number=values[1] or '-', + ) + + yield BoardletFact( + text='Number of Page Views per Visit', + number=values[2] or '-', + ) + + yield BoardletFact( + text='Average Visit Duration in Seconds', + number=values[3] or '-', + ) + + +@TownApp.boardlet(name='Top Pages', order=(2, 2)) +class PlausibleTopPages(TownBoardlet): + + @property + def title(self) -> str: + return 'Top Pages' + + @property + def facts(self) -> Iterator[BoardletFact]: + + data = plausible_stats( + ['visitors'], + '7d', + [], + ['event:page'] + ) + + # Extract and sort the results by the number of visits (metrics) + sorted_results = sorted( + data['results'], key=lambda x: x['metrics'][0], reverse=True) + + # Print the sorted results + for result in sorted_results[:10]: + print(f"Top Page: {result['dimensions'][0]}, Visits:" + f" {result['metrics'][0]}") + yield BoardletFact( + text=result['dimensions'][0], + number=result['metrics'][0] or '-', + )