aa-ledger 0.9.9__py3-none-any.whl → 0.9.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {aa_ledger-0.9.9.dist-info → aa_ledger-0.9.9.1.dist-info}/METADATA +1 -1
  2. {aa_ledger-0.9.9.dist-info → aa_ledger-0.9.9.1.dist-info}/RECORD +77 -77
  3. ledger/__init__.py +9 -9
  4. ledger/api/api_helper/billboard_helper.py +277 -277
  5. ledger/api/ledger/admin.py +289 -289
  6. ledger/app_settings.py +50 -50
  7. ledger/constants.py +5 -0
  8. ledger/decorators.py +92 -11
  9. ledger/helpers/alliance.py +353 -334
  10. ledger/helpers/character.py +260 -260
  11. ledger/helpers/core.py +565 -565
  12. ledger/helpers/corporation.py +455 -421
  13. ledger/helpers/etag.py +237 -237
  14. ledger/helpers/ref_type.py +475 -475
  15. ledger/locale/cs_CZ/LC_MESSAGES/django.po +942 -942
  16. ledger/locale/de/LC_MESSAGES/django.po +961 -961
  17. ledger/locale/django.pot +942 -942
  18. ledger/locale/es/LC_MESSAGES/django.po +943 -943
  19. ledger/locale/fr_FR/LC_MESSAGES/django.po +942 -942
  20. ledger/locale/it_IT/LC_MESSAGES/django.po +942 -942
  21. ledger/locale/ja/LC_MESSAGES/django.po +943 -943
  22. ledger/locale/ko_KR/LC_MESSAGES/django.po +942 -942
  23. ledger/locale/nl_NL/LC_MESSAGES/django.po +942 -942
  24. ledger/locale/pl_PL/LC_MESSAGES/django.po +942 -942
  25. ledger/locale/ru/LC_MESSAGES/django.po +945 -945
  26. ledger/locale/sk/LC_MESSAGES/django.po +944 -944
  27. ledger/locale/uk/LC_MESSAGES/django.po +946 -946
  28. ledger/locale/zh_Hans/LC_MESSAGES/django.po +943 -943
  29. ledger/managers/character_mining_manager.py +239 -239
  30. ledger/managers/character_planetary_manager.py +1 -1
  31. ledger/migrations/0016_characterminingledger_price_per_unit.py +21 -21
  32. ledger/models/characteraudit.py +496 -496
  33. ledger/static/ledger/css/cards.css +1 -1
  34. ledger/static/ledger/css/table.css +1 -1
  35. ledger/static/ledger/js/charts.js +221 -221
  36. ledger/static/ledger/js/planetary.js +143 -143
  37. ledger/tasks.py +442 -449
  38. ledger/templates/ledger/allyledger/admin/alliance_administration.html +46 -46
  39. ledger/templates/ledger/allyledger/admin/alliance_overview.html +108 -108
  40. ledger/templates/ledger/allyledger/alliance_ledger.html +86 -86
  41. ledger/templates/ledger/bundles/ally-administration-bundles.html +59 -59
  42. ledger/templates/ledger/bundles/char-administration-bundles.html +66 -66
  43. ledger/templates/ledger/bundles/character-ledger-bundles.html +66 -66
  44. ledger/templates/ledger/bundles/corp-administration-bundles.html +68 -68
  45. ledger/templates/ledger/bundles/corporation-ledger-bundles.html +75 -75
  46. ledger/templates/ledger/charledger/admin/character_administration.html +39 -39
  47. ledger/templates/ledger/charledger/admin/character_overview.html +106 -106
  48. ledger/templates/ledger/charledger/character_ledger.html +94 -94
  49. ledger/templates/ledger/charledger/planetary/planetary_ledger.html +54 -54
  50. ledger/templates/ledger/corpledger/admin/corporation_administration.html +39 -39
  51. ledger/templates/ledger/corpledger/admin/corporation_overview.html +108 -108
  52. ledger/templates/ledger/corpledger/corporation_ledger.html +129 -86
  53. ledger/templates/ledger/partials/administration/alliance.html +37 -37
  54. ledger/templates/ledger/partials/administration/alliance_corporations.html +58 -58
  55. ledger/templates/ledger/partials/administration/corporation_characters.html +34 -34
  56. ledger/templates/ledger/partials/information/daily.html +56 -56
  57. ledger/templates/ledger/partials/information/day.html +48 -48
  58. ledger/templates/ledger/partials/information/hourly.html +53 -53
  59. ledger/templates/ledger/partials/information/summary.html +88 -88
  60. ledger/templates/ledger/partials/information/view_character_content.html +35 -35
  61. ledger/templates/ledger/partials/table/char-ledger.html +85 -85
  62. ledger/templates/ledger/partials/table/corp-ledger.html +66 -66
  63. ledger/templates/ledger/partials/view/card.html +160 -160
  64. ledger/tests/test_decarators.py +102 -17
  65. ledger/tests/test_helpers/test_etag.py +149 -149
  66. ledger/tests/test_managers/test_character_mining_manager.py +54 -54
  67. ledger/tests/test_models/test_characterminingledger.py +107 -106
  68. ledger/tests/test_tasks.py +282 -282
  69. ledger/tests/test_templatetags.py +5 -2
  70. ledger/tests/test_views/test_access.py +852 -852
  71. ledger/tests/testdata/esi.json +1 -2
  72. ledger/tests/testdata/eveuniverse.json +391 -391
  73. ledger/urls.py +66 -21
  74. ledger/views/alliance/alliance_ledger.py +203 -203
  75. ledger/views/corporation/corporation_ledger.py +25 -9
  76. {aa_ledger-0.9.9.dist-info → aa_ledger-0.9.9.1.dist-info}/WHEEL +0 -0
  77. {aa_ledger-0.9.9.dist-info → aa_ledger-0.9.9.1.dist-info}/licenses/LICENSE +0 -0
