templatesdir = $(datadir)/templates
templates_analytics_DATA = \
+ src/templates/analytics/docs.html \
src/templates/analytics/index.html
templates_analyticsdir = $(templatesdir)/analytics
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 }
return NotImplemented
+ def __hash__(self):
+ return hash(self.page)
+
@staticmethod
def sanitise_page_name(page):
if not page:
--- /dev/null
+{% extends "../base.html" %}
+
+{% block title %}{{ _("Analytics") }} - {{ _("Docs") }}{% end block %}
+
+{% block container %}
+ <section class="hero is-primary">
+ <div class="hero-body">
+ <div class="container">
+ <nav class="breadcrumb" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/">{{ _("Home") }}</a>
+ </li>
+ <li>
+ <a href="/analytics">{{ _("Analytics") }}</a>
+ </li>
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Documentation") }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <h1 class="title">{{ _("Documents") }}</h1>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ <h4 class="title is-4">{{ _("Most Popular Pages") }}</h4>
+
+ {% module DocsList(popular_pages, show_author=False) %}
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ <h4 class="title is-4">{{ _("Popular Searches") }}</h4>
+
+ <table class="table is-fullwidth">
+ <thead>
+ <tr>
+ <th>{{ _("Search Query") }}</th>
+ <th class="has-text-right">{{ _("Searches") }}</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {% for search in sorted(popular_searches, key=lambda q: popular_searches[q], reverse=True) %}
+ <tr>
+ <th scope="row">{{ search }}</th>
+ <td class="has-text-right">{{ popular_searches[search] }}</td>
+ </tr>
+ {% end %}
+ </tbody>
+ </table>
+ </div>
+ </section>
+{% end block %}
</div>
</div>
</section>
+
+ <section class="section">
+ <div class="container">
+ <div class="buttons">
+ <a class="button" href="/analytics/docs">
+ {{ _("Documentation") }}
+ </a>
+ </div>
+ </div>
+ </section>
{% end block %}
# Analytics
(r"/analytics", analytics.IndexHandler),
+ (r"/analytics/docs", analytics.DocsHandler),
# Authentication
(r"/join", auth.JoinHandler),
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: