Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ Section Order:

### Fixed

- Class 'Iterable' does not define '__sub__', so the '-' operator cannot be used on
- Class `Iterable` does not define `__sub__`, so the `-` operator cannot be used on
its instances

### Changed

- Task `QueueOnce` options
- Modular providers

## [4.1.0] - 2026-05-19

Expand Down
8 changes: 4 additions & 4 deletions sovtimer/locale/django.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: AA Sov Timer 4.1.0\n"
"Report-Msgid-Bugs-To: https://github.com/ppfeufer/aa-sov-timer/issues\n"
"POT-Creation-Date: 2026-05-21 19:59+0200\n"
"POT-Creation-Date: 2026-06-02 18:04+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -127,14 +127,14 @@ msgstr ""
msgid "Join our team of translators!"
msgstr ""

#: sovtimer/views.py:148
#: sovtimer/views.py:147
msgid "Defenders making progress"
msgstr ""

#: sovtimer/views.py:152
#: sovtimer/views.py:151
msgid "Attackers making progress"
msgstr ""

#: sovtimer/views.py:156
#: sovtimer/views.py:155
msgid "Neither side has made any progress yet"
msgstr ""
6 changes: 3 additions & 3 deletions sovtimer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from allianceauth.services.hooks import get_extension_logger

# AA Sovereignty Timer
from sovtimer import __title__
from sovtimer.providers import AppLogger, ESIHandler
from sovtimer.providers.applogger import AppLogger
from sovtimer.providers.esi import ESIHandler

if TYPE_CHECKING:
# Third Party
Expand All @@ -27,7 +27,7 @@
# Alliance Auth
from esi.stubs import SovereigntyCampaignsGetItem

logger = AppLogger(my_logger=get_extension_logger(name=__name__), prefix=__title__)
logger = AppLogger(my_logger=get_extension_logger(name=__name__))


class AaSovtimer(models.Model):
Expand Down
3 changes: 3 additions & 0 deletions sovtimer/providers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Initialize the providers
"""
44 changes: 44 additions & 0 deletions sovtimer/providers/applogger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
AppLogger provider
"""

# Standard Library
import logging

# AA Sovereignty Timer
from sovtimer import __title__


class AppLogger(logging.LoggerAdapter):
"""
Custom logger adapter that adds a prefix to log messages.

Taken from the `allianceauth-app-utils` package.
Credits to: Erik Kalkoken
"""

def __init__(self, my_logger: logging.Logger):
"""
Initializes the AppLogger with a logger and a prefix.

:param my_logger: Logger instance
:type my_logger: logging.Logger
"""

super().__init__(my_logger, {})

self.prefix = __title__

def process(self, msg, kwargs):
"""
Prepares the log message by adding the prefix.

:param msg: Log message
:type msg: str
:param kwargs: Additional keyword arguments
:type kwargs: dict
:return: Prefixed log message and kwargs
:rtype: tuple
"""

return f"[{self.prefix}] {msg}", kwargs
45 changes: 3 additions & 42 deletions sovtimer/providers.py → sovtimer/providers/esi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

# Standard Library
import logging
from typing import TYPE_CHECKING, Any

# Third Party
Expand All @@ -20,14 +19,16 @@
__app_name_verbose__,
__esi_compatibility_date__,
__github_url__,
__title__,
__version__,
)
from sovtimer.providers.applogger import AppLogger

if TYPE_CHECKING:
# Alliance Auth
from esi.stubs import AllianceDetail, SovereigntyCampaignsGetItem

logger = AppLogger(get_extension_logger(__name__))

