From 68e05d8fb860bd4564dac6d3272f491656cea976 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 28 Jun 2019 16:45:28 -0500 Subject: [PATCH 01/28] Beginning to add more views. --- tracking/managers.py | 30 ++++++----- .../templates/tracking/visitor_overview.html | 53 +++++++++++++++++++ tracking/tests/test_views.py | 45 ++++++++++++++-- tracking/urls.py | 6 ++- tracking/views.py | 44 ++++++++++++++- 5 files changed, 159 insertions(+), 19 deletions(-) create mode 100644 tracking/templates/tracking/visitor_overview.html diff --git a/tracking/managers.py b/tracking/managers.py index deae9ea..84ef5cc 100644 --- a/tracking/managers.py +++ b/tracking/managers.py @@ -4,7 +4,7 @@ from django.utils import timezone from django.contrib.auth import get_user_model from django.db import models -from django.db.models import Count, Avg +from django.db.models import Count, Avg, Sum from tracking.settings import TRACK_PAGEVIEWS, TRACK_ANONYMOUS_USERS from tracking.cache import CacheManager @@ -164,24 +164,26 @@ def user_stats(self, start_date=None, end_date=None): user_kwargs['visit_history__start_time__isnull'] = False visit_kwargs['start_time__isnull'] = False - users = list(get_user_model().objects.filter(**user_kwargs).annotate( + users = get_user_model().objects.filter(**user_kwargs).annotate( visit_count=Count('visit_history'), time_on_site=Avg('visit_history__time_on_site'), + page_count=Count('visit_history__pageviews'), + pages_per_visit = Count('visit_history__pageviews') / Count('visit_history'), ).filter(visit_count__gt=0).order_by( '-time_on_site', get_user_model().USERNAME_FIELD, - )) - - # Aggregate pageviews per visit - for user in users: - user.pages_per_visit = user.visit_history.filter( - **visit_kwargs - ).annotate( - page_count=Count('pageviews') - ).filter(page_count__gt=0).aggregate( - pages_per_visit=Avg('page_count'))['pages_per_visit'] - # Lop off the floating point, turn into timedelta - user.time_on_site = timedelta(seconds=int(user.time_on_site)) + ) + +# # Aggregate pageviews per visit +# for user in users: +# user.pages_per_visit = user.visit_history.filter( +# **visit_kwargs +# ).annotate( +# page_count=Count('pageviews') +# ).filter(page_count__gt=0).aggregate( +# pages_per_visit=Avg('page_count'))['pages_per_visit'] +# # Lop off the floating point, turn into timedelta +# user.time_on_site = timedelta(seconds=int(user.time_on_site)) return users diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html new file mode 100644 index 0000000..d879621 --- /dev/null +++ b/tracking/templates/tracking/visitor_overview.html @@ -0,0 +1,53 @@ + + + +{% firstof user.get_full_name user %} Visit Overview - django-tracking2 + + +

{% firstof user.get_full_name user %} Visit Overview - django-tracking2

+
+
+ {{ form.as_table }} + +
+
+
+

+ Visitor tracking began on + {{ track_start_time|date:"Y-m-d H:i:s" }} +

+ {% if warn_incomplete %} +

+ The start time precedes the oldest tracked visitor, thus + the stats are not complete for the specified range. +

+ {% endif %} +
+
+
+
# Visits
+
{{ user.visit_count }}
+
Avg. Time on Site
+
{{ user.time_on_site|default_if_none:"n/a" }}
+
Avg. Pages/Visit
+
{{ user.pages_per_visit|floatformat|default:"n/a" }}
+
+ + + + + + + + + {% for visit in visits %} + + + + + {% endfor %} + +
Visits by {% firstof user.get_full_name user %}
Start TimeEnd Time
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
+
+ + \ No newline at end of file diff --git a/tracking/tests/test_views.py b/tracking/tests/test_views.py index 28f9692..f03f98d 100644 --- a/tracking/tests/test_views.py +++ b/tracking/tests/test_views.py @@ -18,9 +18,9 @@ class ViewsTestCase(TestCase): def setUp(self): self.auth = {'username': 'john', 'password': 'smith'} - user = User.objects.create_user(**self.auth) - user.is_superuser = True - user.save() + self.user = User.objects.create_user(**self.auth) + self.user.is_superuser = True + self.user.save() self.assertTrue(self.client.login(**self.auth)) def test_dashboard_default(self): @@ -71,6 +71,45 @@ def test_logout_tracking(self, mock_end): self.assertEqual(visitor.end_time, self.now) self.assertTrue(visitor.time_on_site > 0) + def test_visitor_overview_default(self): + # make a non PAGEVIEW tracking request + Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + response = self.client.get('/tracking/visitors/%s/' % self.user.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.context['user'], + self.user) + + def test_visitor_overview_times(self): + # make a non PAGEVIEW tracking request + Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + response = self.client.get( + '/tracking/visitors/%s/?start=2014-11&end=2014-12-01' % self.user.pk) + self.assertEqual(response.status_code, 200) + + def test_visitor_overview_times_bad(self): + # make a non PAGEVIEW tracking request + Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + response = self.client.get( + '/tracking/visitors/%s/?start=2014-aa&end=2014-12-01' % self.user.pk) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'Enter a valid date/time.') + class AdminViewTestCase(TestCase): diff --git a/tracking/urls.py b/tracking/urls.py index ffe3b6a..ab7ccb1 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -1,7 +1,11 @@ from django.conf.urls import url -from tracking.views import dashboard +from tracking.views import ( + dashboard, + visitor_overview, +) urlpatterns = [ url(r'^$', dashboard, name='tracking-dashboard'), + url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), ] diff --git a/tracking/views.py b/tracking/views.py index b734c4a..0eeedd9 100644 --- a/tracking/views.py +++ b/tracking/views.py @@ -3,7 +3,11 @@ from datetime import timedelta from django import forms -from django.shortcuts import render +from django.shortcuts import ( + render, + get_object_or_404, +) +from django.contrib.auth import get_user_model from django.contrib.auth.decorators import permission_required from django.utils.timezone import now @@ -66,3 +70,41 @@ def dashboard(request): 'pageview_stats': pageview_stats, } return render(request, 'tracking/dashboard.html', context) + +@permission_required('tracking.visitor_log') +def visitor_overview(request, user_id): + print('user_id') + print(user_id) + "Counts, aggregations and more!" +# user = get_object_or_404(get_user_model(), pk=user_id) + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + + form = DashboardForm(data=request.GET or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + + # queries take `date` objects (for now) + user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() + visits = Visitor.objects.filter(start_time__range=(start_time, end_time)) + + context = { + 'form': form, + 'track_start_time': track_start_time, + 'warn_incomplete': warn_incomplete, + 'visits': visits, + 'user': user, + } + return render(request, 'tracking/visitor_overview.html', context) From ffe55e01899872ab32c945552c86a292d3809467 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 28 Jun 2019 16:46:33 -0500 Subject: [PATCH 02/28] Beginning to add more views. --- tracking/views.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tracking/views.py b/tracking/views.py index 0eeedd9..853e28d 100644 --- a/tracking/views.py +++ b/tracking/views.py @@ -73,8 +73,6 @@ def dashboard(request): @permission_required('tracking.visitor_log') def visitor_overview(request, user_id): - print('user_id') - print(user_id) "Counts, aggregations and more!" # user = get_object_or_404(get_user_model(), pk=user_id) end_time = now() @@ -108,3 +106,39 @@ def visitor_overview(request, user_id): 'user': user, } return render(request, 'tracking/visitor_overview.html', context) + +@permission_required('tracking.visitor_log') +def visitor_visits(request, visit_id): + "Counts, aggregations and more!" +# user = get_object_or_404(get_user_model(), pk=user_id) + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + + form = DashboardForm(data=request.GET or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + + # queries take `date` objects (for now) + user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() + visits = Visitor.objects.filter(start_time__range=(start_time, end_time)) + + context = { + 'form': form, + 'track_start_time': track_start_time, + 'warn_incomplete': warn_incomplete, + 'visits': visits, + 'user': user, + } + return render(request, 'tracking/visitor_overview.html', context) \ No newline at end of file From 7d9229e083b1c126baa4e44221ad62beefcb343d Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Mon, 1 Jul 2019 16:55:57 -0500 Subject: [PATCH 03/28] In progress of adding views. --- .../templates/tracking/snippets/stats.html | 2 +- .../templates/tracking/visitor_overview.html | 14 ++--- .../templates/tracking/visitor_visits.html | 53 +++++++++++++++++++ tracking/urls.py | 2 + tracking/views.py | 35 ++---------- 5 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 tracking/templates/tracking/visitor_visits.html diff --git a/tracking/templates/tracking/snippets/stats.html b/tracking/templates/tracking/snippets/stats.html index 901f34e..db8c74d 100644 --- a/tracking/templates/tracking/snippets/stats.html +++ b/tracking/templates/tracking/snippets/stats.html @@ -67,7 +67,7 @@

Registered Users

{% for user in user_stats %} - {% firstof user.get_full_name user %} + {% firstof user.get_full_name user %} {{ user.visit_count }} {{ user.time_on_site|default_if_none:"n/a" }} {{ user.pages_per_visit|floatformat|default:"n/a" }} diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index d879621..513422e 100644 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -1,10 +1,10 @@ -{% firstof user.get_full_name user %} Visit Overview - django-tracking2 +{% firstof user.get_full_name user %} Visits Overview - django-tracking2 -

{% firstof user.get_full_name user %} Visit Overview - django-tracking2

+

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{{ form.as_table }} @@ -26,11 +26,11 @@

{% firstof user.get_full_name user %} Visit Overview - django-tracking2

# Visits
-
{{ user.visit_count }}
+
{{ user.visit_count }}
Avg. Time on Site
-
{{ user.time_on_site|default_if_none:"n/a" }}
+
{{ user.time_on_site|default_if_none:"n/a" }}
Avg. Pages/Visit
-
{{ user.pages_per_visit|floatformat|default:"n/a" }}
+
{{ user.pages_per_visit|floatformat|default:"n/a" }}
@@ -42,8 +42,8 @@

{% firstof user.get_full_name user %} Visit Overview - django-tracking2

{% for visit in visits %} - - + + {% endfor %} diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html new file mode 100644 index 0000000..7427431 --- /dev/null +++ b/tracking/templates/tracking/visitor_visits.html @@ -0,0 +1,53 @@ + + + +{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 + + +

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

+
+
+
Duration
+
{{ visit.time_on_site }} +
# of Page Views
+
{{ len(pageviews) }} +
IP
+
{{ visit.ip_address }} +
User Agent
+
{{ visit.user_agent }} +
+
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
+ + + + + + + {% regroup pageviews by url as pageList %} + {% for page in pageList %} + + + + + {% endfor %} + +
Views/Page
URL# of views
{{ pageList.grouper }}{{ len(pageList) }}
+ + + + + + + + + {% for view in pageviews %} + + + + + {% endfor %} + +
Page Views
TimeURL
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}
+
+ + \ No newline at end of file diff --git a/tracking/urls.py b/tracking/urls.py index ab7ccb1..1675861 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -3,9 +3,11 @@ from tracking.views import ( dashboard, visitor_overview, + visitor_visits, ) urlpatterns = [ url(r'^$', dashboard, name='tracking-dashboard'), url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), + url(r'^visits/(?P.*)/$', visitor_visits, name='tracking-visitor-visits'), ] diff --git a/tracking/views.py b/tracking/views.py index 853e28d..2a39b7f 100644 --- a/tracking/views.py +++ b/tracking/views.py @@ -109,36 +109,11 @@ def visitor_overview(request, user_id): @permission_required('tracking.visitor_log') def visitor_visits(request, visit_id): - "Counts, aggregations and more!" -# user = get_object_or_404(get_user_model(), pk=user_id) - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=request.GET or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) - - # queries take `date` objects (for now) - user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() - visits = Visitor.objects.filter(start_time__range=(start_time, end_time)) + visit = get_object_or_404(Visitor, pk=visit_id) + pageviews = visit.pageviews.all() context = { - 'form': form, - 'track_start_time': track_start_time, - 'warn_incomplete': warn_incomplete, - 'visits': visits, - 'user': user, + 'visit': visit, + 'pageviews': pageviews, } - return render(request, 'tracking/visitor_overview.html', context) \ No newline at end of file + return render(request, 'tracking/visitor_visits.html', context) From b4104bd745b46df08c41a6cd1d8404b6ab107812 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Thu, 11 Jul 2019 17:15:29 -0500 Subject: [PATCH 04/28] In progress of adding views. --- tracking/managers.py | 11 ++++-- .../templates/tracking/snippets/stats.html | 2 +- .../tracking/visitor_page_detail.html | 34 ++++++++++++++++++ .../templates/tracking/visitor_visits.html | 9 +++-- tracking/urls.py | 2 ++ tracking/views.py | 36 ++++++++++++++++++- 6 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 tracking/templates/tracking/visitor_page_detail.html diff --git a/tracking/managers.py b/tracking/managers.py index 84ef5cc..9d21e2d 100644 --- a/tracking/managers.py +++ b/tracking/managers.py @@ -1,5 +1,7 @@ from __future__ import division +import logging + from datetime import timedelta from django.utils import timezone from django.contrib.auth import get_user_model @@ -8,6 +10,7 @@ from tracking.settings import TRACK_PAGEVIEWS, TRACK_ANONYMOUS_USERS from tracking.cache import CacheManager +log = logging.getLogger(__file__) class VisitorManager(CacheManager): def active(self, registered_only=True): @@ -163,12 +166,14 @@ def user_stats(self, start_date=None, end_date=None): else: user_kwargs['visit_history__start_time__isnull'] = False visit_kwargs['start_time__isnull'] = False + + log.critical(visit_kwargs) users = get_user_model().objects.filter(**user_kwargs).annotate( - visit_count=Count('visit_history'), + visit_count=Count('visit_history', distinct=True), time_on_site=Avg('visit_history__time_on_site'), - page_count=Count('visit_history__pageviews'), - pages_per_visit = Count('visit_history__pageviews') / Count('visit_history'), + page_count=Count('visit_history__pageviews', distinct=True), + pages_per_visit=Count('visit_history__pageviews', distinct=True)/Count('visit_history', distinct=True), ).filter(visit_count__gt=0).order_by( '-time_on_site', get_user_model().USERNAME_FIELD, diff --git a/tracking/templates/tracking/snippets/stats.html b/tracking/templates/tracking/snippets/stats.html index db8c74d..1167764 100644 --- a/tracking/templates/tracking/snippets/stats.html +++ b/tracking/templates/tracking/snippets/stats.html @@ -67,7 +67,7 @@

Registered Users

{% for user in user_stats %} - {% firstof user.get_full_name user %} + {% firstof user.get_full_name user %} {{ user.visit_count }} {{ user.time_on_site|default_if_none:"n/a" }} {{ user.pages_per_visit|floatformat|default:"n/a" }} diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html new file mode 100644 index 0000000..b523ff3 --- /dev/null +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -0,0 +1,34 @@ + + + +{% firstof user.get_full_name user %} {{ page_url }} Visits - django-tracking2 + + +

{% firstof user.get_full_name user %} {{ page_url }} Visits - django-tracking2

+
+
+
Total Views
+
{{ total_views }} +
Avg Duration
+
{{ avg_duration }} +
Avg Views/Visit
+
{{ avg_views_per_visit }} +
+ + + + + + + + {% for visit in visits %} + + + + + {% endfor %} + +
Visits involving {% page_url %} by {% firstof user.get_full_name user %}
Start TimeEnd Time
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
+
+ + \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index 7427431..ae6cbaf 100644 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -10,7 +10,7 @@

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s"
Duration
{{ visit.time_on_site }}
# of Page Views
-
{{ len(pageviews) }} +
{{ pageviews|length }}
IP
{{ visit.ip_address }}
User Agent
@@ -23,11 +23,10 @@

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" # of views - {% regroup pageviews by url as pageList %} - {% for page in pageList %} + {% for url, views in pageview_stats.items %} - {{ pageList.grouper }} - {{ len(pageList) }} + {{ url }} + {{ views }} {% endfor %} diff --git a/tracking/urls.py b/tracking/urls.py index 1675861..d3799d5 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -4,10 +4,12 @@ dashboard, visitor_overview, visitor_visits, + visitor_page_detail, ) urlpatterns = [ url(r'^$', dashboard, name='tracking-dashboard'), url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), url(r'^visits/(?P.*)/$', visitor_visits, name='tracking-visitor-visits'), + url(r'^visitors/(?P.*)/page/(?P.*)/$', visitor_page_detail, name='tracking-page-detail'), ] diff --git a/tracking/views.py b/tracking/views.py index 2a39b7f..f94f509 100644 --- a/tracking/views.py +++ b/tracking/views.py @@ -1,6 +1,7 @@ import logging from datetime import timedelta +from statistics import mean from django import forms from django.shortcuts import ( @@ -13,6 +14,7 @@ from tracking.models import Visitor, Pageview from tracking.settings import TRACK_PAGEVIEWS +from _collections import OrderedDict log = logging.getLogger(__file__) @@ -96,7 +98,7 @@ def visitor_overview(request, user_id): # queries take `date` objects (for now) user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() - visits = Visitor.objects.filter(start_time__range=(start_time, end_time)) + visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) context = { 'form': form, @@ -111,9 +113,41 @@ def visitor_overview(request, user_id): def visitor_visits(request, visit_id): visit = get_object_or_404(Visitor, pk=visit_id) pageviews = visit.pageviews.all() + pageview_stats = {} + for v in pageviews: + if v.url not in pageview_stats: + pageview_stats[v.url] = 0 + pageview_stats[v.url] += 1 + pageview_stats = OrderedDict(sorted(pageview_stats.items(), key=lambda x: x[1], reverse=True)) context = { 'visit': visit, 'pageviews': pageviews, + 'pageview_stats': pageview_stats, } return render(request, 'tracking/visitor_visits.html', context) + +@permission_required('tracking.visitor_log') +def visitor_page_detail(request, user_id, page_url): + user = get_object_or_404(get_user_model(), pk=user_id) + pageviews = Pageview.objects.filter(url=page_url, visitor__user__pk=user_id) + totalViewTime = 0 + numPageViews = 0 + viewsPerVisit = {} + for v in pageviews: + numPageViews += 1 + totalViewTime += v.view_time + if v.pk not in viewsPerVisit: + viewsPerVisit[v.pk] = 0 + viewsPerVisit[v.pk] += 1 + visits = Visitor.objects.filter(pageviews__in=pageviews).order_by('end_time', 'start_time') + + context = { + 'total_views': numPageViews, + 'avg_duration': totalViewTime/numPageViews, + 'avg_views_per_visit': mean(viewsPerVisit.values()), + 'visits': visits, + 'user': user, + 'page_url': page_url, + } + return render(request, 'tracking/visitor_visits.html', context) \ No newline at end of file From 48175ea47d64889c396babbd9352f890acfc1daa Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 12 Jul 2019 17:32:48 -0500 Subject: [PATCH 05/28] In progress of adding views. --- .../templates/tracking/page_overview.html | 32 +++++++++++++++++++ .../tracking/visitor_page_detail.html | 4 +-- .../tracking/visitor_pageview_detail.html | 15 +++++++++ .../templates/tracking/visitor_visits.html | 8 ++--- tracking/urls.py | 6 +++- tracking/views.py | 30 ++++++++++++++--- 6 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 tracking/templates/tracking/page_overview.html create mode 100644 tracking/templates/tracking/visitor_pageview_detail.html diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html new file mode 100644 index 0000000..967db1e --- /dev/null +++ b/tracking/templates/tracking/page_overview.html @@ -0,0 +1,32 @@ + + + +Page Overview - django-tracking2 + + +

Page Overview - django-tracking2

+
+
+
Total Page Views
+
{{ total_page_views }} +
# of Pages Viewed
+
{{ total_pages }} +
+ + + + + + + + {% for c in pageview_counts %} + + + + + {% endfor %} + +
Pages
URL# of Views
{{ c.url }}{{ c.views }}
+
+ + \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index b523ff3..2827c3a 100644 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -9,13 +9,11 @@

{% firstof user.get_full_name user %} {{ page_url }} Visits - django-trackin
Total Views
{{ total_views }} -
Avg Duration
-
{{ avg_duration }}
Avg Views/Visit
{{ avg_views_per_visit }}
- + diff --git a/tracking/templates/tracking/visitor_pageview_detail.html b/tracking/templates/tracking/visitor_pageview_detail.html new file mode 100644 index 0000000..212d2ea --- /dev/null +++ b/tracking/templates/tracking/visitor_pageview_detail.html @@ -0,0 +1,15 @@ + + + +{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %} {{ pageview.view_time|date:"Y-m-d H:i:s" }} {{ pageview.url }} Pageview - django-tracking2 + + +

{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %} {{ pageview.view_time|date:"Y-m-d H:i:s" }} {{ pageview.url }} Pageview - django-tracking2

+
+
+
Method
+
{{ pageview.method }} +
+
+ + \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index ae6cbaf..e893e10 100644 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -1,10 +1,10 @@ -{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 +{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 -

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

+

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

Duration
@@ -25,7 +25,7 @@

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s"

{% for url, views in pageview_stats.items %} - + {% endfor %} @@ -42,7 +42,7 @@

{% firstof user.get_full_name user %} {{ visit.start_time|date:"Y-m-d H:i:s" {% for view in pageviews %}

- + {% endfor %} diff --git a/tracking/urls.py b/tracking/urls.py index d3799d5..c2ec160 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -5,11 +5,15 @@ visitor_overview, visitor_visits, visitor_page_detail, + visitor_pageview_detail, + page_overview, ) urlpatterns = [ url(r'^$', dashboard, name='tracking-dashboard'), + url(r'^visitors/(?P.*)/page/(?P.*)/$', visitor_page_detail, name='tracking-page-detail'), + url(r'^visitors/(?P.*)/pageview/(?P.*)/$', visitor_pageview_detail, name='tracking-pageview-detail'), url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), url(r'^visits/(?P.*)/$', visitor_visits, name='tracking-visitor-visits'), - url(r'^visitors/(?P.*)/page/(?P.*)/$', visitor_page_detail, name='tracking-page-detail'), + url(r'^pages/$', page_overview, name='tracking-page-overview'), ] diff --git a/tracking/views.py b/tracking/views.py index f94f509..9ec2cf9 100644 --- a/tracking/views.py +++ b/tracking/views.py @@ -2,6 +2,8 @@ from datetime import timedelta from statistics import mean +from functools import reduce +from operator import add from django import forms from django.shortcuts import ( @@ -11,6 +13,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.decorators import permission_required from django.utils.timezone import now +from django.db.models import Count, Avg, Sum from tracking.models import Visitor, Pageview from tracking.settings import TRACK_PAGEVIEWS @@ -131,23 +134,40 @@ def visitor_visits(request, visit_id): def visitor_page_detail(request, user_id, page_url): user = get_object_or_404(get_user_model(), pk=user_id) pageviews = Pageview.objects.filter(url=page_url, visitor__user__pk=user_id) - totalViewTime = 0 numPageViews = 0 viewsPerVisit = {} for v in pageviews: numPageViews += 1 - totalViewTime += v.view_time if v.pk not in viewsPerVisit: viewsPerVisit[v.pk] = 0 viewsPerVisit[v.pk] += 1 - visits = Visitor.objects.filter(pageviews__in=pageviews).order_by('end_time', 'start_time') + visits = Visitor.objects.filter(pageviews__in=pageviews).distinct().order_by('end_time', 'start_time') context = { 'total_views': numPageViews, - 'avg_duration': totalViewTime/numPageViews, 'avg_views_per_visit': mean(viewsPerVisit.values()), 'visits': visits, 'user': user, 'page_url': page_url, } - return render(request, 'tracking/visitor_visits.html', context) \ No newline at end of file + return render(request, 'tracking/visitor_page_detail.html', context) + +@permission_required('tracking.visitor_log') +def visitor_pageview_detail(request, user_id, pageview_id): + pageview = get_object_or_404(Pageview, pk=pageview_id) + + context = { + 'pageview': pageview, + } + return render(request, 'tracking/visitor_pageview_detail.html', context) + +@permission_required('tracking.visitor_log') +def page_overview(request): + pageview_counts = Pageview.objects.values('url').annotate(views=Count('url')).order_by('-views') + + context = { + 'pageview_counts': pageview_counts, + 'total_page_views': reduce(lambda acc, c: acc + c['views'], pageview_counts, 0), + 'total_pages': len(pageview_counts), + } + return render(request, 'tracking/page_overview.html', context) From cec2f3aa581b4262bd3ebbc0a9c2911a4698a10c Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Wed, 17 Jul 2019 11:07:40 -0500 Subject: [PATCH 06/28] In progress of adding views. --- tracking/templates/tracking/page_detail.html | 32 ++++++++++++++++++++ tracking/views.py | 15 ++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100755 tracking/templates/tracking/page_detail.html mode change 100644 => 100755 tracking/views.py diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html new file mode 100755 index 0000000..3f6001b --- /dev/null +++ b/tracking/templates/tracking/page_detail.html @@ -0,0 +1,32 @@ + + + +{{ page_url }} Details - django-tracking2 + + +

{{ page_url }} Details - django-tracking2

+
+
+
Total Views
+
{{ total_views }} +
# of Unique Visitors
+
{{ visitors }} +
+
Visits involving {% page_url %} by {% firstof user.get_full_name user %}Visits involving {{ page_url }} by {% firstof user.get_full_name user %}
Start Time End Time
{{ url }}{{ url }} {{ views }}
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}{{ view.url }}
+ + + + + + + {% for visit in visits %} + + + + + {% endfor %} + +
Pageviews
Start TimeEnd Time
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
+

+ + \ No newline at end of file diff --git a/tracking/views.py b/tracking/views.py old mode 100644 new mode 100755 index 9ec2cf9..6db382c --- a/tracking/views.py +++ b/tracking/views.py @@ -154,7 +154,7 @@ def visitor_page_detail(request, user_id, page_url): @permission_required('tracking.visitor_log') def visitor_pageview_detail(request, user_id, pageview_id): - pageview = get_object_or_404(Pageview, pk=pageview_id) + pageview = get_object_or_404(Pageview, pk=pageview_id, visitor__user_id=user_id) context = { 'pageview': pageview, @@ -171,3 +171,16 @@ def page_overview(request): 'total_pages': len(pageview_counts), } return render(request, 'tracking/page_overview.html', context) + +@permission_required('tracking.visitor_log') +def page_detail(request, page_url): + pageviews = Pageview.objects.filter(url=page_url) + uniqueVisitors = Pageview.objects.values('visitor_id').distinct().count() + visits = Visitor.objects.filter(pageviews__in=pageviews).distinct().order_by('end_time', 'start_time') + + context = { + 'total_views': pageviews.count(), + 'visitors': uniqueVisitors, + 'pageviews': pageviews.order_by('-view_time'), + } + return render(request, 'tracking/page_detail.html', context) From d390e1f8b68ecfdb6dca80b757771fed1796ac67 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Wed, 17 Jul 2019 16:04:05 -0500 Subject: [PATCH 07/28] In progress of adding views. --- tracking/templates/tracking/page_detail.html | 12 +++--- .../templates/tracking/page_overview.html | 4 +- .../templates/tracking/visitor_overview.html | 12 ++++++ .../templates/tracking/visitor_visits.html | 18 +++++++-- tracking/urls.py | 4 +- tracking/views.py | 38 +++++++++++++------ 6 files changed, 66 insertions(+), 22 deletions(-) diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index 3f6001b..fbfb1ca 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -15,14 +15,16 @@

{{ page_url }} Details - django-tracking2

- - + + + - {% for visit in visits %} + {% for pv in pageviews %} - - + + + {% endfor %} diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index 967db1e..ac94102 100644 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -21,8 +21,8 @@

Page Overview - django-tracking2

{% for c in pageview_counts %} - - + + {% endfor %} diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index 513422e..28250be 100644 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -33,6 +33,12 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{{ user.pages_per_visit|floatformat|default:"n/a" }} + {% if visits.has_previous %} + previous + {% endif %} + {% if visits.has_next %} + next + {% endif %}
Pageviews
Start TimeEnd TimeTimeUserURL
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof user.get_full_name user %}{{ page_url }}
{{ c.url }}{{ c.views }}{{ c.url }}{{ c.views }}
@@ -48,6 +54,12 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

Visits by {% firstof user.get_full_name user %}
+ {% if visits.has_previous %} + previous + {% endif %} + {% if visits.has_next %} + next + {% endif %} \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index e893e10..9e37858 100644 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -23,15 +23,21 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:" # of views - {% for url, views in pageview_stats.items %} + {% for url, stats in pageview_stats.items %} - {{ url }} - {{ views }} + {{ url }} + {{ stats.views }} {% endfor %} + {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} @@ -47,6 +53,12 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:" {% endfor %}

Page Views
+ {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} \ No newline at end of file diff --git a/tracking/urls.py b/tracking/urls.py index c2ec160..5a57fd0 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -7,13 +7,15 @@ visitor_page_detail, visitor_pageview_detail, page_overview, + page_detail, ) urlpatterns = [ url(r'^$', dashboard, name='tracking-dashboard'), - url(r'^visitors/(?P.*)/page/(?P.*)/$', visitor_page_detail, name='tracking-page-detail'), + url(r'^visitors/(?P.*)/page/$', visitor_page_detail, name='tracking-visitor-page-detail'), url(r'^visitors/(?P.*)/pageview/(?P.*)/$', visitor_pageview_detail, name='tracking-pageview-detail'), url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), url(r'^visits/(?P.*)/$', visitor_visits, name='tracking-visitor-visits'), url(r'^pages/$', page_overview, name='tracking-page-overview'), + url(r'^page/$', page_detail, name='tracking-page-detail'), ] diff --git a/tracking/views.py b/tracking/views.py index 6db382c..8c27ea8 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -1,5 +1,6 @@ import logging +from _collections import OrderedDict from datetime import timedelta from statistics import mean from functools import reduce @@ -10,14 +11,15 @@ render, get_object_or_404, ) +from django.http import HttpResponseNotFound from django.contrib.auth import get_user_model from django.contrib.auth.decorators import permission_required from django.utils.timezone import now from django.db.models import Count, Avg, Sum +from django.core.paginator import Paginator from tracking.models import Visitor, Pageview from tracking.settings import TRACK_PAGEVIEWS -from _collections import OrderedDict log = logging.getLogger(__file__) @@ -30,7 +32,6 @@ '%Y', # '2006' ] - class DashboardForm(forms.Form): start = forms.DateTimeField(required=False, input_formats=input_formats) end = forms.DateTimeField(required=False, input_formats=input_formats) @@ -80,6 +81,7 @@ def dashboard(request): def visitor_overview(request, user_id): "Counts, aggregations and more!" # user = get_object_or_404(get_user_model(), pk=user_id) + page = request.GET.get('page', 1) end_time = now() start_time = end_time - timedelta(days=7) defaults = {'start': start_time, 'end': end_time} @@ -102,36 +104,45 @@ def visitor_overview(request, user_id): # queries take `date` objects (for now) user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) + paginator = Paginator(visits, 100) context = { 'form': form, 'track_start_time': track_start_time, 'warn_incomplete': warn_incomplete, - 'visits': visits, + 'visits': paginator.page(page), 'user': user, } return render(request, 'tracking/visitor_overview.html', context) @permission_required('tracking.visitor_log') def visitor_visits(request, visit_id): + page = request.GET.get('page', 1) visit = get_object_or_404(Visitor, pk=visit_id) pageviews = visit.pageviews.all() pageview_stats = {} for v in pageviews: if v.url not in pageview_stats: - pageview_stats[v.url] = 0 - pageview_stats[v.url] += 1 - pageview_stats = OrderedDict(sorted(pageview_stats.items(), key=lambda x: x[1], reverse=True)) + pageview_stats[v.url] = { + 'views': 0, + } + pageview_stats[v.url]['views'] += 1 + pageview_stats = OrderedDict(sorted(pageview_stats.items(), key=lambda x: x[1]['views'], reverse=True)) + paginator = Paginator(pageviews, 100) context = { 'visit': visit, - 'pageviews': pageviews, + 'pageviews': paginator.page(page), 'pageview_stats': pageview_stats, } return render(request, 'tracking/visitor_visits.html', context) @permission_required('tracking.visitor_log') -def visitor_page_detail(request, user_id, page_url): +def visitor_page_detail(request, user_id): + try: + page_url = request.GET['page_url'] + except: + return HttpResponseNotFound() user = get_object_or_404(get_user_model(), pk=user_id) pageviews = Pageview.objects.filter(url=page_url, visitor__user__pk=user_id) numPageViews = 0 @@ -174,13 +185,18 @@ def page_overview(request): @permission_required('tracking.visitor_log') def page_detail(request, page_url): - pageviews = Pageview.objects.filter(url=page_url) + try: + page_url = request.GET['page_url'] + except: + return HttpResponseNotFound() + pageviews = Pageview.objects.filter(url=page_url).order_by('-view_time') + pv_count = pageviews.count() uniqueVisitors = Pageview.objects.values('visitor_id').distinct().count() - visits = Visitor.objects.filter(pageviews__in=pageviews).distinct().order_by('end_time', 'start_time') context = { - 'total_views': pageviews.count(), + 'total_views': pv_count, 'visitors': uniqueVisitors, 'pageviews': pageviews.order_by('-view_time'), + 'page_url': page_url, } return render(request, 'tracking/page_detail.html', context) From 29600ae864f98da26f0f4e5ca074b222bf657e1c Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Thu, 18 Jul 2019 17:20:01 -0500 Subject: [PATCH 08/28] In progress of adding pagination. --- .../templates/tracking/visitor_visits.html | 58 +++++++++++-------- tracking/views.py | 50 ++++++++-------- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index 9e37858..73da4ac 100644 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -1,42 +1,54 @@ -{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 +{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 -

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.start_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

