From 55ed268d099b2331c277e25f93c90f0e88eed340 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Mon, 8 Jan 2024 17:45:41 +0000 Subject: [PATCH] analytics: Add page about docs analytics Signed-off-by: Michael Tremer --- Makefile.am | 1 + src/backend/analytics.py | 60 ++++++++++++++++++++++++++++++ src/backend/wiki.py | 3 ++ src/templates/analytics/docs.html | 59 +++++++++++++++++++++++++++++ src/templates/analytics/index.html | 10 +++++ src/web/__init__.py | 1 + src/web/analytics.py | 15 ++++++++ 7 files changed, 149 insertions(+) create mode 100644 src/templates/analytics/docs.html diff --git a/Makefile.am b/Makefile.am index 3856bdac..2df8b3a0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,6 +116,7 @@ templates_DATA = \ templatesdir = $(datadir)/templates templates_analytics_DATA = \ + src/templates/analytics/docs.html \ src/templates/analytics/index.html templates_analyticsdir = $(templatesdir)/analytics diff --git a/src/backend/analytics.py b/src/backend/analytics.py index ac350130..274181ae 100644 --- a/src/backend/analytics.py +++ b/src/backend/analytics.py @@ -158,3 +158,63 @@ class Analytics(misc.Object): return res.c return 0 + + # Popular Pages + + def get_most_popular_docs_pages(self, host, since=None, offset=None, limit=None): + # Make since an absolute timestamp + if since and isinstance(since, datetime.timedelta): + since = datetime.datetime.utcnow() - since + + pages = self.backend.wiki._get_pages(""" + SELECT + wiki.*, + COUNT(*) AS _c + FROM + wiki_current + LEFT JOIN + wiki ON wiki_current.id = wiki.id + LEFT JOIN + analytics_unique_visits + ON (CASE WHEN wiki.page = '/' THEN '/docs' + ELSE '/docs' || wiki.page END) = analytics_unique_visits.uri + WHERE + host = %s + AND + uri LIKE '/docs%%' + GROUP BY + wiki.id + ORDER BY + _c DESC + LIMIT + %s + OFFSET + %s + """, host, limit, offset, + ) + + return list(pages) + + # Search + + def get_search_queries(self, host, uri, limit=None): + res = self.db.query(""" + SELECT + q, + COUNT(*) AS c + FROM + analytics_unique_visits + WHERE + host = %s + AND + uri = %s + AND + q IS NOT NULL + GROUP BY + q + LIMIT + %s + """, host, uri, limit, + ) + + return { " ".join(row.q) : row.c for row in res } diff --git a/src/backend/wiki.py b/src/backend/wiki.py index caacf702..74893435 100644 --- a/src/backend/wiki.py +++ b/src/backend/wiki.py @@ -390,6 +390,9 @@ class Page(misc.Object): return NotImplemented + def __hash__(self): + return hash(self.page) + @staticmethod def sanitise_page_name(page): if not page: diff --git a/src/templates/analytics/docs.html b/src/templates/analytics/docs.html new file mode 100644 index 00000000..cb5b0b18 --- /dev/null +++ b/src/templates/analytics/docs.html @@ -0,0 +1,59 @@ +{% extends "../base.html" %} + +{% block title %}{{ _("Analytics") }} - {{ _("Docs") }}{% end block %} + +{% block container %} +
+
+
+ + +

{{ _("Documents") }}

+
+
+
+ +
+
+

{{ _("Most Popular Pages") }}

+ + {% module DocsList(popular_pages, show_author=False) %} +
+
+ +
+
+

{{ _("Popular Searches") }}

+ + + + + + + + + + + {% for search in sorted(popular_searches, key=lambda q: popular_searches[q], reverse=True) %} + + + + + {% end %} + +
{{ _("Search Query") }}{{ _("Searches") }}
{{ search }}{{ popular_searches[search] }}
+
+
+{% end block %} diff --git a/src/templates/analytics/index.html b/src/templates/analytics/index.html index 8cd60afb..7f380229 100644 --- a/src/templates/analytics/index.html +++ b/src/templates/analytics/index.html @@ -52,4 +52,14 @@ + +
+ +
{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 59429c0d..ded884c1 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -124,6 +124,7 @@ class Application(tornado.web.Application): # Analytics (r"/analytics", analytics.IndexHandler), + (r"/analytics/docs", analytics.DocsHandler), # Authentication (r"/join", auth.JoinHandler), diff --git a/src/web/analytics.py b/src/web/analytics.py index ab8179e5..8eafe488 100644 --- a/src/web/analytics.py +++ b/src/web/analytics.py @@ -12,6 +12,21 @@ class IndexHandler(base.BaseHandler): self.render("analytics/index.html") +class DocsHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self): + # Most Popular Pages + popular_pages = self.backend.analytics.get_most_popular_docs_pages( + self.request.host, since=datetime.timedelta(hours=24 * 365), limit=50) + + # Popular Searches + popular_searches = self.backend.analytics.get_search_queries( + self.request.host, "/docs/search", limit=25) + + self.render("analytics/docs.html", + popular_pages=popular_pages, popular_searches=popular_searches) + + class SummaryModule(ui_modules.UIModule): def render(self, host=None, uri=None): if host is None: -- 2.39.2