mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-13 14:30:17 +02:00
Analytics UA to V4 Conversion
This commit is contained in:
parent
6b932b1188
commit
a2f217ace5
@ -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',)
|
||||
|
@ -4,6 +4,3 @@ from django.apps import AppConfig
|
||||
class AnalyticsConfig(AppConfig):
|
||||
name = 'allianceauth.analytics'
|
||||
label = 'analytics'
|
||||
|
||||
def ready(self):
|
||||
import allianceauth.analytics.signals
|
||||
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
@ -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
|
@ -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),
|
||||
),
|
||||
]
|
@ -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)]
|
@ -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',
|
||||
),
|
||||
]
|
@ -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)
|
||||
|
@ -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)
|
@ -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"
|
||||
|
@ -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)
|
@ -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)
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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"]
|
||||
|
@ -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'
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user