+

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

Duration
{{ visit.time_on_site }}
# of Page Views
-
{{ pageviews|length }} +
{{ pvcount }}
IP
{{ visit.ip_address }}
User Agent
{{ visit.user_agent }} -
- - - - - - - - {% for url, stats in pageview_stats.items %} - - - - - {% endfor %} - -
Views/Page
URL# of views
{{ url }}{{ stats.views }}
+ + {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} + + + + + + + + {% for stat in pageview_stats %} + + + + + {% endfor %} + +
Views/Page
URL# of views
{{ stat.url }}{{ stat.views }}
+ {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} {% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %} @@ -54,10 +66,10 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"

Page Views
{% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %}
diff --git a/tracking/views.py b/tracking/views.py index 8c27ea8..133d13b 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -117,23 +117,20 @@ def visitor_overview(request, user_id): @permission_required('tracking.visitor_log') def visitor_visits(request, visit_id): - page = request.GET.get('page', 1) + pvpage = request.GET.get('pvpage', 1) + pvspage = request.GET.get('pvspage', 1) visit = get_object_or_404(Visitor, pk=visit_id) - pageviews = visit.pageviews.all() - pageview_stats = {} - for v in pageviews: - if v.url not in pageview_stats: - pageview_stats[v.url] = { - 'views': 0, - } - pageview_stats[v.url]['views'] += 1 - pageview_stats = OrderedDict(sorted(pageview_stats.items(), key=lambda x: x[1]['views'], reverse=True)) - paginator = Paginator(pageviews, 100) + pvcount = visit.pageviews.count() + pageviews = visit.pageviews.order_by('-view_time') + pageview_stats = visit.pageviews.values('url').annotate(views=Count('url')).order_by('-views') + pvspaginator = Paginator(pageview_stats, 100) + pvpaginator = Paginator(pageviews, 100) context = { 'visit': visit, - 'pageviews': paginator.page(page), - 'pageview_stats': pageview_stats, + 'pageviews': pvpaginator.page(pvpage), + 'pageview_stats': pvspaginator.page(pvspage), + 'pvcount': pvcount, } return render(request, 'tracking/visitor_visits.html', context) @@ -144,19 +141,24 @@ def visitor_page_detail(request, user_id): except: return HttpResponseNotFound() user = get_object_or_404(get_user_model(), pk=user_id) - pageviews = Pageview.objects.filter(url=page_url, visitor__user__pk=user_id) - numPageViews = 0 - viewsPerVisit = {} - for v in pageviews: - numPageViews += 1 - if v.pk not in viewsPerVisit: - viewsPerVisit[v.pk] = 0 - viewsPerVisit[v.pk] += 1 - visits = Visitor.objects.filter(pageviews__in=pageviews).distinct().order_by('end_time', 'start_time') + aggs = Visitor.objects.filter( + pageviews__url=page_url, + user__pk=user_id, + ).values('pk').annotate(views=Count('pageciews')).aggregate( + Avg('pageviews__count'), + Sum('pageviews__count') + ) + visits = Visitor.objects.filter( + pageviews__url=page_url, + user__pk=user_id, + ).distinct().order_by( + 'end_time', + 'start_time' + ) context = { - 'total_views': numPageViews, - 'avg_views_per_visit': mean(viewsPerVisit.values()), + 'total_views': aggs['pageviews__count__sum'], + 'avg_views_per_visit': aggs['pageviews__count__avg'], 'visits': visits, 'user': user, 'page_url': page_url, From 59717d590b7957f78ac14b9d4b8ed1f6e4e2a443 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 19 Jul 2019 15:20:36 -0500 Subject: [PATCH 09/28] In progress of refining views. --- tracking/models.py | 6 +-- tracking/templates/tracking/page_detail.html | 12 ++++++ .../templates/tracking/page_overview.html | 12 ++++++ .../tracking/visitor_page_detail.html | 18 +++++++-- .../tracking/visitor_pageview_detail.html | 7 +++- .../templates/tracking/visitor_visits.html | 6 ++- tracking/views.py | 37 ++++++++++++++----- 7 files changed, 79 insertions(+), 19 deletions(-) diff --git a/tracking/models.py b/tracking/models.py index 9a341c6..b05b9e8 100644 --- a/tracking/models.py +++ b/tracking/models.py @@ -38,7 +38,7 @@ class Visitor(models.Model): # Update to GenericIPAddress in Django 1.4 ip_address = models.CharField(max_length=39, editable=False) user_agent = models.TextField(null=True, editable=False) - start_time = models.DateTimeField(default=timezone.now, editable=False) + start_time = models.DateTimeField(default=timezone.now, editable=False, db_index=True) expiry_age = models.IntegerField(null=True, editable=False) expiry_time = models.DateTimeField(null=True, editable=False) time_on_site = models.IntegerField(null=True, editable=False) @@ -89,11 +89,11 @@ class Pageview(models.Model): related_name='pageviews', on_delete=models.CASCADE, ) - url = models.TextField(null=False, editable=False) + url = models.TextField(null=False, editable=False, db_index=True) referer = models.TextField(null=True, editable=False) query_string = models.TextField(null=True, editable=False) method = models.CharField(max_length=20, null=True) - view_time = models.DateTimeField() + view_time = models.DateTimeField(db_index=True) objects = PageviewManager() diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index fbfb1ca..b246a52 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -12,6 +12,12 @@

{{ page_url }} Details - django-tracking2

# of Unique Visitors
{{ visitors }} + {% if pageview_counts.has_previous %} + previous + {% endif %} + {% if pageview_counts.has_next %} + next + {% endif %} @@ -29,6 +35,12 @@

{{ page_url }} Details - django-tracking2

{% endfor %}
Pageviews
+ {% if pageview_counts.has_previous %} + previous + {% endif %} + {% if pageview_counts.has_next %} + next + {% endif %} \ No newline at end of file diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index ac94102..a454871 100644 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -12,6 +12,12 @@

Page Overview - django-tracking2

# of Pages Viewed
{{ total_pages }} + {% if pageview_counts.has_previous %} + previous + {% endif %} + {% if pageview_counts.has_next %} + next + {% endif %} @@ -27,6 +33,12 @@

Page Overview - django-tracking2

{% endfor %}
Pages
+ {% if pageview_counts.has_previous %} + previous + {% endif %} + {% if pageview_counts.has_next %} + next + {% endif %} \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index 2827c3a..42561cc 100644 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -1,10 +1,10 @@ -{% firstof user.get_full_name user %} {{ page_url }} Visits - django-tracking2 +{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - django-tracking2 -

{% firstof user.get_full_name user %} {{ page_url }} Visits - django-tracking2

+

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - django-tracking2

Total Views
@@ -12,8 +12,14 @@

{% firstof user.get_full_name user %} {{ page_url }} Visits - django-trackin
Avg Views/Visit
{{ avg_views_per_visit }}

+ {% if visits.has_previous %} + previous + {% endif %} + {% if visits.has_next %} + next + {% endif %} - + @@ -27,6 +33,12 @@

{% firstof user.get_full_name user %} {{ page_url }} Visits - django-trackin {% endfor %}

Visits involving {{ page_url }} by {% firstof user.get_full_name user %}Visits by {% firstof user.get_full_name user %} involving {{ page_url }}
Start Time End Time
+ {% if visits.has_previous %} + previous + {% endif %} + {% if visits.has_next %} + next + {% endif %}
\ No newline at end of file diff --git a/tracking/templates/tracking/visitor_pageview_detail.html b/tracking/templates/tracking/visitor_pageview_detail.html index 212d2ea..bb0df2b 100644 --- a/tracking/templates/tracking/visitor_pageview_detail.html +++ b/tracking/templates/tracking/visitor_pageview_detail.html @@ -1,15 +1,18 @@ -{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %} {{ pageview.view_time|date:"Y-m-d H:i:s" }} {{ pageview.url }} Pageview - django-tracking2 +{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %}'s View of {{ pageview.url }} at {{ pageview.view_time|date:"Y-m-d H:i:s" }} - django-tracking2 -

{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %} {{ pageview.view_time|date:"Y-m-d H:i:s" }} {{ pageview.url }} Pageview - django-tracking2

+

{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %}'s View of {{ pageview.url }} at {{ pageview.view_time|date:"Y-m-d H:i:s" }} - django-tracking2

Method
{{ pageview.method }} +
Duration
+
{{ duration }}
+ Page details across all visitors \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index 73da4ac..910b853 100644 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -1,16 +1,18 @@ -{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2 +{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Detail - django-tracking2 -

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Overview - django-tracking2

+

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} Visit Detail - django-tracking2

Duration
{{ visit.time_on_site }}
# of Page Views
{{ pvcount }} +
Avg Time/Page
+
{{ avg_time_per_page|default_if_none:"n/a" }}
IP
{{ visit.ip_address }}
User Agent
diff --git a/tracking/views.py b/tracking/views.py index 133d13b..8c1b58a 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -103,6 +103,8 @@ def visitor_overview(request, user_id): # queries take `date` objects (for now) user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() + if user: + user.time_on_site = timedelta(seconds=user.time_on_site) visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) paginator = Paginator(visits, 100) @@ -120,6 +122,7 @@ def visitor_visits(request, visit_id): pvpage = request.GET.get('pvpage', 1) pvspage = request.GET.get('pvspage', 1) visit = get_object_or_404(Visitor, pk=visit_id) + visit.time_on_site = timedelta(seconds=visit.time_on_site) pvcount = visit.pageviews.count() pageviews = visit.pageviews.order_by('-view_time') pageview_stats = visit.pageviews.values('url').annotate(views=Count('url')).order_by('-views') @@ -131,6 +134,7 @@ def visitor_visits(request, visit_id): 'pageviews': pvpaginator.page(pvpage), 'pageview_stats': pvspaginator.page(pvspage), 'pvcount': pvcount, + 'avg_time_per_page': visit.time_on_site/pvcount if pvcount else None } return render(request, 'tracking/visitor_visits.html', context) @@ -140,13 +144,14 @@ def visitor_page_detail(request, user_id): page_url = request.GET['page_url'] except: return HttpResponseNotFound() + page = request.GET.get('page', 1) user = get_object_or_404(get_user_model(), pk=user_id) aggs = Visitor.objects.filter( pageviews__url=page_url, user__pk=user_id, - ).values('pk').annotate(views=Count('pageciews')).aggregate( - Avg('pageviews__count'), - Sum('pageviews__count') + ).values('pk').annotate(views=Count('pageviews')).aggregate( + Avg('views'), + Sum('views') ) visits = Visitor.objects.filter( pageviews__url=page_url, @@ -155,11 +160,12 @@ def visitor_page_detail(request, user_id): 'end_time', 'start_time' ) + paginator = Paginator(visits, 100) context = { - 'total_views': aggs['pageviews__count__sum'], - 'avg_views_per_visit': aggs['pageviews__count__avg'], - 'visits': visits, + 'total_views': aggs['views__sum'], + 'avg_views_per_visit': aggs['views__avg'], + 'visits': paginator.page(page), 'user': user, 'page_url': page_url, } @@ -168,37 +174,50 @@ def visitor_page_detail(request, user_id): @permission_required('tracking.visitor_log') def visitor_pageview_detail(request, user_id, pageview_id): pageview = get_object_or_404(Pageview, pk=pageview_id, visitor__user_id=user_id) + next_pv = Pageview.objects.filter( + visitor__user_id=user_id, + view_time__gt=pageview.view_time, + ).order_by('view_time').first() + if next_pv: + duration = next_pv.view_time - pageview.view_time + else: + duration = None context = { 'pageview': pageview, + 'duration': duration, } return render(request, 'tracking/visitor_pageview_detail.html', context) @permission_required('tracking.visitor_log') def page_overview(request): + page = request.GET.get('page', 1) pageview_counts = Pageview.objects.values('url').annotate(views=Count('url')).order_by('-views') + paginator = Paginator(pageview_counts, 100) context = { - 'pageview_counts': pageview_counts, + 'pageview_counts': paginator.page(page), 'total_page_views': reduce(lambda acc, c: acc + c['views'], pageview_counts, 0), 'total_pages': len(pageview_counts), } return render(request, 'tracking/page_overview.html', context) @permission_required('tracking.visitor_log') -def page_detail(request, page_url): +def page_detail(request): try: page_url = request.GET['page_url'] except: return HttpResponseNotFound() + page = request.GET.get('page', 1) pageviews = Pageview.objects.filter(url=page_url).order_by('-view_time') pv_count = pageviews.count() uniqueVisitors = Pageview.objects.values('visitor_id').distinct().count() + paginator = Paginator(pageviews, 100) context = { 'total_views': pv_count, 'visitors': uniqueVisitors, - 'pageviews': pageviews.order_by('-view_time'), + 'pageviews': paginator.page(page), 'page_url': page_url, } return render(request, 'tracking/page_detail.html', context) From 2487d1938aa961648e453a384a4cfa047c56a805 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 05:34:50 -0500 Subject: [PATCH 10/28] Fixed some formatting, and added tooltips to links. --- tracking/templates/tracking/dashboard.html | 1 + tracking/templates/tracking/page_detail.html | 6 +++--- tracking/templates/tracking/page_overview.html | 4 ++-- tracking/templates/tracking/snippets/stats.html | 2 +- tracking/templates/tracking/visitor_overview.html | 4 ++-- tracking/templates/tracking/visitor_page_detail.html | 4 ++-- tracking/templates/tracking/visitor_visits.html | 4 ++-- tracking/views.py | 2 ++ 8 files changed, 15 insertions(+), 12 deletions(-) mode change 100644 => 100755 tracking/templates/tracking/dashboard.html mode change 100644 => 100755 tracking/templates/tracking/page_overview.html mode change 100644 => 100755 tracking/templates/tracking/snippets/stats.html mode change 100644 => 100755 tracking/templates/tracking/visitor_overview.html mode change 100644 => 100755 tracking/templates/tracking/visitor_page_detail.html mode change 100644 => 100755 tracking/templates/tracking/visitor_visits.html diff --git a/tracking/templates/tracking/dashboard.html b/tracking/templates/tracking/dashboard.html old mode 100644 new mode 100755 index a4910f9..209303f --- a/tracking/templates/tracking/dashboard.html +++ b/tracking/templates/tracking/dashboard.html @@ -11,6 +11,7 @@

Dashboard - django-tracking2

+ Page centric statistics

Visitor tracking began on diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index b246a52..347cdd8 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -28,9 +28,9 @@

{{ page_url }} Details - django-tracking2

{% for pv in pageviews %} - {{ pv.view_time|date:"Y-m-d H:i:s" }} - {% firstof user.get_full_name user %} - {{ page_url }} + {{ pv.view_time|date:"Y-m-d H:i:s" }} + {% firstof user.get_full_name user %} + {{ page_url }} {% endfor %} diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html old mode 100644 new mode 100755 index a454871..f66f956 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -27,8 +27,8 @@

Page Overview - django-tracking2

{% for c in pageview_counts %} - {{ c.url }} - {{ c.views }} + {{ c.url }} + {{ c.views }} {% endfor %} diff --git a/tracking/templates/tracking/snippets/stats.html b/tracking/templates/tracking/snippets/stats.html old mode 100644 new mode 100755 index 1167764..7b75b28 --- a/tracking/templates/tracking/snippets/stats.html +++ b/tracking/templates/tracking/snippets/stats.html @@ -67,7 +67,7 @@

Registered Users

{% for user in user_stats %} - {% firstof user.get_full_name user %} + {% firstof user.get_full_name user %} {{ user.visit_count }} {{ user.time_on_site|default_if_none:"n/a" }} {{ user.pages_per_visit|floatformat|default:"n/a" }} diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html old mode 100644 new mode 100755 index 28250be..728eae6 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -48,8 +48,8 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{% for visit in visits %} - {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} + {{ visit.start_time|date:"Y-m-d H:i:s" }} + {{ visit.end_time|date:"Y-m-d H:i:s" }} {% endfor %} diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html old mode 100644 new mode 100755 index 42561cc..4b8731f --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -27,8 +27,8 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - dj {% for visit in visits %} - {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} + {{ visit.start_time|date:"Y-m-d H:i:s" }} + {{ visit.end_time|date:"Y-m-d H:i:s" }} {% endfor %} diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html old mode 100644 new mode 100755 index 910b853..8997a18 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -33,7 +33,7 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:" {% for stat in pageview_stats %} - {{ stat.url }} + {{ stat.url }} {{ stat.views }} {% endfor %} @@ -62,7 +62,7 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:" {% for view in pageviews %} {{ view.view_time|date:"Y-m-d H:i:s" }} - {{ view.url }} + {{ view.url }} {% endfor %} diff --git a/tracking/views.py b/tracking/views.py index 8c1b58a..a089cbe 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -62,6 +62,8 @@ def dashboard(request): # queries take `date` objects (for now) user_stats = Visitor.objects.user_stats(start_time, end_time) visitor_stats = Visitor.objects.stats(start_time, end_time) + for us in user_stats: + us.time_on_site = timedelta(seconds=us.time_on_site) if TRACK_PAGEVIEWS: pageview_stats = Pageview.objects.stats(start_time, end_time) else: From 444399e1a6556ca94f0aadc4ab3d69b1d4999045 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 06:03:18 -0500 Subject: [PATCH 11/28] Forgot to add migration. --- .../migrations/0003_auto_20190722_1056.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 tracking/migrations/0003_auto_20190722_1056.py diff --git a/tracking/migrations/0003_auto_20190722_1056.py b/tracking/migrations/0003_auto_20190722_1056.py new file mode 100755 index 0000000..6284b6d --- /dev/null +++ b/tracking/migrations/0003_auto_20190722_1056.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('tracking', '0002_auto_20180918_2014'), + ] + + operations = [ + migrations.AlterField( + model_name='pageview', + name='url', + field=models.TextField(editable=False, db_index=True), + ), + migrations.AlterField( + model_name='pageview', + name='view_time', + field=models.DateTimeField(db_index=True), + ), + migrations.AlterField( + model_name='visitor', + name='start_time', + field=models.DateTimeField(editable=False, db_index=True, default=django.utils.timezone.now), + ), + ] \ No newline at end of file From fd2a86eec4a99118089d78d918402c38a38d4e26 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 06:28:16 -0500 Subject: [PATCH 12/28] Removed bad migration. --- .../migrations/0003_auto_20190722_1056.py | 30 ------------------- tracking/models.py | 2 +- 2 files changed, 1 insertion(+), 31 deletions(-) delete mode 100755 tracking/migrations/0003_auto_20190722_1056.py mode change 100644 => 100755 tracking/models.py diff --git a/tracking/migrations/0003_auto_20190722_1056.py b/tracking/migrations/0003_auto_20190722_1056.py deleted file mode 100755 index 6284b6d..0000000 --- a/tracking/migrations/0003_auto_20190722_1056.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('tracking', '0002_auto_20180918_2014'), - ] - - operations = [ - migrations.AlterField( - model_name='pageview', - name='url', - field=models.TextField(editable=False, db_index=True), - ), - migrations.AlterField( - model_name='pageview', - name='view_time', - field=models.DateTimeField(db_index=True), - ), - migrations.AlterField( - model_name='visitor', - name='start_time', - field=models.DateTimeField(editable=False, db_index=True, default=django.utils.timezone.now), - ), - ] \ No newline at end of file diff --git a/tracking/models.py b/tracking/models.py old mode 100644 new mode 100755 index b05b9e8..5f76223 --- a/tracking/models.py +++ b/tracking/models.py @@ -89,7 +89,7 @@ class Pageview(models.Model): related_name='pageviews', on_delete=models.CASCADE, ) - url = models.TextField(null=False, editable=False, db_index=True) + url = models.CharField(null=False, editable=False, db_index=True, max_length=32779) referer = models.TextField(null=True, editable=False) query_string = models.TextField(null=True, editable=False) method = models.CharField(max_length=20, null=True) From 07be2b760253e689917bce39d2580a22fdeaa8a5 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 07:06:07 -0500 Subject: [PATCH 13/28] Fixing migration. --- tracking/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracking/models.py b/tracking/models.py index 5f76223..9040f63 100755 --- a/tracking/models.py +++ b/tracking/models.py @@ -89,7 +89,7 @@ class Pageview(models.Model): related_name='pageviews', on_delete=models.CASCADE, ) - url = models.CharField(null=False, editable=False, db_index=True, max_length=32779) + url = models.CharField(null=False, editable=False, db_index=True, max_length=2048) referer = models.TextField(null=True, editable=False) query_string = models.TextField(null=True, editable=False) method = models.CharField(max_length=20, null=True) From ef9d73b14639643709291f55fadcd5b4f164a3ef Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 08:15:04 -0500 Subject: [PATCH 14/28] Setting url field back to text field. Looks like can't index URLs. --- tracking/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracking/models.py b/tracking/models.py index 9040f63..a02b3ba 100755 --- a/tracking/models.py +++ b/tracking/models.py @@ -89,7 +89,7 @@ class Pageview(models.Model): related_name='pageviews', on_delete=models.CASCADE, ) - url = models.CharField(null=False, editable=False, db_index=True, max_length=2048) + url = models.TextField(null=False, editable=False) referer = models.TextField(null=True, editable=False) query_string = models.TextField(null=True, editable=False) method = models.CharField(max_length=20, null=True) From bb5d0bffe59cd9cc5351e98b1858e65f9c054494 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Mon, 22 Jul 2019 09:20:16 -0500 Subject: [PATCH 15/28] Added migrations. --- .../migrations/0003_auto_20190722_1418.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 tracking/migrations/0003_auto_20190722_1418.py diff --git a/tracking/migrations/0003_auto_20190722_1418.py b/tracking/migrations/0003_auto_20190722_1418.py new file mode 100755 index 0000000..d871770 --- /dev/null +++ b/tracking/migrations/0003_auto_20190722_1418.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('tracking', '0002_auto_20180918_2014'), + ] + + operations = [ + migrations.AlterField( + model_name='pageview', + name='view_time', + field=models.DateTimeField(db_index=True), + ), + migrations.AlterField( + model_name='visitor', + name='start_time', + field=models.DateTimeField(editable=False, default=django.utils.timezone.now, db_index=True), + ), + ] \ No newline at end of file From 2847fffb72b6944ee6fce2889c609bff4e0c3ffd Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Tue, 23 Jul 2019 11:14:22 -0500 Subject: [PATCH 16/28] Fixed url to page overview. --- tracking/templates/tracking/page_overview.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index f66f956..5fb7eb1 100755 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -13,10 +13,10 @@

Page Overview - django-tracking2

{{ total_pages }} {% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %} @@ -34,10 +34,10 @@

Page Overview - django-tracking2

Pages
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %}

From b9eef5950188603d277808405ffdf6d74e89b701 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Tue, 23 Jul 2019 15:27:01 -0500 Subject: [PATCH 17/28] Fixed paging on visitor_visits. --- tracking/templates/tracking/visitor_visits.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index 8997a18..fda7798 100755 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -19,10 +19,10 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"
{{ visit.user_agent }} {% if pageview_stats.has_previous %} - previous + previous {% endif %} {% if pageview_stats.has_next %} - next + next {% endif %} @@ -40,17 +40,17 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"

Views/Page
{% if pageview_stats.has_previous %} - previous + previous {% endif %} {% if pageview_stats.has_next %} - next + next {% endif %} {% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %} @@ -68,10 +68,10 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"

Page Views
{% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %} From 9f244fa7ddd2df8d31afd33a0783bb71974cb5d0 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Wed, 24 Jul 2019 14:25:30 -0500 Subject: [PATCH 18/28] Added time range filters to all appropriate views. --- tracking/managers.py | 2 - tracking/templates/tracking/page_detail.html | 7 ++ .../templates/tracking/page_overview.html | 14 ++- .../tracking/visitor_page_detail.html | 7 ++ tracking/views.py | 96 +++++++++++++++++-- 5 files changed, 111 insertions(+), 15 deletions(-) diff --git a/tracking/managers.py b/tracking/managers.py index 9d21e2d..1a43b25 100644 --- a/tracking/managers.py +++ b/tracking/managers.py @@ -167,8 +167,6 @@ def user_stats(self, start_date=None, end_date=None): user_kwargs['visit_history__start_time__isnull'] = False visit_kwargs['start_time__isnull'] = False - log.critical(visit_kwargs) - users = get_user_model().objects.filter(**user_kwargs).annotate( visit_count=Count('visit_history', distinct=True), time_on_site=Avg('visit_history__time_on_site'), diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index 347cdd8..e1815c3 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -5,6 +5,13 @@

{{ page_url }} Details - django-tracking2

+
+
+ + {{ form.as_table }} + +
+
Total Views
diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index 5fb7eb1..a83f8ea 100755 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -5,6 +5,12 @@

Page Overview - django-tracking2

+
+
+ {{ form.as_table }} + +
+
Total Page Views
@@ -13,10 +19,10 @@

Page Overview - django-tracking2

{{ total_pages }}
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %} @@ -34,10 +40,10 @@

Page Overview - django-tracking2

Pages
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %}
diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index 4b8731f..49e9075 100755 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -5,6 +5,13 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - django-tracking2

+
+
+ + {{ form.as_table }} + +
+
Total Views
diff --git a/tracking/views.py b/tracking/views.py index a089cbe..c66aeb5 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -146,19 +146,43 @@ def visitor_page_detail(request, user_id): page_url = request.GET['page_url'] except: return HttpResponseNotFound() + + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + + form = DashboardForm(data=(request.GET if 'end' in request.GET else None) or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + page = request.GET.get('page', 1) user = get_object_or_404(get_user_model(), pk=user_id) - aggs = Visitor.objects.filter( + relevant_visits = Visitor.objects.filter( pageviews__url=page_url, user__pk=user_id, - ).values('pk').annotate(views=Count('pageviews')).aggregate( + start_time__lt=end_time, + ) + if start_time: + relevant_visits = relevant_visits.filter(start_time__gte=start_time) + else: + relevant_visits = relevant_visits.filter(start_time__isnull=False) + + aggs = relevant_visits.values('pk').annotate(views=Count('pageviews')).aggregate( Avg('views'), Sum('views') ) - visits = Visitor.objects.filter( - pageviews__url=page_url, - user__pk=user_id, - ).distinct().order_by( + visits = relevant_visits.distinct().order_by( 'end_time', 'start_time' ) @@ -170,6 +194,9 @@ def visitor_page_detail(request, user_id): 'visits': paginator.page(page), 'user': user, 'page_url': page_url, + 'form': form, + 'track_start_time': track_start_time, + 'warn_incomplete': warn_incomplete, } return render(request, 'tracking/visitor_page_detail.html', context) @@ -193,14 +220,39 @@ def visitor_pageview_detail(request, user_id, pageview_id): @permission_required('tracking.visitor_log') def page_overview(request): + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + + form = DashboardForm(data=request.GET or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + page = request.GET.get('page', 1) - pageview_counts = Pageview.objects.values('url').annotate(views=Count('url')).order_by('-views') + relevant_pvs = Pageview.objects.filter(view_time__lt=end_time) + if start_time: + relevant_pvs = relevant_pvs.filter(view_time__gte=start_time) + pageview_counts = relevant_pvs.values('url').annotate(views=Count('url')).order_by('-views') paginator = Paginator(pageview_counts, 100) context = { 'pageview_counts': paginator.page(page), 'total_page_views': reduce(lambda acc, c: acc + c['views'], pageview_counts, 0), 'total_pages': len(pageview_counts), + 'form': form, + 'track_start_time': track_start_time, + 'warn_incomplete': warn_incomplete, } return render(request, 'tracking/page_overview.html', context) @@ -210,10 +262,33 @@ def page_detail(request): page_url = request.GET['page_url'] except: return HttpResponseNotFound() + + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + + form = DashboardForm(data=(request.GET if 'end' in request.GET else None) or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + page = request.GET.get('page', 1) - pageviews = Pageview.objects.filter(url=page_url).order_by('-view_time') + relevant_pvs = Pageview.objects.filter(view_time__lt=end_time) + if start_time: + relevant_pvs = relevant_pvs.filter(view_time__gte=start_time) + pageviews = relevant_pvs.filter(url=page_url).order_by('-view_time') pv_count = pageviews.count() - uniqueVisitors = Pageview.objects.values('visitor_id').distinct().count() + uniqueVisitors = relevant_pvs.values('visitor_id').distinct().count() paginator = Paginator(pageviews, 100) context = { @@ -221,5 +296,8 @@ def page_detail(request): 'visitors': uniqueVisitors, 'pageviews': paginator.page(page), 'page_url': page_url, + 'form': form, + 'track_start_time': track_start_time, + 'warn_incomplete': warn_incomplete, } return render(request, 'tracking/page_detail.html', context) From dc968e705444ae0a49d9245f227094a99a37aed7 Mon Sep 17 00:00:00 2001 From: Edward Turpin Date: Fri, 26 Jul 2019 23:12:09 -0500 Subject: [PATCH 19/28] Made date filters to carry over to consecutive views. --- tracking/templates/tracking/page_detail.html | 15 +-- .../templates/tracking/page_overview.html | 12 +- .../templates/tracking/snippets/stats.html | 2 +- .../templates/tracking/visitor_overview.html | 12 +- .../tracking/visitor_page_detail.html | 12 +- .../templates/tracking/visitor_visits.html | 112 +++++++++--------- tracking/views.py | 10 ++ 7 files changed, 95 insertions(+), 80 deletions(-) diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index e1815c3..3035a1b 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -20,10 +20,10 @@

{{ page_url }} Details - django-tracking2

{{ visitors }}
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %} @@ -35,18 +35,19 @@

{{ page_url }} Details - django-tracking2

{% for pv in pageviews %} - - - + + + + {% endfor %}
Pageviews
{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof user.get_full_name user %}{{ page_url }}{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof user.get_full_name user %}{{ page_url }}
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %}
diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index a83f8ea..a3afa5f 100755 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -19,10 +19,10 @@

Page Overview - django-tracking2

{{ total_pages }}
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %} @@ -33,17 +33,17 @@

Page Overview - django-tracking2

{% for c in pageview_counts %} - - + + {% endfor %}
Pages
{{ c.url }}{{ c.views }}{{ c.url }}{{ c.views }}
{% if pageview_counts.has_previous %} - previous + previous {% endif %} {% if pageview_counts.has_next %} - next + next {% endif %}
diff --git a/tracking/templates/tracking/snippets/stats.html b/tracking/templates/tracking/snippets/stats.html index 7b75b28..a0f0d2d 100755 --- a/tracking/templates/tracking/snippets/stats.html +++ b/tracking/templates/tracking/snippets/stats.html @@ -67,7 +67,7 @@

Registered Users

{% for user in user_stats %} - {% firstof user.get_full_name user %} + {% firstof user.get_full_name user %} {{ user.visit_count }} {{ user.time_on_site|default_if_none:"n/a" }} {{ user.pages_per_visit|floatformat|default:"n/a" }} diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index 728eae6..8e35d97 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -34,10 +34,10 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{% if visits.has_previous %} - previous + previous {% endif %} {% if visits.has_next %} - next + next {% endif %} @@ -48,17 +48,17 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{% for visit in visits %} - - + + {% endfor %}
Visits by {% firstof user.get_full_name user %}
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
{% if visits.has_previous %} - previous + previous {% endif %} {% if visits.has_next %} - next + next {% endif %} diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index 49e9075..a83d856 100755 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -20,10 +20,10 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - dj
{{ avg_views_per_visit }} {% if visits.has_previous %} - previous + previous {% endif %} {% if visits.has_next %} - next + next {% endif %} @@ -34,17 +34,17 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - dj

{% for visit in visits %} - - + + {% endfor %}
Visits by {% firstof user.get_full_name user %} involving {{ page_url }}
{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}{{ visit.start_time|date:"Y-m-d H:i:s" }}{{ visit.end_time|date:"Y-m-d H:i:s" }}
{% if visits.has_previous %} - previous + previous {% endif %} {% if visits.has_next %} - next + next {% endif %} diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index fda7798..1d3f4f7 100755 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -18,61 +18,65 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"
User Agent
{{ visit.user_agent }} - {% if pageview_stats.has_previous %} - previous - {% endif %} - {% if pageview_stats.has_next %} - next - {% endif %} - - - - - - - - {% for stat in pageview_stats %} - - - - - {% endfor %} - -
Views/Page
URL# of views
{{ stat.url }}{{ stat.views }}
- {% if pageview_stats.has_previous %} - previous - {% endif %} - {% if pageview_stats.has_next %} - next - {% endif %} - - {% if pageviews.has_previous %} - previous - {% endif %} - {% if pageviews.has_next %} - next - {% endif %} - - - - - - - - {% for view in pageviews %} - - - - +
+ {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} +
Page Views
TimeURL
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}
+ + + + + + + {% for stat in pageview_stats %} + + + + {% endfor %} - -
Views/Page
URL# of views
{{ stat.url }}{{ stat.views }}
- {% if pageviews.has_previous %} - previous - {% endif %} - {% if pageviews.has_next %} - next - {% endif %} + + + {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} + + +
+ {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} + + + + + + + + {% for view in pageviews %} + + + + + {% endfor %} + +
Page Views
TimeURL
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}
+ {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} +
\ No newline at end of file diff --git a/tracking/views.py b/tracking/views.py index c66aeb5..58c9c6e 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -76,6 +76,8 @@ def dashboard(request): 'user_stats': user_stats, 'visitor_stats': visitor_stats, 'pageview_stats': pageview_stats, + 'start_time': start_time, + 'end_time': end_time, } return render(request, 'tracking/dashboard.html', context) @@ -116,6 +118,8 @@ def visitor_overview(request, user_id): 'warn_incomplete': warn_incomplete, 'visits': paginator.page(page), 'user': user, + 'start_time': start_time, + 'end_time': end_time, } return render(request, 'tracking/visitor_overview.html', context) @@ -197,6 +201,8 @@ def visitor_page_detail(request, user_id): 'form': form, 'track_start_time': track_start_time, 'warn_incomplete': warn_incomplete, + 'start_time': start_time, + 'end_time': end_time, } return render(request, 'tracking/visitor_page_detail.html', context) @@ -253,6 +259,8 @@ def page_overview(request): 'form': form, 'track_start_time': track_start_time, 'warn_incomplete': warn_incomplete, + 'start_time': start_time, + 'end_time': end_time, } return render(request, 'tracking/page_overview.html', context) @@ -299,5 +307,7 @@ def page_detail(request): 'form': form, 'track_start_time': track_start_time, 'warn_incomplete': warn_incomplete, + 'start_time': start_time, + 'end_time': end_time, } return render(request, 'tracking/page_detail.html', context) From de80b3d7693e081a62a41cf818e93349f1d7e185 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 2 Aug 2019 10:58:19 -0500 Subject: [PATCH 20/28] Fexed page_detal.html --- tracking/templates/tracking/page_detail.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index e1815c3..c24ab74 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -19,10 +19,10 @@

