RandomCatLover Claude Sonnet 5 commited on
Commit
40f139d
·
1 Parent(s): c5a7532

Add Umami page-view tracking

Browse files

Uses st.html(..., unsafe_allow_javascript=True) rather than
components.html: the latter sandboxes content in its own iframe, so
Umami would only ever see a meaningless iframe-scoped page instead of
real top-level pageviews. Guarded by an element id so a Streamlit
rerun (which happens on every interaction, not just navigation) never
injects a duplicate script tag or fires an extra pageview beacon.
Website ID/script URL are plain constants, not secrets -- they're
visible in any page's source regardless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +3 -0
  2. src/analytics.py +33 -0
app.py CHANGED
@@ -13,9 +13,12 @@ import streamlit as st
13
 
14
  import google_oauth
15
  import token_store
 
16
 
17
  st.set_page_config(page_title="Board Game Tracker", layout="wide")
18
 
 
 
19
 
20
  def _drive_redirect_uri() -> str:
21
  return st.secrets["auth"]["redirect_uri"].removesuffix("/oauth2callback")
 
13
 
14
  import google_oauth
15
  import token_store
16
+ from analytics import inject_umami
17
 
18
  st.set_page_config(page_title="Board Game Tracker", layout="wide")
19
 
20
+ inject_umami()
21
+
22
 
23
  def _drive_redirect_uri() -> str:
24
  return st.secrets["auth"]["redirect_uri"].removesuffix("/oauth2callback")
src/analytics.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ # Not sensitive -- an analytics site ID is plain HTML visible in any page's
4
+ # source, so this lives as a code constant rather than a secret/env var.
5
+ _UMAMI_SCRIPT_URL = "https://umami-acsk4kc0kw04o4ww4gosgoos.c.ai-man.cc/script.js"
6
+ _UMAMI_WEBSITE_ID = "ae40c7e5-ca49-4786-b185-e0a90f337e55"
7
+ _SCRIPT_TAG_ID = "umami-analytics-script"
8
+
9
+
10
+ def inject_umami() -> None:
11
+ """Injects the Umami tracking script into the page.
12
+
13
+ st.html renders directly into the top-level DOM (unlike
14
+ components.html, which sandboxes content in its own iframe -- Umami
15
+ would then only ever see a meaningless "iframe page" instead of real
16
+ pageviews). Guarded by an element id so a Streamlit rerun never injects
17
+ a second copy or fires a duplicate pageview.
18
+ """
19
+ st.html(
20
+ f"""
21
+ <script>
22
+ if (!document.getElementById('{_SCRIPT_TAG_ID}')) {{
23
+ var s = document.createElement('script');
24
+ s.id = '{_SCRIPT_TAG_ID}';
25
+ s.defer = true;
26
+ s.src = '{_UMAMI_SCRIPT_URL}';
27
+ s.setAttribute('data-website-id', '{_UMAMI_WEBSITE_ID}');
28
+ document.head.appendChild(s);
29
+ }}
30
+ </script>
31
+ """,
32
+ unsafe_allow_javascript=True,
33
+ )