# ESI client
esi = ESIClientProvider(
# Use the latest compatibility date, see https://esi.evetech.net/meta/compatibility-dates
Expand Down Expand Up @@ -230,43 +231,3 @@ def get_sovereignty_systems(
force_refresh=force_refresh,
return_response=return_response,
)


class AppLogger(logging.LoggerAdapter):
"""
Custom logger adapter that adds a prefix to log messages.

Taken from the `allianceauth-app-utils` package.
Credits to: Erik Kalkoken
"""

def __init__(self, my_logger: logging.Logger, prefix: str = "Sovereignty Timer"):
"""
Initializes the AppLogger with a logger and a prefix.

:param my_logger: Logger instance
:type my_logger: logging.Logger
:param prefix: Prefix string to add to log messages
:type prefix: str
"""

super().__init__(my_logger, {})

self.prefix = prefix

def process(self, msg, kwargs):
"""
Prepares the log message by adding the prefix.

:param msg: Log message
:type msg: str
:param kwargs: Additional keyword arguments
:type kwargs: dict
:return: Prefixed log message and kwargs
:rtype: tuple
"""

return f"[{self.prefix}] {msg}", kwargs


logger = AppLogger(my_logger=get_extension_logger(name=__name__), prefix=__title__)
5 changes: 2 additions & 3 deletions sovtimer/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
from allianceauth.services.tasks import QueueOnce

# AA Sovereignty Timer
from sovtimer import __title__
from sovtimer.constants import Constants
from sovtimer.models import Alliance, Campaign, SovereigntyStructure
from sovtimer.providers import AppLogger
from sovtimer.providers.applogger import AppLogger

logger = AppLogger(my_logger=get_extension_logger(name=__name__), prefix=__title__)
logger = AppLogger(my_logger=get_extension_logger(name=__name__))


# Params for all tasks
Expand Down
104 changes: 29 additions & 75 deletions sovtimer/tests/test_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from esi.exceptions import HTTPClientError, HTTPNotModified

# AA Sovereignty Timer
from sovtimer.providers import AppLogger, ESIHandler
from sovtimer import __title__
from sovtimer.providers.applogger import AppLogger
from sovtimer.providers.esi import ESIHandler
from sovtimer.tests import BaseTestCase


Expand Down Expand Up @@ -48,7 +50,7 @@ def test_when_typing_TYPE_CHECKING_true_then_stub_types_are_imported_into_module
sys.modules["esi.stubs"] = fake_stubs

with patch.object(typing, "TYPE_CHECKING", True):
providers = importlib.import_module("sovtimer.providers")
providers = importlib.import_module("sovtimer.providers.esi")
importlib.reload(providers)

self.assertTrue(hasattr(providers, "AllianceDetail"))
Expand All @@ -58,7 +60,7 @@ def test_when_typing_TYPE_CHECKING_true_then_stub_types_are_imported_into_module
sys.modules.update(original_sys_modules)

try:
importlib.reload(importlib.import_module("sovtimer.providers"))
importlib.reload(importlib.import_module("sovtimer.providers.esi"))
except Exception:
pass

Expand Down Expand Up @@ -240,8 +242,8 @@ def test_returns_campaigns_when_esi_returns_data(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.ESIHandler.result") as mock_result,
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch("sovtimer.providers.esi.ESIHandler.result") as mock_result,
):
mock_result.return_value = [{"campaign_id": 1}, {"campaign_id": 2}]

Expand All @@ -264,9 +266,10 @@ def test_raises_exception_when_result_raises(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch(
"sovtimer.providers.ESIHandler.result", side_effect=Exception("Error")
"sovtimer.providers.esi.ESIHandler.result",
side_effect=Exception("Error"),
) as mock_result,
):
with self.assertRaises(Exception):
Expand All @@ -288,9 +291,9 @@ def test_logs_debug_message_when_fetching_campaigns(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.logger.debug") as mock_logger,
patch("sovtimer.providers.ESIHandler.result") as mock_result,
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch("sovtimer.providers.esi.logger.debug") as mock_logger,
patch("sovtimer.providers.esi.ESIHandler.result") as mock_result,
):
mock_result.return_value = [{"campaign_id": 1}]

Expand All @@ -304,8 +307,8 @@ class TestESIHandlerGetAlliancesAllianceId(BaseTestCase):
Test the ESIHandler.get_alliances_alliance_id method.
"""

@patch("sovtimer.providers.esi", new=MagicMock())
@patch("sovtimer.providers.ESIHandler.result")
@patch("sovtimer.providers.esi.esi", new=MagicMock())
@patch("sovtimer.providers.esi.ESIHandler.result")
def test_returns_alliance_data_when_operation_succeeds(self, mock_result):
"""
Test that the method returns alliance data when the ESI operation succeeds.
Expand All @@ -327,8 +330,8 @@ def test_returns_alliance_data_when_operation_succeeds(self, mock_result):
self.assertIn("operation", called_kwargs)
self.assertFalse(called_kwargs.get("force_refresh"))

@patch("sovtimer.providers.esi", new=MagicMock())
@patch("sovtimer.providers.ESIHandler.result")
@patch("sovtimer.providers.esi.esi", new=MagicMock())
@patch("sovtimer.providers.esi.ESIHandler.result")
def test_passes_force_refresh_to_result_operation(self, mock_result):
"""
Test that the force_refresh parameter is passed correctly to the ESIHandler.result method.
Expand Down Expand Up @@ -367,8 +370,8 @@ def test_returns_systems_when_esi_returns_data(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.ESIHandler.result") as mock_result,
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch("sovtimer.providers.esi.ESIHandler.result") as mock_result,
):
mock_result.return_value = [{"system_id": 1}, {"system_id": 2}]

Expand All @@ -391,9 +394,10 @@ def test_raises_exception_when_result_raises(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch(
"sovtimer.providers.ESIHandler.result", side_effect=Exception("Error")
"sovtimer.providers.esi.ESIHandler.result",
side_effect=Exception("Error"),
) as mock_result,
):
with self.assertRaises(Exception):
Expand All @@ -413,9 +417,9 @@ def test_logs_debug_message_when_fetching_systems(self):
"""

with (
patch("sovtimer.providers.esi", new=MagicMock()),
patch("sovtimer.providers.logger.debug") as mock_logger,
patch("sovtimer.providers.ESIHandler.result") as mock_result,
patch("sovtimer.providers.esi.esi", new=MagicMock()),
patch("sovtimer.providers.esi.logger.debug") as mock_logger,
patch("sovtimer.providers.esi.ESIHandler.result") as mock_result,
):
mock_result.return_value = [{"system_id": 1}]

Expand All @@ -438,62 +442,12 @@ def test_adds_prefix_to_log_message(self):
"""

logger = logging.getLogger("test_logger")
app_logger = AppLogger(logger, "PREFIX")
app_logger = AppLogger(logger)

with self.assertLogs("test_logger", level="INFO") as log:
app_logger.info("This is a test message")

self.assertIn("[PREFIX] This is a test message", log.output[0])

def test_handles_empty_prefix(self):
"""
Tests that the AppLogger handles an empty prefix correctly.

:return:
:rtype:
"""

logger = logging.getLogger("test_logger")
app_logger = AppLogger(logger, "")

with self.assertLogs("test_logger", level="INFO") as log:
app_logger.info("Message without prefix")

self.assertIn("Message without prefix", log.output[0])

def test_handles_non_string_prefix(self):
"""
Tests that the AppLogger handles a non-string prefix correctly.

:return:
:rtype:
"""

logger = logging.getLogger("test_logger")
app_logger = AppLogger(logger, 123)

with self.assertLogs("test_logger", level="INFO") as log:
app_logger.info("Message with numeric prefix")

self.assertIn("[123] Message with numeric prefix", log.output[0])

def test_handles_special_characters_in_prefix(self):
"""
Tests that the AppLogger handles special characters in the prefix correctly.

:return:
:rtype:
"""

logger = logging.getLogger("test_logger")
app_logger = AppLogger(logger, "!@#$%^&*()")

with self.assertLogs("test_logger", level="INFO") as log:
app_logger.info("Message with special characters in prefix")

self.assertIn(
"[!@#$%^&*()] Message with special characters in prefix", log.output[0]
)
self.assertIn(f"[{__title__}] This is a test message", log.output[0])

def test_handles_empty_message(self):
"""
Expand All @@ -504,9 +458,9 @@ def test_handles_empty_message(self):
"""

logger = logging.getLogger("test_logger")
app_logger = AppLogger(logger, "PREFIX")
app_logger = AppLogger(logger)

with self.assertLogs("test_logger", level="INFO") as log:
app_logger.info("")

self.assertIn("[PREFIX] ", log.output[0])
self.assertIn(f"[{__title__}] ", log.output[0])
Loading
Loading