{{ page_url }} Details - django-tracking2

# of Unique Visitors
{{ visitors }} - {% if pageview_counts.has_previous %} + {% if pageviews.has_previous %} previous {% endif %} - {% if pageview_counts.has_next %} + {% if pageviews.has_next %} next {% endif %} @@ -35,17 +35,17 @@

{{ page_url }} Details - django-tracking2

{% for pv in pageviews %} - - - + + + {% endfor %}
{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof user.get_full_name user %}{{ page_url }}{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof pv.visitor.user.get_full_name pv.visitor.user %}{{ page_url }}
- {% if pageview_counts.has_previous %} + {% if pageviews.has_previous %} previous {% endif %} - {% if pageview_counts.has_next %} + {% if pageviews.has_next %} next {% endif %} From c67b99d231d98304cf7963f689ad3d14ae41758d Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 2 Aug 2019 14:34:18 -0500 Subject: [PATCH 21/28] Refactoring code for DRY principle. Adding checks for enabled and disabled settings. --- tracking/settings.py | 2 + tracking/templates/tracking/dashboard.html | 23 +-- tracking/templates/tracking/page_detail.html | 8 +- .../templates/tracking/page_overview.html | 7 +- .../tracking/snippets/tracking_filters.html | 18 +++ .../templates/tracking/visitor_overview.html | 25 +--- .../tracking/visitor_page_detail.html | 8 +- .../tracking/visitor_pageview_detail.html | 4 + .../templates/tracking/visitor_visits.html | 118 ++++++++------- tracking/utils.py | 37 +++++ tracking/views.py | 141 +++--------------- 11 files changed, 158 insertions(+), 233 deletions(-) create mode 100644 tracking/templates/tracking/snippets/tracking_filters.html diff --git a/tracking/settings.py b/tracking/settings.py index 764f295..e9e7780 100644 --- a/tracking/settings.py +++ b/tracking/settings.py @@ -21,3 +21,5 @@ TRACK_REFERER = getattr(settings, 'TRACK_REFERER', False) TRACK_QUERY_STRING = getattr(settings, 'TRACK_QUERY_STRING', False) + +TRACKING2_PAGING_SIZE = getattr(settings, 'TRACKING2_PAGING_SIZE', 100) \ No newline at end of file diff --git a/tracking/templates/tracking/dashboard.html b/tracking/templates/tracking/dashboard.html index 209303f..57532f0 100755 --- a/tracking/templates/tracking/dashboard.html +++ b/tracking/templates/tracking/dashboard.html @@ -5,25 +5,10 @@

