From a5b951c4d5570fa84c46ed15c66164253832fa53 Mon Sep 17 00:00:00 2001 From: Katherine Fleming <2205659+kflemin@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:46:48 -0600 Subject: [PATCH] add google analytics --- README.md | 3 +- bsyncviewer/settings/docker.py | 4 +++ bsyncviewer/templates/base.html | 1 + bsyncviewer/templates/base2.html | 2 ++ bsyncviewer/templates/google_analytics.html | 9 ++++++ bsyncviewer/templatetags/navtags.py | 12 ++++++++ bsyncviewer/tests/test_views.py | 32 ++++++++++++++++++++- docker-compose.dev.yml | 3 ++ docker/README.md | 24 ++++++++++++++++ 9 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 bsyncviewer/templates/google_analytics.html diff --git a/README.md b/README.md index c4586b2..bd97cf9 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ This is the repository for the BuildingSync Validator web application. pyenv virtualenv 3.9.23 bsync-validator-3.9.23 pyenv local bsync-validator-3.9.23 ``` -1. Install PostgreSQL server for local development (if desired) +1. Install PostgreSQL server for local development (if desired) or run from docker + `docker compose -f docker-compose.yml -f docker-compose.dev.yml up` - Once installed, create your local database and user: ```bash psql -c 'DROP DATABASE bsync_validator;' diff --git a/bsyncviewer/settings/docker.py b/bsyncviewer/settings/docker.py index e1dfb29..809fa97 100644 --- a/bsyncviewer/settings/docker.py +++ b/bsyncviewer/settings/docker.py @@ -26,6 +26,10 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False +GOOGLE_ANALYTICS_MEASUREMENT_ID = os.environ.get( + "GOOGLE_ANALYTICS_MEASUREMENT_ID", "G-P8CNFE9GY2" +) + ALLOWED_HOSTS = [ "127.0.0.1", "localhost", diff --git a/bsyncviewer/templates/base.html b/bsyncviewer/templates/base.html index 1a3b643..858b485 100644 --- a/bsyncviewer/templates/base.html +++ b/bsyncviewer/templates/base.html @@ -4,6 +4,7 @@ {% load static %} {% block bootstrap_extra_head %} + {% google_analytics %} {# Load CSS and JavaScript #} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} diff --git a/bsyncviewer/templates/base2.html b/bsyncviewer/templates/base2.html index d356b8d..c53e528 100644 --- a/bsyncviewer/templates/base2.html +++ b/bsyncviewer/templates/base2.html @@ -1,5 +1,6 @@ {% load bootstrap4 %} +{% load navtags %} {% load static %} @@ -12,6 +13,7 @@
+ {% google_analytics %} diff --git a/bsyncviewer/templates/google_analytics.html b/bsyncviewer/templates/google_analytics.html new file mode 100644 index 0000000..3c299b5 --- /dev/null +++ b/bsyncviewer/templates/google_analytics.html @@ -0,0 +1,9 @@ +{% if measurement_id %} + + +{% endif %} diff --git a/bsyncviewer/templatetags/navtags.py b/bsyncviewer/templatetags/navtags.py index 338aacd..4839ac6 100644 --- a/bsyncviewer/templatetags/navtags.py +++ b/bsyncviewer/templatetags/navtags.py @@ -1,9 +1,21 @@ from django import template +from django.conf import settings from django.urls import Resolver404, resolve register = template.Library() +@register.inclusion_tag("google_analytics.html") +def google_analytics(): + return { + "measurement_id": ( + "" + if settings.DEBUG + else getattr(settings, "GOOGLE_ANALYTICS_MEASUREMENT_ID", "") + ) + } + + @register.simple_tag def active_page(request, *view_names): if not request: diff --git a/bsyncviewer/tests/test_views.py b/bsyncviewer/tests/test_views.py index f0e2b84..4a03f9e 100644 --- a/bsyncviewer/tests/test_views.py +++ b/bsyncviewer/tests/test_views.py @@ -4,7 +4,8 @@ from django.conf import settings from django.core.files.uploadedfile import SimpleUploadedFile from django.core.management import call_command -from django.test import Client, TestCase +from django.template.loader import render_to_string +from django.test import Client, SimpleTestCase, TestCase, override_settings from django.urls import reverse from bsyncviewer.models.attribute import Attribute @@ -15,6 +16,35 @@ TEST_SCHEMA_VERSION = "0.0.1" +class GoogleAnalyticsTemplateTests(SimpleTestCase): + @override_settings(DEBUG=False, GOOGLE_ANALYTICS_MEASUREMENT_ID="G-P8CNFE9GY2") + def test_tracking_enabled_in_both_layouts(self): + for template_name in ("base.html", "base2.html"): + with self.subTest(template_name=template_name): + content = render_to_string(template_name) + self.assertEqual( + content.count( + "https://www.googletagmanager.com/gtag/js?id=G-P8CNFE9GY2" + ), + 1, + ) + self.assertEqual( + content.count("gtag('config', 'G\\u002DP8CNFE9GY2');"), 1 + ) + + @override_settings(DEBUG=True, GOOGLE_ANALYTICS_MEASUREMENT_ID="G-P8CNFE9GY2") + def test_tracking_disabled_in_development(self): + for template_name in ("base.html", "base2.html"): + with self.subTest(template_name=template_name): + self.assertNotIn("googletagmanager", render_to_string(template_name)) + + @override_settings(DEBUG=False, GOOGLE_ANALYTICS_MEASUREMENT_ID="") + def test_tracking_disabled_without_measurement_id(self): + for template_name in ("base.html", "base2.html"): + with self.subTest(template_name=template_name): + self.assertNotIn("googletagmanager", render_to_string(template_name)) + + class BsyncviewerViewTests(TestCase): def test_index_ok(self): response = self.client.get(reverse("index")) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 783f9a3..717e9d5 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -3,6 +3,9 @@ # docker-compose -f docker-compose.yml -f docker-compose.dev.yml up services: + db-postgres: + ports: + - "127.0.0.1:5432:5432" web: command: python3 manage.py runserver 0.0.0.0:80 environment: diff --git a/docker/README.md b/docker/README.md index 1ef4cbb..ff4a16f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -49,6 +49,30 @@ docker-compose -f docker-compose.prod.yml up -d --build # Debugging +## Google Analytics + +Production settings enable GA4 page-view tracking with Measurement ID `G-P8CNFE9GY2`. +Tracking is disabled when Django's `DEBUG` setting is true. The shared page layouts +include the tag; the Django admin does not. + +To change or disable tracking, set `GOOGLE_ANALYTICS_MEASUREMENT_ID` in the web +container's environment. For example, add this entry under `web.environment` in +your Compose configuration to disable it: + +```yaml +- GOOGLE_ANALYTICS_MEASUREMENT_ID= +``` + +Rebuild and redeploy to apply the template changes. Then visit the public site +with browser tracking blockers disabled and check GA4's Realtime report for the +visit. Data collection cannot be verified until the updated site is deployed. + +The tag does not implement cookie consent. Confirm applicable consent and privacy +notice requirements before deploying; if consent is required, gate the tag on +consent before enabling collection. + +## Container Logs + - Tail log ```bash