ledger/app_settings.py CHANGED
@@ -1,50 +1,50 @@
1
- """
2
- App Settings
3
- """
4
-
5
- # Standard Library
6
- import sys
7
-
8
- # Alliance Auth (External Libs)
9
- from app_utils.app_settings import clean_setting
10
-
11
- IS_TESTING = sys.argv[1:2] == ["test"]
12
-
13
- # Set Test Mode True or False
14
-
15
- # Set Naming on Auth Hook
16
- LEDGER_APP_NAME = clean_setting("LEDGER_APP_NAME", "Ledger")
17
-
18
- # zKillboard - https://zkillboard.com/
19
- EVE_BASE_URL = "https://esi.evetech.net/"
20
- EVE_API_URL = "https://esi.evetech.net/latest/"
21
- EVE_BASE_URL_REGEX = r"^http[s]?:\/\/esi.evetech\.net\/"
22
-
23
- # fuzzwork
24
- FUZZ_BASE_URL = "https://www.fuzzwork.co.uk/"
25
- FUZZ_API_URL = "https://www.fuzzwork.co.uk/api/"
26
- FUZZ_BASE_URL_REGEX = r"^http[s]?:\/\/(www\.)?fuzzwork\.co\.uk\/"
27
-
28
- # Global timeout for tasks in seconds to reduce task accumulation during outages.
29
- LEDGER_TASKS_TIME_LIMIT = clean_setting("LEDGER_TASKS_TIME_LIMIT", 600)
30
-
31
- LEDGER_STALE_TYPES = clean_setting(
32
- "LEDGER_STALE_TYPES",
33
- {
34
- "wallet_journal": 30,
35
- "wallet_division_names": 30,
36
- "wallet_division": 30,
37
- "mining_ledger": 30,
38
- "planets": 30,
39
- "planets_details": 30,
40
- },
41
- )
42
-
43
- # Mining Price Calculation
44
- LEDGER_USE_COMPRESSED = clean_setting("LEDGER_USE_COMPRESSED", True)
45
- LEDGER_PRICE_PERCENTAGE = clean_setting("LEDGER_PRICE_PERCENTAGE", 0.9)
46
-
47
- # Ledger Cache System
48
- LEDGER_CACHE_STALE = 60 * 60 * 168 # 168 hours
49
- LEDGER_CACHE_KEY = "LEDGER"
50
- LEDGER_CACHE_ENABLED = True
1
+ """
2
+ App Settings
3
+ """
4
+
5
+ # Standard Library
6
+ import sys
7
+
8
+ # Alliance Auth (External Libs)
9
+ from app_utils.app_settings import clean_setting
10
+
11
+ IS_TESTING = sys.argv[1:2] == ["test"]
12
+
13
+ # Set Test Mode True or False
14
+
15
+ # Set Naming on Auth Hook
16
+ LEDGER_APP_NAME = clean_setting("LEDGER_APP_NAME", "Ledger")
17
+
18
+ # zKillboard - https://zkillboard.com/
19
+ EVE_BASE_URL = "https://esi.evetech.net/"
20
+ EVE_API_URL = "https://esi.evetech.net/latest/"
21
+ EVE_BASE_URL_REGEX = r"^http[s]?:\/\/esi.evetech\.net\/"
22
+
23
+ # fuzzwork
24
+ FUZZ_BASE_URL = "https://www.fuzzwork.co.uk/"
25
+ FUZZ_API_URL = "https://www.fuzzwork.co.uk/api/"
26
+ FUZZ_BASE_URL_REGEX = r"^http[s]?:\/\/(www\.)?fuzzwork\.co\.uk\/"
27
+
28
+ # Global timeout for tasks in seconds to reduce task accumulation during outages.
29
+ LEDGER_TASKS_TIME_LIMIT = clean_setting("LEDGER_TASKS_TIME_LIMIT", 600)
30
+
31
+ LEDGER_STALE_TYPES = clean_setting(
32
+ "LEDGER_STALE_TYPES",
33
+ {
34
+ "wallet_journal": 30,
35
+ "wallet_division_names": 30,
36
+ "wallet_division": 30,
37
+ "mining_ledger": 30,
38
+ "planets": 30,
39
+ "planets_details": 30,
40
+ },
41
+ )
42
+
43
+ # Mining Price Calculation
44
+ LEDGER_USE_COMPRESSED = clean_setting("LEDGER_USE_COMPRESSED", True)
45
+ LEDGER_PRICE_PERCENTAGE = clean_setting("LEDGER_PRICE_PERCENTAGE", 0.9)
46
+
47
+ # Ledger Cache System
48
+ LEDGER_CACHE_STALE = 60 * 60 * 168 # 168 hours
49
+ LEDGER_CACHE_KEY = "LEDGER"
50
+ LEDGER_CACHE_ENABLED = True
ledger/constants.py CHANGED
@@ -139,6 +139,11 @@ P3_PRODUCTS = [
139
139
  P4_PRODUCTS = [2867, 2868, 2869, 2870, 2871, 2872, 2875, 2876]
140
140
  P5_PRODUCTS = []
141
141
 
142
+ NPC_ENTITIES = [
143
+ 1000125, # Concord Bounties (Bounty Prizes, ESS
144
+ 1000132, # Secure Commerce Commission (Market Fees)
145
+ 1000413, # Air Laboratories (Daily Login Rewards, etc.)
146
+ ]
142
147
 
143
148
  BOUNTY_PRIZES = ["bounty_prizes"]
144
149
  ESS_TRANSFER = ["ess_escrow_transfer"]
ledger/decorators.py CHANGED
@@ -4,13 +4,15 @@ Decorators
4
4
 
5
5
  # Standard Library
6
6
  import time
7
+ import uuid
7
8
  from functools import wraps
8
9
 
9
10
  # Alliance Auth
10
11
  from allianceauth.services.hooks import get_extension_logger
11
12
 
12
13
  # Alliance Auth (External Libs)
13
- from app_utils.esi import EsiDailyDowntime, fetch_esi_status
14
+ from app_utils.allianceauth import get_redis_client
15
+ from app_utils.esi import fetch_esi_status
14
16
  from app_utils.logging import LoggerAddTag
15
17
 
16
18
  # AA Ledger
@@ -20,25 +22,104 @@ from ledger.app_settings import IS_TESTING
20
22
  logger = LoggerAddTag(get_extension_logger(__name__), __title__)
21
23
 
22
24
 
23
- def when_esi_is_available(func):
24
- """Make sure the decorated task only runs when esi is available.
25
+ def _esi_cache_available(redis_client, cache_key):
26
+ """Return True if ESI availability cache is present. Handles Redis errors."""
27
+ try:
28
+ if redis_client.get(cache_key):
29
+ return True
30
+ except Exception: # pylint: disable=broad-except
31
+ # If Redis is flaky, fall through to live check
32
+ logger.debug("Error reading ESI cache, will attempt live check.")
33
+ return False
25
34
 
26
- Raise exception when ESI is offline.
27
- Complete the task without running it when downtime is detected.
28
35
 
36
+ def _acquire_lock(redis_client, lock_key, token, lock_ttl):
37
+ """Try to acquire a short-lived lock in Redis. Returns True on success."""
38
+ try:
39
+ return redis_client.set(lock_key, token, nx=True, ex=lock_ttl)
40
+ except Exception: # pylint: disable=broad-except
41
+ logger.exception("Failed to acquire ESI status lock, will attempt live check.")
42
+ return False
43
+
44
+
45
+ def _wait_for_lock_or_cache(redis_client, cache_key, lock_key, token, lock_ttl):
46
+ """Wait a short time for either the cache to appear or the lock to become available.
47
+
48
+ Returns a tuple (acquired, cache_available).
49
+ """
50
+ waited = 0.0
51
+ poll_interval = 0.1
52
+ max_wait = float(lock_ttl) - 0.1
53
+ while waited < max_wait:
54
+ try:
55
+ # If cache was set while waiting, proceed.
56
+ if _esi_cache_available(redis_client, cache_key):
57
+ logger.debug("ESI status became available in cache while waiting.")
58
+ return False, True
59
+
60
+ # If the lock expired or was released, try to acquire it.
61
+ if not redis_client.get(lock_key):
62
+ try:
63
+ acquired = redis_client.set(lock_key, token, nx=True, ex=lock_ttl)
64
+ if acquired:
65
+ # We acquired the lock and will perform the check
66
+ return True, False
67
+ except Exception: # pylint: disable=broad-except
68
+ logger.debug("Failed to re-acquire lock while waiting for holder.")
69
+ except Exception: # pylint: disable=broad-except
70
+ logger.debug("Error polling ESI cache while waiting for lock holder.")
71
+ time.sleep(poll_interval)
72
+ waited += poll_interval
73
+
74
+ return False, False
75
+
76
+
77
+ def when_esi_is_available(func):
78
+ """
79
+ Make sure the decorated task only runs when esi is available and store the result.
80
+ Complete the task without running it when downtime is detected.
29
81
  Automatically disabled during tests.
30
82
  """
31
83
 
32
84
  @wraps(func)
33
85
  def outer(*args, **kwargs):
34
- if IS_TESTING is not True:
86
+ # During tests we skip ESI checks
87
+ if IS_TESTING:
88
+ return func(*args, **kwargs)
89
+
90
+ cache_key = "esi_status_is_up"
91
+ redis_client = get_redis_client()
92
+
93
+ # Prevent hammering ESI status endpoint
94
+ if _esi_cache_available(redis_client, cache_key):
95
+ logger.debug("ESI status cached as available.")
96
+ return func(*args, **kwargs)
97
+
98
+ # Try to acquire a short-lived lock so only one process checks ESI
99
+ token = str(uuid.uuid4())
100
+ lock_key = "esi_status_lock"
101
+ lock_ttl = 5
102
+ acquired = _acquire_lock(redis_client, lock_key, token, lock_ttl)
103
+
104
+ # If we didn't get the lock, wait a short time for cache or lock
105
+ if not acquired:
106
+ acquired, cache_available = _wait_for_lock_or_cache(
107
+ redis_client, cache_key, lock_key, token, lock_ttl
108
+ )
109
+ if cache_available:
110
+ return func(*args, **kwargs)
111
+
112
+ # Check ESI status
113
+ if fetch_esi_status().is_ok:
114
+ logger.debug("ESI is available, proceeding.")
115
+ # 5 second TTL
35
116
  try:
36
- fetch_esi_status().raise_for_status()
37
- except EsiDailyDowntime:
38
- logger.info("Daily Downtime detected. Aborting.")
39
- return None # function will not run
117
+ redis_client.setex(cache_key, 5, "1")
118
+ except Exception: # pylint: disable=broad-except
119
+ logger.debug("Failed to set ESI availability cache.")
120
+ return func(*args, **kwargs)
40
121
 
41
- return func(*args, **kwargs)
122
+ return None # function will not run
42
123
 
43
124
  return outer
44
125