Self-hostable website analytics on Django: a straightforward collector API, dashboards, a world map, and PDF reports.
analyticsdjangodockerhandcodedpythonself-hostedsqliteviteweb-analytics
1import random
2
3from django.db import migrations
4from django.utils import timezone
5
6
7def create_proprium(apps, schema_editor):
8 """
9 Makes an initial Property named 'Proprium' for us to use for our own
10 analytics. Sets the user to our initial "admin" user.
11 """
12 User = apps.get_model('accounts', 'User')
13 Property = apps.get_model('properties', 'Property')
14
15 user = User.objects.get(username='admin')
16 Property.objects.create(name='Proprium', user=user, is_protected=True)
17
18
19def create_exemplar(apps, schema_editor):
20 """
21 Makes an initial Property named 'Exemplar' for us to add a bunch of sample
22 events to it for testing. Sets the user to our initial "admin" user.
23 """
24 User = apps.get_model('accounts', 'User')
25 Property = apps.get_model('properties', 'Property')
26 Event = apps.get_model('properties', 'Event')
27
28 user = User.objects.get(username='admin')
29 property = Property.objects.create(name='Exemplar', user=user)
30
31 url_paths = [
32 {'path': '/', 'title': 'Home'},
33 {'path': '/about', 'title': 'About'},
34 {'path': '/contact', 'title': 'Contact'},
35 {'path': '/blog', 'title': 'Blog'},
36 {'path': '/blog/post-1', 'title': 'Post 1'},
37 {'path': '/blog/post-2', 'title': 'Post 2'},
38 {'path': '/blog/post-3', 'title': 'Post 3'},
39 ]
40
41 screen_sizes = [
42 {'width': 1024, 'height': 768},
43 {'width': 1280, 'height': 1024},
44 {'width': 1366, 'height': 768},
45 {'width': 1920, 'height': 1080},
46 {'width': 2560, 'height': 1440},
47 ]
48
49 platforms = [
50 'Windows',
51 'Macintosh',
52 'Linux',
53 'Android',
54 'iOS',
55 ]
56
57 user_ids = [
58 random.randint(100000000, 999999999) for _ in range(random.randint(100, 150))
59 ]
60
61 custom_events = [
62 "Newsletter Signup",
63 "Checkout Success",
64 "New User Signup",
65 ]
66
67 referrer_urls = [
68 'google.com',
69 'bing.com',
70 'yahoo.com',
71 'duckduckgo.com',
72 'ask.com',
73 'baidu.com',
74 'yandex.com',
75 'facebook.com',
76 'twitter.com',
77 'linkedin.com',
78 'reddit.com',
79 'pinterest.com',
80 'youtube.com',
81 'instagram.com',
82 'flickr.com',
83 'tumblr.com',
84 ]
85
86 # Create a list of dicts that include "city", "region", "country", and
87 # "loc" which is "lat,lon" in the US.
88 random_locations = [
89 { 'region': 'New York', },
90 { 'region': 'California', },
91 { 'region': 'Texas', },
92 { 'region': 'North Carolina', },
93 { 'region': 'Florida', },
94 { 'region': 'Illinois', },
95 { 'region': 'Ohio', },
96 { 'region': 'Michigan', },
97 { 'region': 'Pennsylvania', },
98 { 'region': 'Georgia', },
99 { 'region': 'New Jersey', },
100 { 'region': 'Virginia', },
101 { 'region': 'North Dakota', },
102 { 'region': 'South Carolina', },
103 { 'region': 'Indiana', },
104 ]
105
106 random_utm_medium = [
107 'email',
108 'social',
109 'search',
110 'referral',
111 'paid',
112 ]
113
114 random_utm_source = [
115 'google',
116 'bing',
117 'duckduckgo',
118 'facebook',
119 'twitter',
120 'instagram',
121 'linkedin',
122 'reddit',
123 ]
124
125 random_utm_campaign = [
126 '2022 search',
127 '2022 email',
128 '2022 social',
129 '2022 referral',
130 ]
131
132 # Generate some session_start events
133 for user_id in user_ids:
134 page = random.choice(url_paths)
135 screen = random.choice(screen_sizes)
136 location = random.choice(random_locations)
137 event = Event.objects.create(
138 property=property,
139 event='session_start',
140 data={
141 'user_id': user_id,
142 'url': page['path'],
143 'title': page['title'],
144 'referrer': random.choice(referrer_urls),
145 'user_agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
146 'screen_width': screen['width'],
147 'screen_height': screen['height'],
148 'platform': random.choice(platforms),
149 'device': random.choice(['Desktop', 'Tablet', 'Mobile']),
150 'region': location['region'],
151 },
152 )
153 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
154
155 # Generate some session_start events from random users to drive down user engagement
156 for _ in range(random.randint(50, 100)):
157 page = random.choice(url_paths)
158 screen = random.choice(screen_sizes)
159 event = Event.objects.create(
160 property=property,
161 event='session_start',
162 data={
163 'user_id': random.randint(100000000, 999999999),
164 'url': page['path'],
165 'title': page['title'],
166 'referrer': random.choice(referrer_urls),
167 'user_agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
168 'screen_width': screen['width'],
169 'screen_height': screen['height'],
170 'platform': random.choice(platforms),
171 'device': random.choice(['Desktop', 'Tablet', 'Mobile']),
172 'browser': random.choice(['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera']),
173 },
174 )
175 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
176
177 # Generate some page_view events
178 for _ in range(random.randint(300, 600)):
179 page = random.choice(url_paths)
180 event = Event.objects.create(
181 property=property,
182 event='page_view',
183 data={
184 'user_id': random.choice(user_ids),
185 'url': page['path'],
186 'title': page['title'],
187 'utm_medium': random.choice(random_utm_medium),
188 'utm_source': random.choice(random_utm_source),
189 'utm_campaign': random.choice(random_utm_campaign),
190 },
191 )
192 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
193
194
195 # Generate some scroll events
196 for _ in range(random.randint(300, 600)):
197 page = random.choice(url_paths)
198 event = Event.objects.create(
199 property=property,
200 event='scroll',
201 data={
202 'user_id': random.choice(user_ids),
203 'url': page['path'],
204 'title': page['title'],
205 },
206 )
207 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
208
209 # Generate some click events
210 for _ in range(random.randint(300, 600)):
211 page = random.choice(url_paths)
212 screen = random.choice(screen_sizes)
213 event = Event.objects.create(
214 property=property,
215 event='click',
216 data={
217 'user_id': random.choice(user_ids),
218 'url': page['path'],
219 'title': page['title'],
220 'x': random.randint(0, screen['width']),
221 'y': random.randint(0, screen['height']),
222 'target': random.choice(['a', 'button', 'input', 'link', 'select', 'textarea']),
223 'text': random.choice(['', 'Hello World', 'This is a test']),
224 },
225 )
226 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
227
228 # Generate some page_leave events
229 for _ in range(random.randint(300, 600)):
230 page = random.choice(url_paths)
231 event = Event.objects.create(
232 property=property,
233 event='page_leave',
234 data={
235 'user_id': random.choice(user_ids),
236 'url': page['path'],
237 'title': page['title'],
238 'time_on_page': random.randint(0, 100000),
239 },
240 )
241 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
242
243 # Generate some custom events
244 for _ in range(random.randint(300, 600)):
245 event = Event.objects.create(
246 property=property,
247 event=random.choice(custom_events),
248 data={
249 'user_id': random.choice(user_ids),
250 },
251 )
252 Event.objects.filter(id=event.id).update(created_at=timezone.now() - timezone.timedelta(days=random.randint(0, 56)))
253
254
255class Migration(migrations.Migration):
256
257 dependencies = [
258 ('accounts', '0002_initial_data'),
259 ('properties', '0001_initial'),
260 ]
261
262 operations = [
263 migrations.RunPython(create_proprium),
264 migrations.RunPython(create_exemplar),
265 ]