From a2f217ace594e6f4a1a7ad80a08180a489ca7048 Mon Sep 17 00:00:00 2001 From: Ariel Rin Date: Mon, 14 Aug 2023 03:31:33 +0000 Subject: [PATCH] Analytics UA to V4 Conversion --- allianceauth/analytics/admin.py | 8 +- allianceauth/analytics/apps.py | 3 - .../analytics/fixtures/disable_analytics.json | 7 +- allianceauth/analytics/middleware.py | 52 ----- .../migrations/0007_analyticstokens_secret.py | 18 ++ .../0008_add_AA_GA-4_Team_Token .py | 64 ++++++ ...e_analyticstokens_ignore_paths_and_more.py | 28 +++ allianceauth/analytics/models.py | 15 +- allianceauth/analytics/signals.py | 55 ----- allianceauth/analytics/tasks.py | 205 ++++++++---------- .../analytics/tests/test_integration.py | 109 ---------- .../analytics/tests/test_middleware.py | 24 -- allianceauth/analytics/tests/test_models.py | 3 +- allianceauth/analytics/tests/test_tasks.py | 201 +++-------------- allianceauth/analytics/tests/test_utils.py | 12 +- .../task_statistics/tests/test_signals.py | 2 +- .../project_name/settings/base.py | 1 - .../modules/discord/tests/test_integration.py | 1 + 18 files changed, 243 insertions(+), 565 deletions(-) delete mode 100644 allianceauth/analytics/middleware.py create mode 100644 allianceauth/analytics/migrations/0007_analyticstokens_secret.py create mode 100644 allianceauth/analytics/migrations/0008_add_AA_GA-4_Team_Token .py create mode 100644 allianceauth/analytics/migrations/0009_remove_analyticstokens_ignore_paths_and_more.py delete mode 100644 allianceauth/analytics/signals.py delete mode 100644 allianceauth/analytics/tests/test_integration.py delete mode 100644 allianceauth/analytics/tests/test_middleware.py diff --git a/allianceauth/analytics/admin.py b/allianceauth/analytics/admin.py index 28a38b48..383bbbe2 100644 --- a/allianceauth/analytics/admin.py +++ b/allianceauth/analytics/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import AnalyticsIdentifier, AnalyticsPath, AnalyticsTokens +from .models import AnalyticsIdentifier, AnalyticsTokens @admin.register(AnalyticsIdentifier) @@ -13,9 +13,3 @@ class AnalyticsIdentifierAdmin(admin.ModelAdmin): class AnalyticsTokensAdmin(admin.ModelAdmin): search_fields = ['name', ] list_display = ('name', 'type',) - - -@admin.register(AnalyticsPath) -class AnalyticsPathAdmin(admin.ModelAdmin): - search_fields = ['ignore_path', ] - list_display = ('ignore_path',) diff --git a/allianceauth/analytics/apps.py b/allianceauth/analytics/apps.py index 85050412..3f1c20f8 100644 --- a/allianceauth/analytics/apps.py +++ b/allianceauth/analytics/apps.py @@ -4,6 +4,3 @@ from django.apps import AppConfig class AnalyticsConfig(AppConfig): name = 'allianceauth.analytics' label = 'analytics' - - def ready(self): - import allianceauth.analytics.signals diff --git a/allianceauth/analytics/fixtures/disable_analytics.json b/allianceauth/analytics/fixtures/disable_analytics.json index d97ff6c1..ca75cb37 100644 --- a/allianceauth/analytics/fixtures/disable_analytics.json +++ b/allianceauth/analytics/fixtures/disable_analytics.json @@ -3,11 +3,10 @@ "model": "analytics.AnalyticsTokens", "pk": 1, "fields": { - "name": "AA Team Public Google Analytics (Universal)", + "name": "AA Team Public Google Analytics (V4)", "type": "GA-V4", - "token": "UA-186249766-2", - "send_page_views": "False", - "send_celery_tasks": "False", + "token": "G-6LYSMYK8DE", + "secret": "KLlpjLZ-SRGozS5f5wb_kw", "send_stats": "False" } }, diff --git a/allianceauth/analytics/middleware.py b/allianceauth/analytics/middleware.py deleted file mode 100644 index 4dd93685..00000000 --- a/allianceauth/analytics/middleware.py +++ /dev/null @@ -1,52 +0,0 @@ -from bs4 import BeautifulSoup - -from django.conf import settings -from django.utils.deprecation import MiddlewareMixin -from .models import AnalyticsTokens, AnalyticsIdentifier -from .tasks import send_ga_tracking_web_view - -import re - - -class AnalyticsMiddleware(MiddlewareMixin): - def process_response(self, request, response): - """Django Middleware: Process Page Views and creates Analytics Celery Tasks""" - if getattr(settings, "ANALYTICS_DISABLED", False): - return response - analyticstokens = AnalyticsTokens.objects.all() - client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex - try: - title = BeautifulSoup( - response.content, "html.parser").html.head.title.text - except AttributeError: - title = '' - for token in analyticstokens: - # Check if Page View Sending is Disabled - if token.send_page_views is False: - continue - # Check Exclusions - ignore = False - for ignore_path in token.ignore_paths.values(): - ignore_path_regex = re.compile(ignore_path["ignore_path"]) - if re.search(ignore_path_regex, request.path) is not None: - ignore = True - - if ignore is True: - continue - - tracking_id = token.token - locale = request.LANGUAGE_CODE - path = request.path - try: - useragent = request.headers["User-Agent"] - except KeyError: - useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" - - send_ga_tracking_web_view.s(tracking_id=tracking_id, - client_id=client_id, - page=path, - title=title, - locale=locale, - useragent=useragent).\ - apply_async(priority=9) - return response diff --git a/allianceauth/analytics/migrations/0007_analyticstokens_secret.py b/allianceauth/analytics/migrations/0007_analyticstokens_secret.py new file mode 100644 index 00000000..9cf441e2 --- /dev/null +++ b/allianceauth/analytics/migrations/0007_analyticstokens_secret.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-08-30 05:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('analytics', '0006_more_ignore_paths'), + ] + + operations = [ + migrations.AddField( + model_name='analyticstokens', + name='secret', + field=models.CharField(blank=True, max_length=254), + ), + ] diff --git a/allianceauth/analytics/migrations/0008_add_AA_GA-4_Team_Token .py b/allianceauth/analytics/migrations/0008_add_AA_GA-4_Team_Token .py new file mode 100644 index 00000000..efc816ac --- /dev/null +++ b/allianceauth/analytics/migrations/0008_add_AA_GA-4_Team_Token .py @@ -0,0 +1,64 @@ +# Generated by Django 3.1.4 on 2020-12-30 08:53 + +from django.db import migrations +from django.core.exceptions import ObjectDoesNotExist + + +def add_aa_team_token(apps, schema_editor): + # We can't import the Person model directly as it may be a newer + # version than this migration expects. We use the historical version. + Tokens = apps.get_model('analytics', 'AnalyticsTokens') + AnalyticsPath = apps.get_model('analytics', 'AnalyticsPath') + token = Tokens() + try: + ua_token = Tokens.objects.get(token="UA-186249766-2") + original_send_page_views = ua_token.send_page_views + original_send_celery_tasks = ua_token.send_celery_tasks + original_send_stats = ua_token.send_stats + except ObjectDoesNotExist: + original_send_page_views = True + original_send_celery_tasks = True + original_send_stats = True + + try: + user_notifications_count = AnalyticsPath.objects.get(ignore_path=r"^\/user_notifications_count\/.*",) + except ObjectDoesNotExist: + user_notifications_count = AnalyticsPath.objects.create(ignore_path=r"^\/user_notifications_count\/.*") + + try: + admin = AnalyticsPath.objects.get(ignore_path=r"^\/admin\/.*") + except ObjectDoesNotExist: + admin = AnalyticsPath.objects.create(ignore_path=r"^\/admin\/.*") + + try: + account_activate = AnalyticsPath.objects.get(ignore_path=r"^\/account\/activate\/.*") + except ObjectDoesNotExist: + account_activate = AnalyticsPath.objects.create(ignore_path=r"^\/account\/activate\/.*") + + token.type = 'GA-V4' + token.token = 'G-6LYSMYK8DE' + token.secret = 'KLlpjLZ-SRGozS5f5wb_kw' + token.send_page_views = original_send_page_views + token.send_celery_tasks = original_send_celery_tasks + token.send_stats = original_send_stats + token.name = 'AA Team Public Google Analytics (V4)' + token.save() + token.ignore_paths.add(admin, user_notifications_count, account_activate) + token.save() + + +def remove_aa_team_token(apps, schema_editor): + # Have to define some code to remove this identifier + # In case of migration rollback? + Tokens = apps.get_model('analytics', 'AnalyticsTokens') + token = Tokens.objects.filter(token="G-6LYSMYK8DE").delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('analytics', '0007_analyticstokens_secret'), + ] + + operations = [migrations.RunPython( + add_aa_team_token, remove_aa_team_token)] diff --git a/allianceauth/analytics/migrations/0009_remove_analyticstokens_ignore_paths_and_more.py b/allianceauth/analytics/migrations/0009_remove_analyticstokens_ignore_paths_and_more.py new file mode 100644 index 00000000..9cd4c93b --- /dev/null +++ b/allianceauth/analytics/migrations/0009_remove_analyticstokens_ignore_paths_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.0.10 on 2023-05-08 05:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('analytics', '0008_add_AA_GA-4_Team_Token '), + ] + + operations = [ + migrations.RemoveField( + model_name='analyticstokens', + name='ignore_paths', + ), + migrations.RemoveField( + model_name='analyticstokens', + name='send_celery_tasks', + ), + migrations.RemoveField( + model_name='analyticstokens', + name='send_page_views', + ), + migrations.DeleteModel( + name='AnalyticsPath', + ), + ] diff --git a/allianceauth/analytics/models.py b/allianceauth/analytics/models.py index 62726c28..ddee495a 100644 --- a/allianceauth/analytics/models.py +++ b/allianceauth/analytics/models.py @@ -7,22 +7,19 @@ from uuid import uuid4 class AnalyticsIdentifier(models.Model): - identifier = models.UUIDField(default=uuid4, - editable=False) + identifier = models.UUIDField( + default=uuid4, + editable=False) def save(self, *args, **kwargs): if not self.pk and AnalyticsIdentifier.objects.exists(): # Force a single object raise ValidationError('There is can be only one \ AnalyticsIdentifier instance') - self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1 + self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1 return super().save(*args, **kwargs) -class AnalyticsPath(models.Model): - ignore_path = models.CharField(max_length=254, default="/example/", help_text="Regex Expression, If matched no Analytics Page View is sent") - - class AnalyticsTokens(models.Model): class Analytics_Type(models.TextChoices): @@ -32,7 +29,5 @@ class AnalyticsTokens(models.Model): name = models.CharField(max_length=254) type = models.CharField(max_length=254, choices=Analytics_Type.choices) token = models.CharField(max_length=254, blank=False) - send_page_views = models.BooleanField(default=False) - send_celery_tasks = models.BooleanField(default=False) + secret = models.CharField(max_length=254, blank=True) send_stats = models.BooleanField(default=False) - ignore_paths = models.ManyToManyField(AnalyticsPath, blank=True) diff --git a/allianceauth/analytics/signals.py b/allianceauth/analytics/signals.py deleted file mode 100644 index 91565c5e..00000000 --- a/allianceauth/analytics/signals.py +++ /dev/null @@ -1,55 +0,0 @@ -import logging -from celery.signals import task_failure, task_success -from django.conf import settings -from allianceauth.analytics.tasks import analytics_event - -logger = logging.getLogger(__name__) - - -@task_failure.connect -def process_failure_signal( - exception, traceback, - sender, task_id, signal, - args, kwargs, einfo, **kw): - logger.debug("Celery task_failure signal %s" % sender.__class__.__name__) - if getattr(settings, "ANALYTICS_DISABLED", False): - return - - category = sender.__module__ - - if 'allianceauth.analytics' not in category: - if category.endswith(".tasks"): - category = category[:-6] - - action = sender.__name__ - - label = f"{exception.__class__.__name__}" - - analytics_event(category=category, - action=action, - label=label) - - -@task_success.connect -def celery_success_signal(sender, result=None, **kw): - logger.debug("Celery task_success signal %s" % sender.__class__.__name__) - if getattr(settings, "ANALYTICS_DISABLED", False): - return - - category = sender.__module__ - - if 'allianceauth.analytics' not in category: - if category.endswith(".tasks"): - category = category[:-6] - - action = sender.__name__ - label = "Success" - - value = 0 - if isinstance(result, int): - value = result - - analytics_event(category=category, - action=action, - label=label, - value=value) diff --git a/allianceauth/analytics/tasks.py b/allianceauth/analytics/tasks.py index 34826e21..060a7732 100644 --- a/allianceauth/analytics/tasks.py +++ b/allianceauth/analytics/tasks.py @@ -3,7 +3,6 @@ import logging from django.conf import settings from django.apps import apps from celery import shared_task -from allianceauth import __version__ from .models import AnalyticsTokens, AnalyticsIdentifier from .utils import ( install_stat_addons, @@ -12,14 +11,14 @@ from .utils import ( logger = logging.getLogger(__name__) -BASE_URL = "https://www.google-analytics.com/" +BASE_URL = "https://www.google-analytics.com" -DEBUG_URL = f"{BASE_URL}debug/collect" -COLLECTION_URL = f"{BASE_URL}collect" +DEBUG_URL = f"{BASE_URL}/debug/mp/collect" +COLLECTION_URL = f"{BASE_URL}/mp/collect" if getattr(settings, "ANALYTICS_ENABLE_DEBUG", False) and settings.DEBUG: - # Force sending of analytics data during in a debug/test environemt - # Usefull for developers working on this feature. + # Force sending of analytics data during in a debug/test environment + # Useful for developers working on this feature. logger.warning( "You have 'ANALYTICS_ENABLE_DEBUG' Enabled! " "This debug instance will send analytics data!") @@ -31,40 +30,38 @@ if settings.DEBUG is True: ANALYTICS_URL = DEBUG_URL -def analytics_event(category: str, - action: str, - label: str, - value: int = 0, +def analytics_event(namespace: str, + task: str, + label: str = "", + result: str = "", + value: int = 1, event_type: str = 'Celery'): """ Send a Google Analytics Event for each token stored Includes check for if its enabled/disabled Args: - `category` (str): Celery Namespace - `action` (str): Task Name - `label` (str): Optional, Task Success/Exception - `value` (int): Optional, If bulk, Query size, can be a binary True/False + `namespace` (str): Celery Namespace + `task` (str): Task Name + `label` (str): Optional, additional task label + `result` (str): Optional, Task Success/Exception + `value` (int): Optional, If bulk, Query size, can be a Boolean `event_type` (str): Optional, Celery or Stats only, Default to Celery """ - analyticstokens = AnalyticsTokens.objects.all() - client_id = AnalyticsIdentifier.objects.get(id=1).identifier.hex - for token in analyticstokens: - if event_type == 'Celery': - allowed = token.send_celery_tasks - elif event_type == 'Stats': + for token in AnalyticsTokens.objects.filter(type='GA-V4'): + if event_type == 'Stats': allowed = token.send_stats else: allowed = False if allowed is True: - tracking_id = token.token send_ga_tracking_celery_event.s( - tracking_id=tracking_id, - client_id=client_id, - category=category, - action=action, + measurement_id=token.token, + secret=token.secret, + namespace=namespace, + task=task, label=label, + result=result, value=value).apply_async(priority=9) @@ -72,136 +69,104 @@ def analytics_event(category: str, def analytics_daily_stats(): """Celery Task: Do not call directly - Gathers a series of daily statistics and sends analytics events containing them + Gathers a series of daily statistics + Sends analytics events containing them """ users = install_stat_users() tokens = install_stat_tokens() addons = install_stat_addons() logger.debug("Running Daily Analytics Upload") - analytics_event(category='allianceauth.analytics', - action='send_install_stats', + analytics_event(namespace='allianceauth.analytics', + task='send_install_stats', label='existence', value=1, event_type='Stats') - analytics_event(category='allianceauth.analytics', - action='send_install_stats', + analytics_event(namespace='allianceauth.analytics', + task='send_install_stats', label='users', value=users, event_type='Stats') - analytics_event(category='allianceauth.analytics', - action='send_install_stats', + analytics_event(namespace='allianceauth.analytics', + task='send_install_stats', label='tokens', value=tokens, event_type='Stats') - analytics_event(category='allianceauth.analytics', - action='send_install_stats', + analytics_event(namespace='allianceauth.analytics', + task='send_install_stats', label='addons', value=addons, event_type='Stats') for appconfig in apps.get_app_configs(): - analytics_event(category='allianceauth.analytics', - action='send_extension_stats', + analytics_event(namespace='allianceauth.analytics', + task='send_extension_stats', label=appconfig.label, value=1, event_type='Stats') -@shared_task() -def send_ga_tracking_web_view( - tracking_id: str, - client_id: str, - page: str, - title: str, - locale: str, - useragent: str) -> requests.Response: - - """Celery Task: Do not call directly - - Sends Page View events to GA, Called only via analytics.middleware - - Parameters - ---------- - `tracking_id` (str): Unique Server Identifier - `client_id` (str): GA Token - `page` (str): Page Path - `title` (str): Page Title - `locale` (str): Browser Language - `useragent` (str): Browser UserAgent - - Returns - ------- - requests.Reponse Object - """ - headers = {"User-Agent": useragent} - - payload = { - 'v': '1', - 'tid': tracking_id, - 'cid': client_id, - 't': 'pageview', - 'dp': page, - 'dt': title, - 'ul': locale, - 'ua': useragent, - 'aip': 1, - 'an': "allianceauth", - 'av': __version__ - } - - response = requests.post( - ANALYTICS_URL, data=payload, - timeout=5, headers=headers) - logger.debug(f"Analytics Page View HTTP{response.status_code}") - return response - - @shared_task() def send_ga_tracking_celery_event( - tracking_id: str, - client_id: str, - category: str, - action: str, - label: str, - value: int) -> requests.Response: + measurement_id: str, + secret: str, + namespace: str, + task: str, + label: str = "", + result: str = "", + value: int = 1): """Celery Task: Do not call directly - Sends Page View events to GA, Called only via analytics.middleware + Sends an events to GA Parameters ---------- - `tracking_id` (str): Unique Server Identifier - `client_id` (str): GA Token - `category` (str): Celery Namespace - `action` (str): Task Name - `label` (str): Optional, Task Success/Exception + `measurement_id` (str): GA Token + `secret` (str): GA Authentication Secret + `namespace` (str): Celery Namespace + `task` (str): Task Name + `label` (str): Optional, additional task label + `result` (str): Optional, Task Success/Exception `value` (int): Optional, If bulk, Query size, can be a binary True/False - - Returns - ------- - requests.Reponse Object """ - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"} + parameters = { + 'measurement_id': measurement_id, + 'api_secret': secret + } payload = { - 'v': '1', - 'tid': tracking_id, - 'cid': client_id, - 't': 'event', - 'ec': category, - 'ea': action, - 'el': label, - 'ev': value, - 'aip': 1, - 'an': "allianceauth", - 'av': __version__ + 'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex, + "user_properties": { + "allianceauth_version": { + "value": "allianceauth_version" } - - response = requests.post( - ANALYTICS_URL, data=payload, - timeout=5, headers=headers) - logger.debug(f"Analytics Celery/Stats Event HTTP{response.status_code}") - return response + }, + 'non_personalized_ads': True, + "events": [{ + "name": "celery_event", + "params": { + "namespace": namespace, + "task": task, + 'result': result, + 'label': label, + "value": value + } + }] + } + try: + response = requests.post( + ANALYTICS_URL, + params=parameters, + json=payload, + timeout=10) + response.raise_for_status() + logger.debug( + f"Analytics Celery/Stats Event HTTP{response.status_code}") + return response.status_code + except requests.exceptions.HTTPError as e: + logger.debug(e) + return response.status_code + except requests.exceptions.ConnectionError as e: + logger.debug(e) + return "Failed" diff --git a/allianceauth/analytics/tests/test_integration.py b/allianceauth/analytics/tests/test_integration.py deleted file mode 100644 index 81a648aa..00000000 --- a/allianceauth/analytics/tests/test_integration.py +++ /dev/null @@ -1,109 +0,0 @@ -from unittest.mock import patch -from urllib.parse import parse_qs - -import requests_mock - -from django.test import override_settings - -from allianceauth.analytics.tasks import ANALYTICS_URL -from allianceauth.eveonline.tasks import update_character -from allianceauth.tests.auth_utils import AuthUtils -from allianceauth.utils.testing import NoSocketsTestCase - - -@override_settings(CELERY_ALWAYS_EAGER=True) -@requests_mock.mock() -class TestAnalyticsForViews(NoSocketsTestCase): - @override_settings(ANALYTICS_DISABLED=False) - def test_should_run_analytics(self, requests_mocker): - # given - requests_mocker.post(ANALYTICS_URL) - user = AuthUtils.create_user("Bruce Wayne") - self.client.force_login(user) - # when - response = self.client.get("/dashboard/") - # then - self.assertEqual(response.status_code, 200) - self.assertTrue(requests_mocker.called) - - @override_settings(ANALYTICS_DISABLED=True) - def test_should_not_run_analytics(self, requests_mocker): - # given - requests_mocker.post(ANALYTICS_URL) - user = AuthUtils.create_user("Bruce Wayne") - self.client.force_login(user) - # when - response = self.client.get("/dashboard/") - # then - self.assertEqual(response.status_code, 200) - self.assertFalse(requests_mocker.called) - - -@override_settings(CELERY_ALWAYS_EAGER=True) -@requests_mock.mock() -class TestAnalyticsForTasks(NoSocketsTestCase): - @override_settings(ANALYTICS_DISABLED=False) - @patch("allianceauth.eveonline.models.EveCharacter.objects.update_character") - def test_should_run_analytics_for_successful_task( - self, requests_mocker, mock_update_character - ): - # given - requests_mocker.post(ANALYTICS_URL) - user = AuthUtils.create_user("Bruce Wayne") - character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001) - # when - update_character.delay(character.character_id) - # then - self.assertTrue(mock_update_character.called) - self.assertTrue(requests_mocker.called) - payload = parse_qs(requests_mocker.last_request.text) - self.assertListEqual(payload["el"], ["Success"]) - - @override_settings(ANALYTICS_DISABLED=True) - @patch("allianceauth.eveonline.models.EveCharacter.objects.update_character") - def test_should_not_run_analytics_for_successful_task( - self, requests_mocker, mock_update_character - ): - # given - requests_mocker.post(ANALYTICS_URL) - user = AuthUtils.create_user("Bruce Wayne") - character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001) - # when - update_character.delay(character.character_id) - # then - self.assertTrue(mock_update_character.called) - self.assertFalse(requests_mocker.called) - - @override_settings(ANALYTICS_DISABLED=False) - @patch("allianceauth.eveonline.models.EveCharacter.objects.update_character") - def test_should_run_analytics_for_failed_task( - self, requests_mocker, mock_update_character - ): - # given - requests_mocker.post(ANALYTICS_URL) - mock_update_character.side_effect = RuntimeError - user = AuthUtils.create_user("Bruce Wayne") - character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001) - # when - update_character.delay(character.character_id) - # then - self.assertTrue(mock_update_character.called) - self.assertTrue(requests_mocker.called) - payload = parse_qs(requests_mocker.last_request.text) - self.assertNotEqual(payload["el"], ["Success"]) - - @override_settings(ANALYTICS_DISABLED=True) - @patch("allianceauth.eveonline.models.EveCharacter.objects.update_character") - def test_should_not_run_analytics_for_failed_task( - self, requests_mocker, mock_update_character - ): - # given - requests_mocker.post(ANALYTICS_URL) - mock_update_character.side_effect = RuntimeError - user = AuthUtils.create_user("Bruce Wayne") - character = AuthUtils.add_main_character_2(user, "Bruce Wayne", 1001) - # when - update_character.delay(character.character_id) - # then - self.assertTrue(mock_update_character.called) - self.assertFalse(requests_mocker.called) diff --git a/allianceauth/analytics/tests/test_middleware.py b/allianceauth/analytics/tests/test_middleware.py deleted file mode 100644 index c4ca3257..00000000 --- a/allianceauth/analytics/tests/test_middleware.py +++ /dev/null @@ -1,24 +0,0 @@ -from allianceauth.analytics.middleware import AnalyticsMiddleware -from unittest.mock import Mock -from django.http import HttpResponse - -from django.test.testcases import TestCase - - -class TestAnalyticsMiddleware(TestCase): - - def setUp(self): - self.middleware = AnalyticsMiddleware(HttpResponse) - self.request = Mock() - self.request.headers = { - "User-Agent": "AUTOMATED TEST" - } - self.request.path = '/testURL/' - self.request.session = {} - self.request.LANGUAGE_CODE = 'en' - self.response = Mock() - self.response.content = 'hello world' - - def test_middleware(self): - response = self.middleware.process_response(self.request, self.response) - self.assertEqual(self.response, response) diff --git a/allianceauth/analytics/tests/test_models.py b/allianceauth/analytics/tests/test_models.py index fd424f11..452c4eaa 100644 --- a/allianceauth/analytics/tests/test_models.py +++ b/allianceauth/analytics/tests/test_models.py @@ -23,4 +23,5 @@ class TestAnalyticsIdentifier(TestCase): with self.assertRaises(ValidationError): AnalyticsIdentifier.objects.create(identifier=uuid_2) self.assertEqual(AnalyticsIdentifier.objects.count(), 1) - self.assertEqual(AnalyticsIdentifier.objects.get(pk=1).identifier, UUID(uuid_1)) + self.assertEqual(AnalyticsIdentifier.objects.get( + pk=1).identifier, UUID(uuid_1)) diff --git a/allianceauth/analytics/tests/test_tasks.py b/allianceauth/analytics/tests/test_tasks.py index 4b7f4918..0a542e23 100644 --- a/allianceauth/analytics/tests/test_tasks.py +++ b/allianceauth/analytics/tests/test_tasks.py @@ -4,12 +4,11 @@ from django.test.utils import override_settings from allianceauth.analytics.tasks import ( analytics_event, - send_ga_tracking_celery_event, - send_ga_tracking_web_view) + send_ga_tracking_celery_event) from allianceauth.utils.testing import NoSocketsTestCase -GOOGLE_ANALYTICS_DEBUG_URL = 'https://www.google-analytics.com/debug/collect' +GOOGLE_ANALYTICS_DEBUG_URL = 'https://www.google-analytics.com/debug/mp/collect' @override_settings(CELERY_ALWAYS_EAGER=True, CELERY_EAGER_PROPAGATES_EXCEPTIONS=True) @@ -18,195 +17,53 @@ class TestAnalyticsTasks(NoSocketsTestCase): def test_analytics_event(self, requests_mocker): requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL) analytics_event( - category='allianceauth.analytics', - action='send_tests', - label='test', - value=1, - event_type='Stats') - - def test_send_ga_tracking_web_view_sent(self, requests_mocker): - """This test sends if the event SENDS to google. - Not if it was successful. - """ - # given - requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL) - tracking_id = 'UA-186249766-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' - page = '/index/' - title = 'Hello World' - locale = 'en' - useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" - # when - response = send_ga_tracking_web_view( - tracking_id, - client_id, - page, - title, - locale, - useragent) - # then - self.assertEqual(response.status_code, 200) - - def test_send_ga_tracking_web_view_success(self, requests_mocker): - # given - requests_mocker.register_uri( - 'POST', - GOOGLE_ANALYTICS_DEBUG_URL, - json={"hitParsingResult":[{'valid': True}]} - ) - tracking_id = 'UA-186249766-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' - page = '/index/' - title = 'Hello World' - locale = 'en' - useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" - # when - json_response = send_ga_tracking_web_view( - tracking_id, - client_id, - page, - title, - locale, - useragent).json() - # then - self.assertTrue(json_response["hitParsingResult"][0]["valid"]) - - def test_send_ga_tracking_web_view_invalid_token(self, requests_mocker): - # given - requests_mocker.register_uri( - 'POST', - GOOGLE_ANALYTICS_DEBUG_URL, - json={ - "hitParsingResult":[ - { - 'valid': False, - 'parserMessage': [ - { - 'messageType': 'INFO', - 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', - 'messageCode': 'VALUE_MODIFIED' - }, - { - 'messageType': 'ERROR', - 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", - 'messageCode': 'VALUE_INVALID', 'parameter': 'tid' - } - ], - 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2' - } - ] - } - ) - tracking_id = 'UA-IntentionallyBadTrackingID-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' - page = '/index/' - title = 'Hello World' - locale = 'en' - useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" - # when - json_response = send_ga_tracking_web_view( - tracking_id, - client_id, - page, - title, - locale, - useragent).json() - # then - self.assertFalse(json_response["hitParsingResult"][0]["valid"]) - self.assertEqual( - json_response["hitParsingResult"][0]["parserMessage"][1]["description"], - "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details." - ) - - # [{'valid': False, 'parserMessage': [{'messageType': 'INFO', 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', 'messageCode': 'VALUE_MODIFIED'}, {'messageType': 'ERROR', 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", 'messageCode': 'VALUE_INVALID', 'parameter': 'tid'}], 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2'}] + namespace='allianceauth.analytics', + task='send_tests', + label='test', + value=1, + result="Success", + event_type='Stats') def test_send_ga_tracking_celery_event_sent(self, requests_mocker): # given requests_mocker.register_uri('POST', GOOGLE_ANALYTICS_DEBUG_URL) - tracking_id = 'UA-186249766-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' + token = 'G-6LYSMYK8DE' + secret = 'KLlpjLZ-SRGozS5f5wb_kw', category = 'test' action = 'test' label = 'test' value = '1' # when - response = send_ga_tracking_celery_event( - tracking_id, - client_id, - category, - action, - label, - value) + task = send_ga_tracking_celery_event( + token, + secret, + category, + action, + label, + value) # then - self.assertEqual(response.status_code, 200) + self.assertEqual(task, 200) def test_send_ga_tracking_celery_event_success(self, requests_mocker): # given requests_mocker.register_uri( 'POST', GOOGLE_ANALYTICS_DEBUG_URL, - json={"hitParsingResult":[{'valid': True}]} + json={"validationMessages": []} ) - tracking_id = 'UA-186249766-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' + token = 'G-6LYSMYK8DE' + secret = 'KLlpjLZ-SRGozS5f5wb_kw', category = 'test' action = 'test' label = 'test' value = '1' # when - json_response = send_ga_tracking_celery_event( - tracking_id, - client_id, - category, - action, - label, - value).json() + task = send_ga_tracking_celery_event( + token, + secret, + category, + action, + label, + value) # then - self.assertTrue(json_response["hitParsingResult"][0]["valid"]) - - def test_send_ga_tracking_celery_event_invalid_token(self, requests_mocker): - # given - requests_mocker.register_uri( - 'POST', - GOOGLE_ANALYTICS_DEBUG_URL, - json={ - "hitParsingResult":[ - { - 'valid': False, - 'parserMessage': [ - { - 'messageType': 'INFO', - 'description': 'IP Address from this hit was anonymized to 1.132.110.0.', - 'messageCode': 'VALUE_MODIFIED' - }, - { - 'messageType': 'ERROR', - 'description': "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details.", - 'messageCode': 'VALUE_INVALID', 'parameter': 'tid' - } - ], - 'hit': '/debug/collect?v=1&tid=UA-IntentionallyBadTrackingID-2&cid=ab33e241fbf042b6aa77c7655a768af7&t=pageview&dp=/index/&dt=Hello World&ul=en&ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36&aip=1&an=allianceauth&av=2.9.0a2' - } - ] - } - ) - tracking_id = 'UA-IntentionallyBadTrackingID-2' - client_id = 'ab33e241fbf042b6aa77c7655a768af7' - category = 'test' - action = 'test' - label = 'test' - value = '1' - # when - json_response = send_ga_tracking_celery_event( - tracking_id, - client_id, - category, - action, - label, - value).json() - # then - self.assertFalse(json_response["hitParsingResult"][0]["valid"]) - self.assertEqual( - json_response["hitParsingResult"][0]["parserMessage"][1]["description"], - "The value provided for parameter 'tid' is invalid. Please see http://goo.gl/a8d4RP#tid for details." - ) + self.assertTrue(task, 200) diff --git a/allianceauth/analytics/tests/test_utils.py b/allianceauth/analytics/tests/test_utils.py index 0da45c23..b9a22329 100644 --- a/allianceauth/analytics/tests/test_utils.py +++ b/allianceauth/analytics/tests/test_utils.py @@ -18,17 +18,17 @@ def create_testdata(): 'abc@example.com', 'password' ) - #Token.objects.all().delete() - #Token.objects.create( + # Token.objects.all().delete() + # Token.objects.create( # character_id=101, # character_name='character1', # access_token='my_access_token' - #) - #Token.objects.create( + # ) + # Token.objects.create( # character_id=102, # character_name='character2', # access_token='my_access_token' - #) + # ) class TestAnalyticsUtils(TestCase): @@ -40,7 +40,7 @@ class TestAnalyticsUtils(TestCase): users = install_stat_users() self.assertEqual(users, expected) - #def test_install_stat_tokens(self): + # def test_install_stat_tokens(self): # create_testdata() # expected = 2 # diff --git a/allianceauth/authentication/task_statistics/tests/test_signals.py b/allianceauth/authentication/task_statistics/tests/test_signals.py index aeeb8d60..fd1530fa 100644 --- a/allianceauth/authentication/task_statistics/tests/test_signals.py +++ b/allianceauth/authentication/task_statistics/tests/test_signals.py @@ -17,7 +17,7 @@ from allianceauth.eveonline.tasks import update_character @override_settings( - CELERY_ALWAYS_EAGER=True,ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED=False + CELERY_ALWAYS_EAGER=True, ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED=False ) class TestTaskSignals(TestCase): fixtures = ["disable_analytics"] diff --git a/allianceauth/project_template/project_name/settings/base.py b/allianceauth/project_template/project_name/settings/base.py index b2eb0257..79c34d14 100644 --- a/allianceauth/project_template/project_name/settings/base.py +++ b/allianceauth/project_template/project_name/settings/base.py @@ -75,7 +75,6 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'allianceauth.analytics.middleware.AnalyticsMiddleware', ] ROOT_URLCONF = 'allianceauth.urls' diff --git a/allianceauth/services/modules/discord/tests/test_integration.py b/allianceauth/services/modules/discord/tests/test_integration.py index 2d50db5e..a6c7fd65 100644 --- a/allianceauth/services/modules/discord/tests/test_integration.py +++ b/allianceauth/services/modules/discord/tests/test_integration.py @@ -377,6 +377,7 @@ class TestTasks(NoSocketsTestCase): @patch(MODULE_PATH + '.models.DISCORD_GUILD_ID', TEST_GUILD_ID) @requests_mock.Mocker() class StateTestCase(NoSocketsTestCase): + fixtures = ['disable_analytics.json'] def setUp(self): clear_cache()