Dashboard - django-tracking2

-
-
- {{ form.as_table }} - -
-
- Page centric statistics -
-

- Visitor tracking began on - {{ track_start_time|date:"Y-m-d H:i:s" }} -

- {% if warn_incomplete %} -

- The start time precedes the oldest tracked visitor, thus - the stats are not complete for the specified range. -

- {% endif %} -
+ {% include "tracking/snippets/tracking_filters.html" %} + {% if has_pageviews %} + Page centric statistics + {% endif %}
{% include "tracking/snippets/stats.html" %}
diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index c24ab74..26315f8 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -5,13 +5,7 @@

{{ page_url }} Details - django-tracking2

-
-
- - {{ form.as_table }} - -
-
+ {% include "tracking/snippets/tracking_filters.html" %}
Total Views
diff --git a/tracking/templates/tracking/page_overview.html b/tracking/templates/tracking/page_overview.html index a83f8ea..a358b0d 100755 --- a/tracking/templates/tracking/page_overview.html +++ b/tracking/templates/tracking/page_overview.html @@ -5,12 +5,7 @@

Page Overview - django-tracking2

-
-
- {{ form.as_table }} - -
-
+ {% include "tracking/snippets/tracking_filters.html" %}
Total Page Views
diff --git a/tracking/templates/tracking/snippets/tracking_filters.html b/tracking/templates/tracking/snippets/tracking_filters.html new file mode 100644 index 0000000..249ed3f --- /dev/null +++ b/tracking/templates/tracking/snippets/tracking_filters.html @@ -0,0 +1,18 @@ +
+
+ {{ form.as_table }} + +
+
+
+

