1import json
2
3from plain.runtime import settings
4from plain.views import View
5
6from .models import Pageview
7
8
9class TrackView(View):
10 def post(self):
11 if getattr(self.request, "impersonator", None):
12 # Don't track page views if we're impersonating a user
13 return 200
14
15 try:
16 data = self.request.data
17 except json.JSONDecodeError:
18 return 400
19
20 try:
21 url = data["url"]
22 title = data["title"]
23 referrer = data["referrer"]
24 timestamp = data["timestamp"]
25 except KeyError:
26 return 400
27
28 if user := getattr(self.request, "user", None):
29 user_id = user.id
30 else:
31 user_id = ""
32
33 if session := getattr(self.request, "session", None):
34 session_instance = session.model_instance
35 session_id = str(session_instance.id) if session_instance else ""
36
37 if settings.PAGEVIEWS_ASSOCIATE_ANONYMOUS_SESSIONS:
38 if not user_id:
39 if not session_id:
40 # Make sure we have a key to use
41 session.create()
42 session_instance = session.model_instance
43 session_id = (
44 str(session_instance.id) if session_instance else ""
45 )
46
47 # The user hasn't logged in yet but might later. When they do log in,
48 # the session key itself will be cycled (session fixation attacks),
49 # so we'll store the anonymous session id in the data which will be preserved
50 # when the key cycles, then remove it immediately after.
51 session["pageviews_anonymous_session_id"] = session_id
52 elif user_id and "pageviews_anonymous_session_id" in session:
53 # Associate the previously anonymous pageviews with the user
54 Pageview.query.filter(
55 user_id="",
56 session_id=session["pageviews_anonymous_session_id"],
57 ).update(user_id=user_id)
58
59 # Remove it so we don't keep trying to associate it
60 del session["pageviews_anonymous_session_id"]
61 else:
62 session_id = ""
63
64 Pageview.query.create(
65 user_id=user_id,
66 session_id=session_id,
67 url=url,
68 title=title,
69 referrer=referrer,
70 timestamp=timestamp,
71 )
72
73 return 201