Self-hostable website analytics on Django: a straightforward collector API, dashboards, a world map, and PDF reports.
analyticsdjangodockerhandcodedpythonself-hostedsqliteviteweb-analytics
1import uuid
2
3from django.db import models
4from django.utils import timezone
5from django.contrib.auth import get_user_model
6
7
8User = get_user_model()
9
10
11class Property(models.Model):
12 """
13 A site that we attach all our analytics hits to and connect up to a user.
14 """
15 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
16 user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='properties')
17 name = models.CharField(max_length=255)
18 custom_cards = models.JSONField(default=list, blank=True, null=True)
19 created_at = models.DateTimeField(auto_now_add=True, editable=False)
20 updated_at = models.DateTimeField(auto_now=True, editable=False)
21 is_protected = models.BooleanField(default=False, editable=False)
22 is_public = models.BooleanField(default=False, editable=False)
23
24 class Meta:
25 verbose_name = 'Property'
26 verbose_name_plural = 'Properties'
27
28 def __str__(self):
29 return self.name
30
31 @property
32 def is_active(self):
33 """
34 Returns True if we've recieved any events for this property in the last
35 7 days.
36 """
37 return self.events.filter(created_at__gte=timezone.now() - timezone.timedelta(days=7)).exists()
38
39 @property
40 def total_events(self):
41 return self.events.count()
42
43 @property
44 def total_session_starts(self):
45 return self.events.filter(event="session_start").count()
46
47 @property
48 def total_page_views(self):
49 return self.events.filter(event="page_view").count()
50
51 @property
52 def total_clicks(self):
53 return self.events.filter(event="click").count()
54
55 @property
56 def total_scrolls(self):
57 return self.events.filter(event="scroll").count()
58
59
60class Event(models.Model):
61 """
62 An event that is sent by a site that we want to collect. The most basic of
63 events is a "page_view" event. All events can have a variety of key-value
64 pairs sent along with them that we store in a JSONField.
65
66 As an example a "page_view" may contain the following key-value pairs:
67
68 - url: The URL of the page that was viewed
69 - title: The title of the page that was viewed
70 - referrer: The URL of the page that referred the user to the page that was viewed
71 - user_agent: The user agent of the user that viewed the page
72 - screen_width: The width of the screen of the user that viewed the page
73 - screen_height: The height of the screen of the user that viewed the page
74
75 Users are free to send any event with any key-value pairs they want.
76 """
77 created_at = models.DateTimeField(auto_now_add=True, editable=False)
78 property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name="events", editable=False)
79 event = models.CharField(max_length=255, editable=False)
80 data = models.JSONField(editable=False)
81
82 def __str__(self):
83 return self.event
84
85 class Meta:
86 indexes = [
87 models.Index(fields=['created_at']),
88 models.Index(fields=['property', 'created_at']),
89 models.Index(fields=['property', 'event', 'created_at']),
90 ]