+ Visitor tracking began on + {{ track_start_time|date:"Y-m-d H:i:s" }} +

+ {% if warn_incomplete %} +

+ The start time precedes the oldest tracked visitor, thus + the stats are not complete for the specified range. +

+ {% endif %} +
\ No newline at end of file diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index 728eae6..b3f73c2 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -5,24 +5,7 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

-
-
- {{ form.as_table }} - -
-
-
-

- Visitor tracking began on - {{ track_start_time|date:"Y-m-d H:i:s" }} -

- {% if warn_incomplete %} -

- The start time precedes the oldest tracked visitor, thus - the stats are not complete for the specified range. -

- {% endif %} -
+ {% include "tracking/snippets/tracking_filters.html" %}
# Visits
@@ -44,12 +27,18 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

Start Time End Time + {% if has_geoip %} + Location + {% endif %} {% for visit in visits %} {{ visit.start_time|date:"Y-m-d H:i:s" }} {{ visit.end_time|date:"Y-m-d H:i:s" }} + {% if has_geoip %} + {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} + {% endif %} {% endfor %} diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index 49e9075..f769288 100755 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -5,13 +5,7 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - django-tracking2

-
-
- - {{ form.as_table }} - -
-
+ {% include "tracking/snippets/tracking_filters.html" %}
Total Views
diff --git a/tracking/templates/tracking/visitor_pageview_detail.html b/tracking/templates/tracking/visitor_pageview_detail.html index bb0df2b..beea711 100644 --- a/tracking/templates/tracking/visitor_pageview_detail.html +++ b/tracking/templates/tracking/visitor_pageview_detail.html @@ -11,6 +11,10 @@

{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %}
{{ pageview.method }}
Duration
{{ duration }} + {% if pageview.query_string %} +
Query String
+
{{ pageview.querystring }} + {% endif %}

Page details across all visitors diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_visits.html index fda7798..61f2774 100755 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_visits.html @@ -10,7 +10,7 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"
Duration
{{ visit.time_on_site }}
# of Page Views
-
{{ pvcount }} +
{{ pvcount|default:"n/a" }}
Avg Time/Page
{{ avg_time_per_page|default_if_none:"n/a" }}
IP
@@ -18,61 +18,67 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"
User Agent
{{ visit.user_agent }}

- {% if pageview_stats.has_previous %} - previous - {% endif %} - {% if pageview_stats.has_next %} - next - {% endif %} - - - - - - - - {% for stat in pageview_stats %} - - - - - {% endfor %} - -
Views/Page
URL# of views
{{ stat.url }}{{ stat.views }}
- {% if pageview_stats.has_previous %} - previous - {% endif %} - {% if pageview_stats.has_next %} - next - {% endif %} - - {% if pageviews.has_previous %} - previous - {% endif %} - {% if pageviews.has_next %} - next - {% endif %} - - - - - - - - {% for view in pageviews %} - - - - - {% endfor %} - -
Page Views
TimeURL
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}
- {% if pageviews.has_previous %} - previous - {% endif %} - {% if pageviews.has_next %} - next - {% endif %} + {% if pageview_stats.count %) +
+ {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} + + + + + + + + {% for stat in pageview_stats %} + + + + + {% endfor %} + +
Views/Page
URL# of views
{{ stat.url }}{{ stat.views }}
+ {% if pageview_stats.has_previous %} + previous + {% endif %} + {% if pageview_stats.has_next %} + next + {% endif %} +
+ +
+ {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} + + + + + + + + {% for view in pageviews %} + + + + + {% endfor %} + +
Page Views
TimeURL
{{ view.view_time|date:"Y-m-d H:i:s" }}{{ view.url }}
+ {% if pageviews.has_previous %} + previous + {% endif %} + {% if pageviews.has_next %} + next + {% endif %} +
+ {% endif %)
\ No newline at end of file diff --git a/tracking/utils.py b/tracking/utils.py index bf36af5..61b0507 100644 --- a/tracking/utils.py +++ b/tracking/utils.py @@ -1,14 +1,33 @@ from __future__ import division +from datetime import timedelta + +from django import forms +from django.utils.timezone import now from django.core.exceptions import ValidationError from django.core.validators import validate_ipv46_address +from tracking.models import Visitor + headers = ( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTERED_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ) +# tracking wants to accept more formats than default, here they are +input_formats = [ + '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' + '%Y-%m-%d %H:%M', # '2006-10-25 14:30' + '%Y-%m-%d', # '2006-10-25' + '%Y-%m', # '2006-10' + '%Y', # '2006' +] + + +class TimeRangeForm(forms.Form): + start = forms.DateTimeField(required=False, input_formats=input_formats) + end = forms.DateTimeField(required=False, input_formats=input_formats) def get_ip_address(request): for header in headers: @@ -25,3 +44,21 @@ def get_ip_address(request): def total_seconds(delta): day_seconds = (delta.days * 24 * 3600) + delta.seconds return (delta.microseconds + day_seconds * 10**6) / 10**6 + +def processTimeRangeForm(request): + end_time = now() + start_time = end_time - timedelta(days=7) + defaults = {'start': start_time, 'end': end_time} + form = TimeRangeForm(data=(request.GET if 'end' in request.GET else None) or defaults) + if form.is_valid(): + start_time = form.cleaned_data['start'] + end_time = form.cleaned_data['end'] + # determine when tracking began + try: + obj = Visitor.objects.order_by('start_time')[0] + track_start_time = obj.start_time + except (IndexError, Visitor.DoesNotExist): + track_start_time = now() + # If the start_date is before tracking began, warn about incomplete data + warn_incomplete = (start_time < track_start_time) + return (start_time, end_time, track_start_time, warn_incomplete, form) \ No newline at end of file diff --git a/tracking/views.py b/tracking/views.py index c66aeb5..77c8fa0 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -1,12 +1,8 @@ import logging -from _collections import OrderedDict from datetime import timedelta -from statistics import mean from functools import reduce -from operator import add -from django import forms from django.shortcuts import ( render, get_object_or_404, @@ -14,50 +10,22 @@ from django.http import HttpResponseNotFound from django.contrib.auth import get_user_model from django.contrib.auth.decorators import permission_required -from django.utils.timezone import now from django.db.models import Count, Avg, Sum from django.core.paginator import Paginator from tracking.models import Visitor, Pageview -from tracking.settings import TRACK_PAGEVIEWS +from tracking.settings import ( + TRACK_PAGEVIEWS, + TRACKING2_PAGING_SIZE, + TRACK_USING_GEOIP, +) +from tracking.utils import processTimeRangeForm log = logging.getLogger(__file__) -# tracking wants to accept more formats than default, here they are -input_formats = [ - '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' - '%Y-%m-%d %H:%M', # '2006-10-25 14:30' - '%Y-%m-%d', # '2006-10-25' - '%Y-%m', # '2006-10' - '%Y', # '2006' -] - -class DashboardForm(forms.Form): - start = forms.DateTimeField(required=False, input_formats=input_formats) - end = forms.DateTimeField(required=False, input_formats=input_formats) - - @permission_required('tracking.visitor_log') def dashboard(request): - "Counts, aggregations and more!" - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=request.GET or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) + (start_time, end_time, track_start_time, warn_incomplete, form) = processTimeRangeForm(request) # queries take `date` objects (for now) user_stats = Visitor.objects.user_stats(start_time, end_time) @@ -76,39 +44,21 @@ def dashboard(request): 'user_stats': user_stats, 'visitor_stats': visitor_stats, 'pageview_stats': pageview_stats, + 'has_pageviews': TRACK_PAGEVIEWS, } return render(request, 'tracking/dashboard.html', context) @permission_required('tracking.visitor_log') def visitor_overview(request, user_id): - "Counts, aggregations and more!" -# user = get_object_or_404(get_user_model(), pk=user_id) - page = request.GET.get('page', 1) - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=request.GET or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) + (start_time, end_time, track_start_time, warn_incomplete, form) = processTimeRangeForm(request) + page = request.GET.get('page', 1) # queries take `date` objects (for now) user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() if user: user.time_on_site = timedelta(seconds=user.time_on_site) visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) - paginator = Paginator(visits, 100) + paginator = Paginator(visits, TRACKING2_PAGING_SIZE) context = { 'form': form, @@ -116,6 +66,7 @@ def visitor_overview(request, user_id): 'warn_incomplete': warn_incomplete, 'visits': paginator.page(page), 'user': user, + 'has_geoip': TRACK_USING_GEOIP, } return render(request, 'tracking/visitor_overview.html', context) @@ -128,8 +79,8 @@ def visitor_visits(request, visit_id): pvcount = visit.pageviews.count() pageviews = visit.pageviews.order_by('-view_time') pageview_stats = visit.pageviews.values('url').annotate(views=Count('url')).order_by('-views') - pvspaginator = Paginator(pageview_stats, 100) - pvpaginator = Paginator(pageviews, 100) + pvspaginator = Paginator(pageview_stats, TRACKING2_PAGING_SIZE) + pvpaginator = Paginator(pageviews, TRACKING2_PAGING_SIZE) context = { 'visit': visit, @@ -147,24 +98,7 @@ def visitor_page_detail(request, user_id): except: return HttpResponseNotFound() - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=(request.GET if 'end' in request.GET else None) or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) + (start_time, end_time, track_start_time, warn_incomplete, form) = processTimeRangeForm(request) page = request.GET.get('page', 1) user = get_object_or_404(get_user_model(), pk=user_id) @@ -186,7 +120,7 @@ def visitor_page_detail(request, user_id): 'end_time', 'start_time' ) - paginator = Paginator(visits, 100) + paginator = Paginator(visits, TRACKING2_PAGING_SIZE) context = { 'total_views': aggs['views__sum'], @@ -220,35 +154,19 @@ def visitor_pageview_detail(request, user_id, pageview_id): @permission_required('tracking.visitor_log') def page_overview(request): - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=request.GET or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) + (start_time, end_time, track_start_time, warn_incomplete, form) = processTimeRangeForm(request) page = request.GET.get('page', 1) relevant_pvs = Pageview.objects.filter(view_time__lt=end_time) if start_time: relevant_pvs = relevant_pvs.filter(view_time__gte=start_time) pageview_counts = relevant_pvs.values('url').annotate(views=Count('url')).order_by('-views') - paginator = Paginator(pageview_counts, 100) + paginator = Paginator(pageview_counts, TRACKING2_PAGING_SIZE) context = { 'pageview_counts': paginator.page(page), 'total_page_views': reduce(lambda acc, c: acc + c['views'], pageview_counts, 0), + 'total_pages': len(pageview_counts), 'form': form, 'track_start_time': track_start_time, @@ -263,24 +181,7 @@ def page_detail(request): except: return HttpResponseNotFound() - end_time = now() - start_time = end_time - timedelta(days=7) - defaults = {'start': start_time, 'end': end_time} - - form = DashboardForm(data=(request.GET if 'end' in request.GET else None) or defaults) - if form.is_valid(): - start_time = form.cleaned_data['start'] - end_time = form.cleaned_data['end'] - - # determine when tracking began - try: - obj = Visitor.objects.order_by('start_time')[0] - track_start_time = obj.start_time - except (IndexError, Visitor.DoesNotExist): - track_start_time = now() - - # If the start_date is before tracking began, warn about incomplete data - warn_incomplete = (start_time < track_start_time) + (start_time, end_time, track_start_time, warn_incomplete, form) = processTimeRangeForm(request) page = request.GET.get('page', 1) relevant_pvs = Pageview.objects.filter(view_time__lt=end_time) @@ -289,7 +190,7 @@ def page_detail(request): pageviews = relevant_pvs.filter(url=page_url).order_by('-view_time') pv_count = pageviews.count() uniqueVisitors = relevant_pvs.values('visitor_id').distinct().count() - paginator = Paginator(pageviews, 100) + paginator = Paginator(pageviews, TRACKING2_PAGING_SIZE) context = { 'total_views': pv_count, From cb0d0f4becb804e8320184da26790a2eeebfb39f Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Mon, 5 Aug 2019 15:23:24 -0500 Subject: [PATCH 22/28] In progress of writing tests for views. --- tracking/settings.py | 2 +- ...isitor_visits.html => visitor_detail.html} | 20 +-- .../templates/tracking/visitor_overview.html | 6 +- .../tracking/visitor_page_detail.html | 10 +- .../tracking/visitor_pageview_detail.html | 6 +- tracking/tests/test_managers.py | 8 +- tracking/tests/test_views.py | 132 +++++++++++++++++- tracking/urls.py | 4 +- tracking/views.py | 20 +-- 9 files changed, 173 insertions(+), 35 deletions(-) rename tracking/templates/tracking/{visitor_visits.html => visitor_detail.html} (87%) diff --git a/tracking/settings.py b/tracking/settings.py index e9e7780..a64e6d7 100644 --- a/tracking/settings.py +++ b/tracking/settings.py @@ -22,4 +22,4 @@ TRACK_QUERY_STRING = getattr(settings, 'TRACK_QUERY_STRING', False) -TRACKING2_PAGING_SIZE = getattr(settings, 'TRACKING2_PAGING_SIZE', 100) \ No newline at end of file +TRACK_PAGING_SIZE = getattr(settings, 'TRACK_PAGING_SIZE', 100) \ No newline at end of file diff --git a/tracking/templates/tracking/visitor_visits.html b/tracking/templates/tracking/visitor_detail.html similarity index 87% rename from tracking/templates/tracking/visitor_visits.html rename to tracking/templates/tracking/visitor_detail.html index 61f2774..7765cbc 100755 --- a/tracking/templates/tracking/visitor_visits.html +++ b/tracking/templates/tracking/visitor_detail.html @@ -18,13 +18,13 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"
User Agent
{{ visit.user_agent }}

- {% if pageview_stats.count %) + {% if pvcount %}
{% if pageview_stats.has_previous %} - previous + previous {% endif %} {% if pageview_stats.has_next %} - next + next {% endif %} @@ -42,19 +42,19 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"

Views/Page
{% if pageview_stats.has_previous %} - previous + previous {% endif %} {% if pageview_stats.has_next %} - next + next {% endif %}
{% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %} @@ -72,13 +72,13 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:"

