diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8374404..5b705f9d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,25 @@ # Update this file: # pre-commit autoupdate +# Set the default language versions for the hooks +default_language_version: + python: python3 # Force all Python hooks to use Python 3 + node: 22.12.0 # Force all Node hooks to use Node 22.12.0 + +# Globally exclude files +# https://pre-commit.com/#top_level-exclude +exclude: | + (?x)( + LICENSE| + allianceauth\/static\/allianceauth\/css\/themes\/bootstrap-locals.less| + \.min\.css| + \.min\.js| + \.po| + \.mo| + swagger\.json| + static/(.*)/libs/ + ) + repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.1 @@ -33,9 +52,9 @@ repos: - id: detect-private-key - id: check-case-conflict # Python checks - # - id: check-docstring-first +# - id: check-docstring-first - id: debug-statements - # - id: requirements-txt-fixer +# - id: requirements-txt-fixer - id: fix-encoding-pragma args: [--remove] - id: fix-byte-order-marker @@ -44,40 +63,17 @@ repos: args: [--fix=lf] - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - exclude: | - (?x)( - \.min\.css| - \.min\.js| - \.po| - \.mo| - swagger\.json - ) - id: check-executables-have-shebangs - id: end-of-file-fixer - exclude: | - (?x)( - \.min\.css| - \.min\.js| - \.po| - \.mo| - swagger\.json - ) - repo: https://github.com/editorconfig-checker/editorconfig-checker.python - rev: 3.0.3 + rev: 3.2.0 hooks: - id: editorconfig-checker - exclude: | - (?x)( - LICENSE| - allianceauth\/static\/allianceauth\/css\/themes\/bootstrap-locals.less| - \.po| - \.mo| - swagger\.json - ) - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.43.0 + rev: v0.44.0 hooks: - id: markdownlint + language: node args: - --disable=MD013 # Infrastructure @@ -90,7 +86,7 @@ repos: args: - --indent=4 additional_dependencies: - - tox==4.18.1 # https://github.com/tox-dev/tox/releases/latest + - tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest - repo: https://github.com/abravalheri/validate-pyproject rev: v0.23 hooks: diff --git a/README.md b/README.md index 5e9f5e94..1b4232ea 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [](https://pypi.org/project/allianceauth/) [](https://pypi.org/project/allianceauth/) [](https://gitlab.com/allianceauth/allianceauth/commits/master) -[](http://allianceauth.readthedocs.io/?badge=latest) +[](https://allianceauth.readthedocs.io/?badge=latest) [](https://gitlab.com/allianceauth/allianceauth/commits/master) [](https://discord.gg/fjnHAmk) @@ -14,7 +14,7 @@ An auth system for EVE Online to help in-game organizations manage online servic ## Content - [Overview](#overview) -- [Documentation](http://allianceauth.rtfd.io) +- [Documentation](https://allianceauth.rtfd.io) - [Support](#support) - [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases) - [Developer Team](#development-team) @@ -38,7 +38,7 @@ Main features: - English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr:, Russian :flag_ru:, Italian :flag_it:, French :flag_fr:, Japanese :flag_jp: and Ukrainian :flag_ua: Localization -For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io). +For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](https://allianceauth.rtfd.io). ## Screenshot diff --git a/allianceauth/__init__.py b/allianceauth/__init__.py index 91b57af5..6dbff817 100644 --- a/allianceauth/__init__.py +++ b/allianceauth/__init__.py @@ -5,7 +5,7 @@ manage online service access. # This will make sure the app is always imported when # Django starts so that shared_task will use this app. -__version__ = '4.3.1' +__version__ = '4.6.4' __title__ = 'Alliance Auth' __url__ = 'https://gitlab.com/allianceauth/allianceauth' NAME = f'{__title__} v{__version__}' diff --git a/allianceauth/analytics/admin.py b/allianceauth/analytics/admin.py index 383bbbe2..a60ea3b8 100644 --- a/allianceauth/analytics/admin.py +++ b/allianceauth/analytics/admin.py @@ -1,15 +1,17 @@ +from solo.admin import SingletonModelAdmin + from django.contrib import admin from .models import AnalyticsIdentifier, AnalyticsTokens @admin.register(AnalyticsIdentifier) -class AnalyticsIdentifierAdmin(admin.ModelAdmin): +class AnalyticsIdentifierAdmin(SingletonModelAdmin): search_fields = ['identifier', ] - list_display = ('identifier',) + list_display = ['identifier', ] @admin.register(AnalyticsTokens) class AnalyticsTokensAdmin(admin.ModelAdmin): search_fields = ['name', ] - list_display = ('name', 'type',) + list_display = ['name', 'type', ] diff --git a/allianceauth/analytics/migrations/0010_alter_analyticsidentifier_options.py b/allianceauth/analytics/migrations/0010_alter_analyticsidentifier_options.py new file mode 100644 index 00000000..7c1993e9 --- /dev/null +++ b/allianceauth/analytics/migrations/0010_alter_analyticsidentifier_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2024-12-11 02:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('analytics', '0009_remove_analyticstokens_ignore_paths_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='analyticsidentifier', + options={'verbose_name': 'Analytics Identifier'}, + ), + ] diff --git a/allianceauth/analytics/models.py b/allianceauth/analytics/models.py index 72d11abe..a1d1398b 100644 --- a/allianceauth/analytics/models.py +++ b/allianceauth/analytics/models.py @@ -1,27 +1,21 @@ +from typing import Literal from uuid import uuid4 -from django.core.exceptions import ValidationError +from solo.models import SingletonModel + from django.db import models from django.utils.translation import gettext_lazy as _ -class AnalyticsIdentifier(models.Model): +class AnalyticsIdentifier(SingletonModel): - identifier = models.UUIDField( - default=uuid4, - editable=False) + identifier = models.UUIDField(default=uuid4, editable=False) - def __str__(self) -> str: - return f"{self.identifier}" - - 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 - return super().save(*args, **kwargs) + def __str__(self) -> Literal['Analytics Identifier']: + return "Analytics Identifier" + class Meta: + verbose_name = "Analytics Identifier" class AnalyticsTokens(models.Model): diff --git a/allianceauth/analytics/tasks.py b/allianceauth/analytics/tasks.py index 00ed8202..2b2646f4 100644 --- a/allianceauth/analytics/tasks.py +++ b/allianceauth/analytics/tasks.py @@ -9,7 +9,7 @@ from django.conf import settings from allianceauth import __version__ from .models import AnalyticsIdentifier, AnalyticsTokens -from .utils import install_stat_addons, install_stat_tokens, install_stat_users +from .utils import existence_baremetal_or_docker, install_stat_addons, install_stat_tokens, install_stat_users logger = logging.getLogger(__name__) @@ -67,8 +67,8 @@ def analytics_event(namespace: str, value=value).apply_async(priority=9) -@shared_task() -def analytics_daily_stats(): +@shared_task +def analytics_daily_stats() -> None: """Celery Task: Do not call directly Gathers a series of daily statistics @@ -77,6 +77,7 @@ def analytics_daily_stats(): users = install_stat_users() tokens = install_stat_tokens() addons = install_stat_addons() + existence_type = existence_baremetal_or_docker() logger.debug("Running Daily Analytics Upload") analytics_event(namespace='allianceauth.analytics', @@ -84,6 +85,11 @@ def analytics_daily_stats(): label='existence', value=1, event_type='Stats') + analytics_event(namespace='allianceauth.analytics', + task='send_install_stats', + label=existence_type, + value=1, + event_type='Stats') analytics_event(namespace='allianceauth.analytics', task='send_install_stats', label='users', @@ -99,7 +105,6 @@ def analytics_daily_stats(): label='addons', value=addons, event_type='Stats') - for appconfig in apps.get_app_configs(): if appconfig.label in [ "django_celery_beat", @@ -135,7 +140,7 @@ def analytics_daily_stats(): event_type='Stats') -@shared_task() +@shared_task def send_ga_tracking_celery_event( measurement_id: str, secret: str, @@ -165,7 +170,7 @@ def send_ga_tracking_celery_event( } payload = { - 'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex, + 'client_id': AnalyticsIdentifier.get_solo().identifier.hex, "user_properties": { "allianceauth_version": { "value": __version__ diff --git a/allianceauth/analytics/tests/test_models.py b/allianceauth/analytics/tests/test_models.py index b430f03d..8c06f3fa 100644 --- a/allianceauth/analytics/tests/test_models.py +++ b/allianceauth/analytics/tests/test_models.py @@ -1,6 +1,5 @@ -from uuid import UUID, uuid4 +from uuid import uuid4 -from django.core.exceptions import ValidationError from django.test.testcases import TestCase from allianceauth.analytics.models import AnalyticsIdentifier @@ -13,14 +12,4 @@ uuid_2 = "7aa6bd70701f44729af5e3095ff4b55c" class TestAnalyticsIdentifier(TestCase): def test_identifier_random(self): - self.assertNotEqual(AnalyticsIdentifier.objects.get(), uuid4) - - def test_identifier_singular(self): - AnalyticsIdentifier.objects.all().delete() - AnalyticsIdentifier.objects.create(identifier=uuid_1) - # Yeah i have multiple asserts here, they all do the same thing - 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.assertNotEqual(AnalyticsIdentifier.get_solo(), uuid4) diff --git a/allianceauth/analytics/utils.py b/allianceauth/analytics/utils.py index d09cc65c..b2e3967d 100644 --- a/allianceauth/analytics/utils.py +++ b/allianceauth/analytics/utils.py @@ -1,3 +1,5 @@ +import os + from django.apps import apps from esi.models import Token @@ -36,3 +38,16 @@ def install_stat_addons() -> int: The Number of Installed Apps""" addons = len(list(apps.get_app_configs())) return addons + + +def existence_baremetal_or_docker() -> str: + """Checks the Installation Type of an install + + Returns + ------- + str + existence_baremetal or existence_docker""" + docker_tag = os.getenv('AA_DOCKER_TAG') + if docker_tag: + return "existence_docker" + return "existence_baremetal" diff --git a/allianceauth/authentication/templates/authentication/tokens.html b/allianceauth/authentication/templates/authentication/tokens.html index 42e3c61b..fc9b9c35 100644 --- a/allianceauth/authentication/templates/authentication/tokens.html +++ b/allianceauth/authentication/templates/authentication/tokens.html @@ -1,5 +1,6 @@ {% extends "allianceauth/base-bs5.html" %} +{% load aa_i18n %} {% load i18n %} {% block page_title %} @@ -13,7 +14,7 @@ {% block content %}
- {% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %} + {% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://developers.eveonline.com/authorized-apps where possible."|urlize %}
{% translate "Details" %} | -{% translate "Objective" %} | -{% translate "System" %} | -{% translate "Structure" %} | -{% translate "Eve Time" %} | -{% translate "Local Time" %} | -{% translate "Creator" %} | - - {% if perms.auth.timer_management %} -{% translate "Action" %} | - {% endif %} -||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
- {{ timer.details }}
-
- {% if timer.timer_type != 'UNSPECIFIED' %}
- - ({{ timer.get_timer_type_display }}) - {% endif %} - |
-
-
- {% if timer.objective == "Hostile" %}
- {% translate "Hostile" %}
- {% elif timer.objective == "Friendly" %}
- {% translate "Friendly" %}
- {% elif timer.objective == "Neutral" %}
- {% translate "Neutral" %}
- {% endif %}
- |
-
- - {{ timer.system }} {{ timer.planet_moon }} - | - -
- {% if timer.structure == "POCO" %}
- {% translate "POCO" %}
- {% elif timer.structure == "Orbital Skyhook" %}
- {% translate "Orbital Skyhook" %}
- {% elif timer.structure == "I-HUB" %}
- {% translate "I-HUB" %}
- {% elif timer.structure == "TCU" %} {% comment %} Pending Removal {% endcomment %}
- {% translate "TCU" %}
- {% elif timer.structure == "POS[S]" %}
- {% translate "POS [S]" %}
- {% elif timer.structure == "POS[M]" %}
- {% translate "POS [M]" %}
- {% elif timer.structure == "POS[L]" %}
- {% translate "POS [L]" %}
- {% elif timer.structure == "Citadel[M]" or timer.structure == "Astrahus" %}
- {% translate "Astrahus" %}
- {% elif timer.structure == "Citadel[L]" or timer.structure == "Fortizar" %}
- {% translate "Fortizar" %}
- {% elif timer.structure == "Citadel[XL]" or timer.structure == "Keepstar" %}
- {% translate "Keepstar" %}
- {% elif timer.structure == "Engineering Complex[M]" or timer.structure == "Raitaru" %}
- {% translate "Raitaru" %}
- {% elif timer.structure == "Engineering Complex[L]" or timer.structure == "Azbel" %}
- {% translate "Azbel" %}
- {% elif timer.structure == "Engineering Complex[XL]" or timer.structure == "Sotiyo" %}
- {% translate "Sotiyo" %}
- {% elif timer.structure == "Refinery[M]" or timer.structure == "Athanor" %}
- {% translate "Athanor" %}
- {% elif timer.structure == "Refinery[L]" or timer.structure == "Tatara" %}
- {% translate "Tatara" %}
- {% elif timer.structure == "Cyno Beacon" or timer.structure == "Pharolux Cyno Beacon" %}
- {% translate "Cyno Beacon" %}
- {% elif timer.structure == "Cyno Jammer" or timer.structure == "Tenebrex Cyno Jammer" %}
- {% translate "Cyno Jammer" %}
- {% elif timer.structure == "Jump Gate" or timer.structure == "Ansiblex Jump Gate" %}
- {% translate "Ansiblex Jump Gate" %}
- {% elif timer.structure == "Moon Mining Cycle" %}
- {% translate "Moon Mining Cycle" %}
- {% elif timer.structure == "Metenox Moon Drill" %}
- {% translate "Metenox Moon Drill" %}
- {% elif timer.structure == "Other" %}
- {% translate "Other" %}
- {% endif %}
- |
-
- {{ timer.eve_time | date:"Y-m-d H:i" }} | - -- - - | - -{{ timer.eve_character.character_name }} | +
{% translate "Details" %} | +{% translate "Objective" %} | +{% translate "System" %} | +{% translate "Structure" %} | +{% translate "Eve Time" %} | +{% translate "Local Time" %} | +{% translate "Creator" %} | {% if perms.auth.timer_management %} -- - - - - - - | +{% translate "Action" %} | {% endif %}
---|---|---|---|---|---|---|---|---|
+ {{ timer.details }}
+
+ {% if timer.timer_type != 'UNSPECIFIED' %}
+ + ({{ timer.get_timer_type_display }}) + {% endif %} + |
+
+
+ {% comment %} Objective: Hostile (BG: Danger) {% endcomment %}
+ {% if timer.objective == "Hostile" %}
+
+
+ {% comment %} Objective: Friendly (BG: Primare) {% endcomment %}
+ {% elif timer.objective == "Friendly" %}
+
+
+ {% comment %} Objective: Neutral (BG: Secondary) {% endcomment %}
+ {% elif timer.objective == "Neutral" %}
+
+ {% endif %}
+
+ {{ timer.get_objective_display }}
+
+ |
+
+ + {{ timer.system }} {{ timer.planet_moon }} + | + +
+
+ {{ timer.get_structure_display }}
+
+ |
+
+ {{ timer.eve_time | date:"Y-m-d H:i" }} | + ++ + + | + +{{ timer.eve_character.character_name }} | + + {% if perms.auth.timer_management %} ++ + + + + + + | + {% endif %} +