Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds dashboard with ticket, page and news figures #1669

Merged
merged 36 commits into from
Feb 25, 2025
Merged
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
acbb71b
Adds dashboard with ticket, page and news figures
Tschuppi81 Jan 28, 2025
dc72178
Adds initial plausible stats
Tschuppi81 Jan 28, 2025
83813d8
Add a plausible api class
Tschuppi81 Jan 28, 2025
b833aea
Adds closed_on column for tickts
Tschuppi81 Jan 31, 2025
50187a7
Extract plausible, 30d time window for stats
Tschuppi81 Jan 31, 2025
a333434
Add dashboard icon to managment menu
Tschuppi81 Feb 7, 2025
26829ab
Move boardlets to org and add translations
Tschuppi81 Feb 7, 2025
7abff6f
Rework template and style
Tschuppi81 Feb 7, 2025
36885d5
Rename property
Tschuppi81 Feb 7, 2025
34b420c
Improve
Tschuppi81 Feb 11, 2025
42d4eef
Adds tests
Tschuppi81 Feb 11, 2025
c77de66
More translations
Tschuppi81 Feb 11, 2025
05c746e
Prevent org boardlets occuring in feriennet
Tschuppi81 Feb 11, 2025
4d333dc
Rework template and styles
Tschuppi81 Feb 11, 2025
6b854aa
Fix imports
Tschuppi81 Feb 11, 2025
945d09e
Remove init file from plausible folder
Tschuppi81 Feb 13, 2025
02aa3ac
Adds package file for plausible
Tschuppi81 Feb 13, 2025
7a099f6
Rework boardlets
Tschuppi81 Feb 13, 2025
1b84ced
Reuse handlers and rework test (still not working)
Tschuppi81 Feb 13, 2025
484cdcb
Adjust test for dashboard menu
Tschuppi81 Feb 13, 2025
7c73980
Small improvements and test
Tschuppi81 Feb 18, 2025
9776d21
Adds test
Tschuppi81 Feb 18, 2025
e700bbf
template and style adjustments
Tschuppi81 Feb 18, 2025
e9b299f
Resolve merge conflicts
Tschuppi81 Feb 18, 2025
ea408d5
Adds plausible package
Tschuppi81 Feb 21, 2025
5b95bc0
Fix mypy issues
Tschuppi81 Feb 21, 2025
7f37235
Re-add lost translations
Tschuppi81 Feb 21, 2025
d64b0a6
Add plausible to core levels definition
Tschuppi81 Feb 24, 2025
60d8e6e
Town6: Use primary color for boardlet top border
Tschuppi81 Feb 24, 2025
90adb90
Display Boardlets in org correctly
BreathingFlesh Feb 24, 2025
b251276
Moves visibility icon behind title/text and add simple page/newslette…
Tschuppi81 Feb 24, 2025
91c00cb
Resolve merge conflict
Tschuppi81 Feb 24, 2025
1209213
Make all boardlets the samet height
Tschuppi81 Feb 24, 2025
0275176
Only show boardlet if site id is given
Tschuppi81 Feb 24, 2025
8c3fa5e
Fix dashboard layout for org
Tschuppi81 Feb 25, 2025
afa09c5
Display visibility icon smaller and in primary color
Tschuppi81 Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Adds initial plausible stats
Tschuppi81 committed Jan 28, 2025
commit dc72178f05b52334381e8306935bc2468d0bac32
122 changes: 122 additions & 0 deletions src/onegov/town6/boardlets.py
Original file line number Diff line number Diff line change
@@ -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 @@
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 = {
@@ -82,19 +90,63 @@
}

if visibility not in visibility_icons:
raise ValueError(f'Invalid visibility: {visibility}')

Check warning on line 93 in src/onegov/town6/boardlets.py

Codecov / codecov/patch

src/onegov/town6/boardlets.py#L93

Added line #L93 was not covered by tests

return visibility_icons[visibility]


def get_icon_title(request: TownRequest, visibility: str) -> str:
if visibility not in ['public', 'secret', 'private', 'member']:
raise ValueError(f'Invalid visibility: {visibility}')

Check warning on line 100 in src/onegov/town6/boardlets.py

Codecov / codecov/patch

src/onegov/town6/boardlets.py#L100

Added line #L100 was not covered by tests

return request.translate(_('Visibility ${visibility}',
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 '

Check warning on line 139 in src/onegov/town6/boardlets.py

Codecov / codecov/patch

src/onegov/town6/boardlets.py#L137-L139

Added lines #L137 - L139 were not covered by tests
'permissions', e)
else:
print('HTTP error', e)
except Exception as e:
print('Plausible error occurred:', e)

Check warning on line 144 in src/onegov/town6/boardlets.py

Codecov / codecov/patch

src/onegov/town6/boardlets.py#L142-L144

Added lines #L142 - L144 were not covered by tests

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 @@
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 '-',
)