Page Views
{% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next + next {% endif %}
- {% endif %) + {% endif %}
\ No newline at end of file diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index b3f73c2..2795f48 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -34,10 +34,10 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{% for visit in visits %} - {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} + {{ visit.start_time|date:"Y-m-d H:i:s" }} + {{ visit.end_time|date:"Y-m-d H:i:s" }} {% if has_geoip %} - {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} + {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} {% endif %} {% endfor %} diff --git a/tracking/templates/tracking/visitor_page_detail.html b/tracking/templates/tracking/visitor_page_detail.html index f769288..0e26e9b 100755 --- a/tracking/templates/tracking/visitor_page_detail.html +++ b/tracking/templates/tracking/visitor_page_detail.html @@ -24,12 +24,18 @@

{% firstof user.get_full_name user %}'s Visits involving {{ page_url }} - dj Start Time End Time + {% if has_geoip %} + Location + {% endif %} {% for visit in visits %} - {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} + {{ visit.start_time|date:"Y-m-d H:i:s" }} + {{ visit.end_time|date:"Y-m-d H:i:s" }} + {% if has_geoip %} + {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} + {% endif %} {% endfor %} diff --git a/tracking/templates/tracking/visitor_pageview_detail.html b/tracking/templates/tracking/visitor_pageview_detail.html index beea711..6e1c66d 100644 --- a/tracking/templates/tracking/visitor_pageview_detail.html +++ b/tracking/templates/tracking/visitor_pageview_detail.html @@ -11,9 +11,13 @@

{% firstof pageview.visitor.user.get_full_name pageview.visitor.user.user %}
{{ pageview.method }}
Duration
{{ duration }} + {% if pageview.referer %} +
Referrer
+
{{ pageview.referer }} + {% endif %} {% if pageview.query_string %}
Query String
-
{{ pageview.querystring }} +
{{ pageview.query_string }} {% endif %}

diff --git a/tracking/tests/test_managers.py b/tracking/tests/test_managers.py index 912375c..4673132 100644 --- a/tracking/tests/test_managers.py +++ b/tracking/tests/test_managers.py @@ -84,7 +84,7 @@ def test_visitor_stats(self): # now expand the end time to include `future` as well end_time = start_time + timedelta(days=3) stats = Visitor.objects.stats(start_time, end_time) - self.assertEqual(stats['pages_per_visit'], 4 / 3) + self.assertAlmostEqual(stats['pages_per_visit'], 4 / 3, places=4) guests = { 'time_on_site': timedelta(seconds=30), 'unique': 1, @@ -156,7 +156,7 @@ def test_user_stats(self): user = stats[0] self.assertEqual(user.username, self.user2.username) self.assertEqual(user.visit_count, 1) - self.assertEqual(user.time_on_site, timedelta(seconds=30)) + self.assertAlmostEqual(user.time_on_site, 30) self.assertEqual(user.pages_per_visit, 1) # no start_time @@ -166,13 +166,13 @@ def test_user_stats(self): user1 = stats[1] self.assertEqual(user1.username, self.user1.username) self.assertEqual(user1.visit_count, 1) - self.assertEqual(user1.time_on_site, timedelta(seconds=30)) + self.assertAlmostEqual(user1.time_on_site, 30) self.assertEqual(user1.pages_per_visit, 2.0) user2 = stats[0] self.assertEqual(user2.username, self.user2.username) self.assertEqual(user2.visit_count, 1) - self.assertEqual(user2.time_on_site, timedelta(seconds=30)) + self.assertAlmostEqual(user2.time_on_site, 30) self.assertEqual(user2.pages_per_visit, 1) def test_pageview_stats(self): diff --git a/tracking/tests/test_views.py b/tracking/tests/test_views.py index f03f98d..ae52e20 100644 --- a/tracking/tests/test_views.py +++ b/tracking/tests/test_views.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import timedelta, datetime from django.contrib.admin.sites import AdminSite from django.contrib.auth.models import User @@ -11,7 +11,7 @@ from mock import patch from tracking.admin import VisitorAdmin -from tracking.models import Visitor +from tracking.models import Visitor, Pageview class ViewsTestCase(TestCase): @@ -97,6 +97,12 @@ def test_visitor_overview_times(self): '/tracking/visitors/%s/?start=2014-11&end=2014-12-01' % self.user.pk) self.assertEqual(response.status_code, 200) + def test_visitor_overview_no_records(self): + response = self.client.get( + '/tracking/visitors/%s/?start=2014-11&end=2014-12-01' % self.user.pk) + # Gracefully handle when then there are o records of visits within time range + self.assertEqual(response.status_code, 200) + def test_visitor_overview_times_bad(self): # make a non PAGEVIEW tracking request Visitor.objects.create( @@ -110,6 +116,128 @@ def test_visitor_overview_times_bad(self): self.assertEqual(response.status_code, 200) self.assertContains(response, 'Enter a valid date/time.') + def test_visitor_detail_visitor_does_not_exist(self): + response = self.client.get( + '/tracking/visits/asdf/') + self.assertEqual(response.status_code, 404) + + def test_visitor_detail_no_pageviews(self): + Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + response = self.client.get( + '/tracking/visits/%s/' % self.user.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pvcount'], 0) + + def test_visitor_detail_has_pageview(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=datetime.fromtimestamp(1565033030), + ) + response = self.client.get( + '/tracking/visits/%s/?start=2018&end=2020' % self.user.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pageviews'].count(), 1) + self.assertEqual(response.context['pageview_stats'].count(), 1) + self.assertEqual(response.context['pvcount'], 1) + self.assertEqual(response.context['visit'], visitor) + + def test_visitor_page_detail_page_does_not_exist(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + response = self.client.get( + '/tracking/visitors/%s/page/?page_url=asdf/' % self.user.pk) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['visits'].count(), 0) + self.assertEqual(response.context['total_views'], 0) + self.assertEqual(response.context['avg_views_per_visit'], 0) + self.assertEqual(response.context['visits'].count(), 1) + self.assertEqual(response.context['user'], self.user) + + def test_visitor_page_detail_user_does_not_exist(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + pv = Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=datetime.fromtimestamp(1565033030), + ) + response = self.client.get( + '/tracking/visitors/asdf/page/?page_url=%s&start=2018&end=2020' % pv.url) + self.assertEqual(response.status_code, 404) + + def test_visitor_page_detail_one_pageview(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + pv = Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=datetime.fromtimestamp(1565033030), + ) + response = self.client.get( + '/tracking/visitors/%s/page/?page_url=%s&start=2018&end=2020' % (self.user.pk, pv.url)) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['total_views'], 1) + self.assertEqual(response.context['avg_views_per_visit'], 1) + self.assertEqual(response.context['visits'].count(), 1) + self.assertEqual(response.context['user'], self.user) + self.assertEqual(response.context['page_url'], pv.url) + + def test_visitor_pageview_pageview_does_not_exist(self): + response = self.client.get( + '/tracking/visitors/lkdjf/pageview/lkdjf/') + self.assertEqual(response.status_code, 404) + + def test_visitor_pageview_one_pageview_exists(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + pv = Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=datetime.fromtimestamp(1565033030), + ) + response = self.client.get( + '/tracking/visitors/%s/pageview/%s/' % (self.user.pk, pv.pk)) + self.assertEqual(response.status_code, 200) class AdminViewTestCase(TestCase): diff --git a/tracking/urls.py b/tracking/urls.py index 5a57fd0..691e946 100644 --- a/tracking/urls.py +++ b/tracking/urls.py @@ -3,7 +3,7 @@ from tracking.views import ( dashboard, visitor_overview, - visitor_visits, + visitor_detail, visitor_page_detail, visitor_pageview_detail, page_overview, @@ -15,7 +15,7 @@ url(r'^visitors/(?P.*)/page/$', visitor_page_detail, name='tracking-visitor-page-detail'), url(r'^visitors/(?P.*)/pageview/(?P.*)/$', visitor_pageview_detail, name='tracking-pageview-detail'), url(r'^visitors/(?P.*)/$', visitor_overview, name='tracking-visitor-overview'), - url(r'^visits/(?P.*)/$', visitor_visits, name='tracking-visitor-visits'), + url(r'^visits/(?P.*)/$', visitor_detail, name='tracking-visitor-detail'), url(r'^pages/$', page_overview, name='tracking-page-overview'), url(r'^page/$', page_detail, name='tracking-page-detail'), ] diff --git a/tracking/views.py b/tracking/views.py index 77c8fa0..7f4384d 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -16,7 +16,7 @@ from tracking.models import Visitor, Pageview from tracking.settings import ( TRACK_PAGEVIEWS, - TRACKING2_PAGING_SIZE, + TRACK_PAGING_SIZE, TRACK_USING_GEOIP, ) from tracking.utils import processTimeRangeForm @@ -58,7 +58,7 @@ def visitor_overview(request, user_id): if user: user.time_on_site = timedelta(seconds=user.time_on_site) visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) - paginator = Paginator(visits, TRACKING2_PAGING_SIZE) + paginator = Paginator(visits, TRACK_PAGING_SIZE) context = { 'form': form, @@ -71,7 +71,7 @@ def visitor_overview(request, user_id): return render(request, 'tracking/visitor_overview.html', context) @permission_required('tracking.visitor_log') -def visitor_visits(request, visit_id): +def visitor_detail(request, visit_id): pvpage = request.GET.get('pvpage', 1) pvspage = request.GET.get('pvspage', 1) visit = get_object_or_404(Visitor, pk=visit_id) @@ -79,8 +79,8 @@ def visitor_visits(request, visit_id): pvcount = visit.pageviews.count() pageviews = visit.pageviews.order_by('-view_time') pageview_stats = visit.pageviews.values('url').annotate(views=Count('url')).order_by('-views') - pvspaginator = Paginator(pageview_stats, TRACKING2_PAGING_SIZE) - pvpaginator = Paginator(pageviews, TRACKING2_PAGING_SIZE) + pvspaginator = Paginator(pageview_stats, TRACK_PAGING_SIZE) + pvpaginator = Paginator(pageviews, TRACK_PAGING_SIZE) context = { 'visit': visit, @@ -89,7 +89,7 @@ def visitor_visits(request, visit_id): 'pvcount': pvcount, 'avg_time_per_page': visit.time_on_site/pvcount if pvcount else None } - return render(request, 'tracking/visitor_visits.html', context) + return render(request, 'tracking/visitor_detail.html', context) @permission_required('tracking.visitor_log') def visitor_page_detail(request, user_id): @@ -120,7 +120,7 @@ def visitor_page_detail(request, user_id): 'end_time', 'start_time' ) - paginator = Paginator(visits, TRACKING2_PAGING_SIZE) + paginator = Paginator(visits, TRACK_PAGING_SIZE) context = { 'total_views': aggs['views__sum'], @@ -131,6 +131,7 @@ def visitor_page_detail(request, user_id): 'form': form, 'track_start_time': track_start_time, 'warn_incomplete': warn_incomplete, + 'has_geoip': TRACK_USING_GEOIP, } return render(request, 'tracking/visitor_page_detail.html', context) @@ -161,12 +162,11 @@ def page_overview(request): if start_time: relevant_pvs = relevant_pvs.filter(view_time__gte=start_time) pageview_counts = relevant_pvs.values('url').annotate(views=Count('url')).order_by('-views') - paginator = Paginator(pageview_counts, TRACKING2_PAGING_SIZE) + paginator = Paginator(pageview_counts, TRACK_PAGING_SIZE) context = { 'pageview_counts': paginator.page(page), 'total_page_views': reduce(lambda acc, c: acc + c['views'], pageview_counts, 0), - 'total_pages': len(pageview_counts), 'form': form, 'track_start_time': track_start_time, @@ -190,7 +190,7 @@ def page_detail(request): pageviews = relevant_pvs.filter(url=page_url).order_by('-view_time') pv_count = pageviews.count() uniqueVisitors = relevant_pvs.values('visitor_id').distinct().count() - paginator = Paginator(pageviews, TRACKING2_PAGING_SIZE) + paginator = Paginator(pageviews, TRACK_PAGING_SIZE) context = { 'total_views': pv_count, From 5f156b5959a5efae4ef889ff1a4650b51c385cb4 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Mon, 5 Aug 2019 16:00:54 -0500 Subject: [PATCH 23/28] In progress of writing tests for views. --- tracking/tests/test_views.py | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tracking/tests/test_views.py b/tracking/tests/test_views.py index ae52e20..38b4cec 100644 --- a/tracking/tests/test_views.py +++ b/tracking/tests/test_views.py @@ -238,6 +238,69 @@ def test_visitor_pageview_one_pageview_exists(self): response = self.client.get( '/tracking/visitors/%s/pageview/%s/' % (self.user.pk, pv.pk)) self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pageview'], pv) + self.assertEqual(response.context['duration'], None) + + def test_visitor_pageview_two_pageviews(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + pv1_view_time = datetime.fromtimestamp(1565033030) + pv2_view_time = datetime.fromtimestamp(1565034030) + pv = Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=pv1_view_time, + ) + Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=pv2_view_time , + ) + response = self.client.get( + '/tracking/visitors/%s/pageview/%s/' % (self.user.pk, pv.pk)) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pageview'], pv) + self.assertEqual(response.context['duration'], pv2_view_time - pv1_view_time) + + def test_visitor_page_overview_no_pageviews(self): + response = self.client.get( + '/tracking/pages/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pageview_counts'].count(), 0) + self.assertEqual(response.context['total_page_views'], 0) + self.assertEqual(response.context['total_pages'], 0) + + def test_visitor_page_overview_one_pageviews(self): + visitor = Visitor.objects.create( + session_key='skey', + ip_address='127.0.0.1', + user=self.user, + time_on_site = 0, + ) + Pageview.objects.create( + visitor=visitor, + url='/an/url', + referer='/an/url', + query_string='?a=string', + method='PUT', + view_time=datetime.fromtimestamp(1565033030), + ) + response = self.client.get( + '/tracking/pages/?start=2018&end=2020') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['pageview_counts'].count(), 1) + self.assertEqual(response.context['total_page_views'], 1) + self.assertEqual(response.context['total_pages'], 1) class AdminViewTestCase(TestCase): From 41271663921a210471d01d652c27e09623f017fa Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Fri, 23 Aug 2019 16:26:40 -0500 Subject: [PATCH 24/28] Fixed merge and a few template bugs. --- tracking/templates/tracking/page_detail.html | 39 +++++-------------- .../tracking/snippets/tracking_filters.html | 3 ++ .../templates/tracking/visitor_detail.html | 2 + .../templates/tracking/visitor_overview.html | 5 --- tracking/views.py | 2 +- 5 files changed, 15 insertions(+), 36 deletions(-) diff --git a/tracking/templates/tracking/page_detail.html b/tracking/templates/tracking/page_detail.html index ea4c85c..1b9016c 100755 --- a/tracking/templates/tracking/page_detail.html +++ b/tracking/templates/tracking/page_detail.html @@ -13,57 +13,36 @@

{{ page_url }} Details - django-tracking2

# of Unique Visitors
{{ visitors }} -<<<<<<< HEAD - {% if pageview_counts.has_previous %} - previous - {% endif %} - {% if pageview_counts.has_next %} - next -======= {% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next ->>>>>>> 5f156b5959a5efae4ef889ff1a4650b51c385cb4 + next {% endif %} + {% for pv in pageviews %} -<<<<<<< HEAD - - - - -======= - - - ->>>>>>> 5f156b5959a5efae4ef889ff1a4650b51c385cb4 + + + + {% endfor %}
Pageviews
Time UserMethod URL
{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof user.get_full_name user %}{{ page_url }}{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof pv.visitor.user.get_full_name pv.visitor.user %}{{ page_url }}{{ pv.view_time|date:"Y-m-d H:i:s" }}{% firstof pv.visitor.user.get_full_name pv.visitor.user %}{{ pv.method }}{{ page_url }}
-<<<<<<< HEAD - {% if pageview_counts.has_previous %} - previous - {% endif %} - {% if pageview_counts.has_next %} - next -======= {% if pageviews.has_previous %} - previous + previous {% endif %} {% if pageviews.has_next %} - next ->>>>>>> 5f156b5959a5efae4ef889ff1a4650b51c385cb4 + next {% endif %} diff --git a/tracking/templates/tracking/snippets/tracking_filters.html b/tracking/templates/tracking/snippets/tracking_filters.html index 249ed3f..9c0de13 100644 --- a/tracking/templates/tracking/snippets/tracking_filters.html +++ b/tracking/templates/tracking/snippets/tracking_filters.html @@ -1,6 +1,9 @@
{{ form.as_table }} + {% if page_url %} + + {% endif %}
diff --git a/tracking/templates/tracking/visitor_detail.html b/tracking/templates/tracking/visitor_detail.html index 7765cbc..7b11161 100755 --- a/tracking/templates/tracking/visitor_detail.html +++ b/tracking/templates/tracking/visitor_detail.html @@ -60,12 +60,14 @@

{% firstof visit.user.get_full_name visit.user %} {{ visit.start_time|date:" Page Views Time + Method URL {% for view in pageviews %} {{ view.view_time|date:"Y-m-d H:i:s" }} + {{ view.method }} {{ view.url }} {% endfor %} diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index 09bcddc..c557f6b 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -34,16 +34,11 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{% for visit in visits %} -<<<<<<< HEAD - {{ visit.start_time|date:"Y-m-d H:i:s" }} - {{ visit.end_time|date:"Y-m-d H:i:s" }} -======= {{ visit.start_time|date:"Y-m-d H:i:s" }} {{ visit.end_time|date:"Y-m-d H:i:s" }} {% if has_geoip %} {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} {% endif %} ->>>>>>> 5f156b5959a5efae4ef889ff1a4650b51c385cb4 {% endfor %} diff --git a/tracking/views.py b/tracking/views.py index 5300d02..3fc0fb4 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -82,7 +82,7 @@ def visitor_detail(request, visit_id): visit.time_on_site = timedelta(seconds=visit.time_on_site) pvcount = visit.pageviews.count() pageviews = visit.pageviews.order_by('-view_time') - pageview_stats = visit.pageviews.values('url').annotate(views=Count('url')).order_by('-views') + pageview_stats = visit.pageviews.values('method', 'url').annotate(views=Count('url')).order_by('-views') pvspaginator = Paginator(pageview_stats, TRACK_PAGING_SIZE) pvpaginator = Paginator(pageviews, TRACK_PAGING_SIZE) From 211b7e2c9d6e0a32fa7eed5420fea0594409efc2 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Wed, 4 Sep 2019 13:31:36 -0500 Subject: [PATCH 25/28] Added # pageviews to visitor overview visits. MA-378. --- tracking/templates/tracking/visitor_overview.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index c557f6b..c2bae17 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -27,6 +27,7 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

Start Time End Time + Page Views {% if has_geoip %} Location {% endif %} @@ -36,6 +37,7 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

{{ visit.start_time|date:"Y-m-d H:i:s" }} {{ visit.end_time|date:"Y-m-d H:i:s" }} + {{ visit.pageviews.count }} {% if has_geoip %} {{ visit.geoip_data.city }}, {{ visit.geoip_data.region }}, {{ visit.geoip_data.country_code }} {{ visit.geoip_data.postal_code }} {% endif %} From 7f20e92b81219758ef9466abb3658b6674fa0a69 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Tue, 28 Jan 2020 12:05:24 -0600 Subject: [PATCH 26/28] Fixed erroneous results on visit overview. --- tracking/managers.py | 10 ---------- tracking/templates/tracking/visitor_overview.html | 6 +++--- tracking/views.py | 4 ++++ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/tracking/managers.py b/tracking/managers.py index 1a43b25..3b8f926 100644 --- a/tracking/managers.py +++ b/tracking/managers.py @@ -177,16 +177,6 @@ def user_stats(self, start_date=None, end_date=None): get_user_model().USERNAME_FIELD, ) -# # Aggregate pageviews per visit -# for user in users: -# user.pages_per_visit = user.visit_history.filter( -# **visit_kwargs -# ).annotate( -# page_count=Count('pageviews') -# ).filter(page_count__gt=0).aggregate( -# pages_per_visit=Avg('page_count'))['pages_per_visit'] -# # Lop off the floating point, turn into timedelta -# user.time_on_site = timedelta(seconds=int(user.time_on_site)) return users diff --git a/tracking/templates/tracking/visitor_overview.html b/tracking/templates/tracking/visitor_overview.html index c2bae17..64139b9 100755 --- a/tracking/templates/tracking/visitor_overview.html +++ b/tracking/templates/tracking/visitor_overview.html @@ -9,9 +9,9 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

# Visits
-
{{ user.visit_count }} +
{{ user.visit_count|default:"n/a" }}
Avg. Time on Site
-
{{ user.time_on_site|default_if_none:"n/a" }} +
{{ user.time_on_site|default:"n/a" }}
Avg. Pages/Visit
{{ user.pages_per_visit|floatformat|default:"n/a" }}
@@ -53,4 +53,4 @@

{% firstof user.get_full_name user %} Visits Overview - django-tracking2

- \ No newline at end of file + diff --git a/tracking/views.py b/tracking/views.py index 3fc0fb4..971f3dc 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -59,8 +59,12 @@ def visitor_overview(request, user_id): user = Visitor.objects.user_stats(start_time, end_time).filter(pk=user_id).first() if user: user.time_on_site = timedelta(seconds=user.time_on_site) + else: + # User did not visit at all during this period. Need name but not stats + user = get_object_or_404(get_user_model(), pk=user_id) visits = Visitor.objects.filter(user=user, start_time__range=(start_time, end_time)) paginator = Paginator(visits, TRACK_PAGING_SIZE) + log.critical(visits) context = { 'form': form, From 17d6987186df9b8fbc45852fa1ac5c0c6d710612 Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Mon, 3 Feb 2020 15:03:49 -0600 Subject: [PATCH 27/28] Got tests passing --- tracking/tests/test_geoip.py | 2 +- tracking/tests/test_views.py | 25 +++++++++++++------------ tracking/views.py | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tracking/tests/test_geoip.py b/tracking/tests/test_geoip.py index 19920e3..911b627 100644 --- a/tracking/tests/test_geoip.py +++ b/tracking/tests/test_geoip.py @@ -31,7 +31,7 @@ dj_version = django.get_version() broken_geoip = (dj_version[:3] == '1.5') and (sys.version_info[0] == 3) - +@skipIf(not HAS_GEOIP, "Don't run tests if don't have GEOIP") class GeoIPTestCase(TestCase): def test_geoip_none(self): diff --git a/tracking/tests/test_views.py b/tracking/tests/test_views.py index 38b4cec..eff9e4e 100644 --- a/tracking/tests/test_views.py +++ b/tracking/tests/test_views.py @@ -122,14 +122,14 @@ def test_visitor_detail_visitor_does_not_exist(self): self.assertEqual(response.status_code, 404) def test_visitor_detail_no_pageviews(self): - Visitor.objects.create( + visitor = Visitor.objects.create( session_key='skey', ip_address='127.0.0.1', user=self.user, time_on_site = 0, ) response = self.client.get( - '/tracking/visits/%s/' % self.user.pk) + '/tracking/visits/%s/' % visitor.pk) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['pvcount'], 0) @@ -149,10 +149,10 @@ def test_visitor_detail_has_pageview(self): view_time=datetime.fromtimestamp(1565033030), ) response = self.client.get( - '/tracking/visits/%s/?start=2018&end=2020' % self.user.pk) + '/tracking/visits/%s/?start=2018&end=2020' % visitor.pk) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['pageviews'].count(), 1) - self.assertEqual(response.context['pageview_stats'].count(), 1) + self.assertEqual(len(response.context['pageviews']), 1) + self.assertEqual(len(response.context['pageview_stats']), 1) self.assertEqual(response.context['pvcount'], 1) self.assertEqual(response.context['visit'], visitor) @@ -162,14 +162,14 @@ def test_visitor_page_detail_page_does_not_exist(self): ip_address='127.0.0.1', user=self.user, time_on_site = 0, + start_time=datetime.fromtimestamp(1565033030), ) response = self.client.get( '/tracking/visitors/%s/page/?page_url=asdf/' % self.user.pk) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['visits'].count(), 0) self.assertEqual(response.context['total_views'], 0) self.assertEqual(response.context['avg_views_per_visit'], 0) - self.assertEqual(response.context['visits'].count(), 1) + self.assertEqual(len(response.context['visits']), 0) self.assertEqual(response.context['user'], self.user) def test_visitor_page_detail_user_does_not_exist(self): @@ -188,7 +188,7 @@ def test_visitor_page_detail_user_does_not_exist(self): view_time=datetime.fromtimestamp(1565033030), ) response = self.client.get( - '/tracking/visitors/asdf/page/?page_url=%s&start=2018&end=2020' % pv.url) + '/tracking/visitors/80085/page/?page_url=%s&start=2018&end=2020' % pv.url) self.assertEqual(response.status_code, 404) def test_visitor_page_detail_one_pageview(self): @@ -197,6 +197,7 @@ def test_visitor_page_detail_one_pageview(self): ip_address='127.0.0.1', user=self.user, time_on_site = 0, + start_time=datetime.fromtimestamp(1565033030), ) pv = Pageview.objects.create( visitor=visitor, @@ -211,13 +212,13 @@ def test_visitor_page_detail_one_pageview(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.context['total_views'], 1) self.assertEqual(response.context['avg_views_per_visit'], 1) - self.assertEqual(response.context['visits'].count(), 1) + self.assertEqual(len(response.context['visits']), 1) self.assertEqual(response.context['user'], self.user) self.assertEqual(response.context['page_url'], pv.url) def test_visitor_pageview_pageview_does_not_exist(self): response = self.client.get( - '/tracking/visitors/lkdjf/pageview/lkdjf/') + '/tracking/visitors/80085/pageview/1337/') self.assertEqual(response.status_code, 404) def test_visitor_pageview_one_pageview_exists(self): @@ -276,7 +277,7 @@ def test_visitor_page_overview_no_pageviews(self): response = self.client.get( '/tracking/pages/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['pageview_counts'].count(), 0) + self.assertEqual(len(response.context['pageview_counts']), 0) self.assertEqual(response.context['total_page_views'], 0) self.assertEqual(response.context['total_pages'], 0) @@ -298,7 +299,7 @@ def test_visitor_page_overview_one_pageviews(self): response = self.client.get( '/tracking/pages/?start=2018&end=2020') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['pageview_counts'].count(), 1) + self.assertEqual(len(response.context['pageview_counts']), 1) self.assertEqual(response.context['total_page_views'], 1) self.assertEqual(response.context['total_pages'], 1) diff --git a/tracking/views.py b/tracking/views.py index 971f3dc..e7a8788 100755 --- a/tracking/views.py +++ b/tracking/views.py @@ -131,8 +131,8 @@ def visitor_page_detail(request, user_id): paginator = Paginator(visits, TRACK_PAGING_SIZE) context = { - 'total_views': aggs['views__sum'], - 'avg_views_per_visit': aggs['views__avg'], + 'total_views': aggs['views__sum'] if aggs['views__sum'] else 0, + 'avg_views_per_visit': aggs['views__avg'] if aggs['views__avg'] else 0, 'visits': paginator.page(page), 'user': user, 'page_url': page_url, From 9fcd313132d984c7a21e155c4fc8e4e1c12b288b Mon Sep 17 00:00:00 2001 From: Ed Turpin Date: Mon, 3 Feb 2020 18:12:00 -0600 Subject: [PATCH 28/28] Added last_pageview to user_stats. --- tracking/managers.py | 3 ++- tracking/templates/tracking/snippets/stats.html | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tracking/managers.py b/tracking/managers.py index 3b8f926..eb0321f 100644 --- a/tracking/managers.py +++ b/tracking/managers.py @@ -6,7 +6,7 @@ from django.utils import timezone from django.contrib.auth import get_user_model from django.db import models -from django.db.models import Count, Avg, Sum +from django.db.models import Count, Avg, Sum, Max from tracking.settings import TRACK_PAGEVIEWS, TRACK_ANONYMOUS_USERS from tracking.cache import CacheManager @@ -172,6 +172,7 @@ def user_stats(self, start_date=None, end_date=None): time_on_site=Avg('visit_history__time_on_site'), page_count=Count('visit_history__pageviews', distinct=True), pages_per_visit=Count('visit_history__pageviews', distinct=True)/Count('visit_history', distinct=True), + last_pageview=Max('visit_history__pageviews__view_time'), ).filter(visit_count__gt=0).order_by( '-time_on_site', get_user_model().USERNAME_FIELD, diff --git a/tracking/templates/tracking/snippets/stats.html b/tracking/templates/tracking/snippets/stats.html index a0f0d2d..00746aa 100755 --- a/tracking/templates/tracking/snippets/stats.html +++ b/tracking/templates/tracking/snippets/stats.html @@ -62,6 +62,7 @@

Registered Users

# Visits Avg. Time on Site Avg. Pages/Visit + Last Pageview @@ -71,6 +72,7 @@

Registered Users

{{ user.visit_count }} {{ user.time_on_site|default_if_none:"n/a" }} {{ user.pages_per_visit|floatformat|default:"n/a" }} + {{ user.last_pageview|default:"n/a" }} {% endfor %}