aa-ledger 1.0.4__py3-none-any.whl → 2.0.0__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 (280) hide show
  1. {aa_ledger-1.0.4.dist-info → aa_ledger-2.0.0.dist-info}/METADATA +5 -6
  2. aa_ledger-2.0.0.dist-info/RECORD +267 -0
  3. ledger/__init__.py +2 -2
  4. ledger/admin.py +23 -18
  5. ledger/api/__init__.py +23 -7
  6. ledger/api/{ledger/admin.py → admin.py} +25 -31
  7. ledger/api/alliance.py +755 -0
  8. ledger/api/character.py +786 -0
  9. ledger/api/corporation.py +1141 -0
  10. ledger/api/{helpers.py → helpers/core.py} +33 -33
  11. ledger/api/helpers/icons.py +372 -0
  12. ledger/api/helpers/planetary_helper.py +354 -0
  13. ledger/api/planetary.py +354 -0
  14. ledger/api/schema.py +240 -15
  15. ledger/app_settings.py +11 -27
  16. ledger/auth_hooks.py +2 -2
  17. ledger/constants.py +50 -177
  18. ledger/decorators.py +2 -46
  19. ledger/forms.py +133 -39
  20. ledger/helpers/billboard.py +194 -144
  21. ledger/helpers/cache.py +105 -0
  22. ledger/helpers/discord.py +2 -4
  23. ledger/helpers/eveonline.py +160 -0
  24. ledger/helpers/ledger_data.py +23 -0
  25. ledger/helpers/ref_type.py +53 -78
  26. ledger/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  27. ledger/locale/cs_CZ/LC_MESSAGES/django.po +349 -193
  28. ledger/locale/de/LC_MESSAGES/django.mo +0 -0
  29. ledger/locale/de/LC_MESSAGES/django.po +528 -379
  30. ledger/locale/django.pot +721 -546
  31. ledger/locale/es/LC_MESSAGES/django.mo +0 -0
  32. ledger/locale/es/LC_MESSAGES/django.po +349 -194
  33. ledger/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  34. ledger/locale/fr_FR/LC_MESSAGES/django.po +349 -193
  35. ledger/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  36. ledger/locale/it_IT/LC_MESSAGES/django.po +349 -193
  37. ledger/locale/ja/LC_MESSAGES/django.mo +0 -0
  38. ledger/locale/ja/LC_MESSAGES/django.po +348 -193
  39. ledger/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  40. ledger/locale/ko_KR/LC_MESSAGES/django.po +349 -193
  41. ledger/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  42. ledger/locale/nl_NL/LC_MESSAGES/django.po +349 -193
  43. ledger/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  44. ledger/locale/pl_PL/LC_MESSAGES/django.po +350 -193
  45. ledger/locale/ru/LC_MESSAGES/django.mo +0 -0
  46. ledger/locale/ru/LC_MESSAGES/django.po +348 -193
  47. ledger/locale/sk/LC_MESSAGES/django.mo +0 -0
  48. ledger/locale/sk/LC_MESSAGES/django.po +348 -193
  49. ledger/locale/uk/LC_MESSAGES/django.mo +0 -0
  50. ledger/locale/uk/LC_MESSAGES/django.po +348 -193
  51. ledger/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  52. ledger/locale/zh_Hans/LC_MESSAGES/django.po +348 -193
  53. ledger/managers/character_audit_manager.py +28 -20
  54. ledger/managers/character_journal_manager.py +185 -357
  55. ledger/managers/character_mining_manager.py +52 -26
  56. ledger/managers/character_planetary_manager.py +178 -136
  57. ledger/managers/corporation_audit_manager.py +36 -27
  58. ledger/managers/corporation_journal_manager.py +92 -56
  59. ledger/managers/general_manager.py +8 -7
  60. ledger/migrations/0018_remove_characterplanet_ledger_char_planet__58a5b6_idx_and_more.py +44 -0
  61. ledger/migrations/0019_rename_characteraudit_characterowner_and_more.py +48 -0
  62. ledger/models/__init__.py +5 -11
  63. ledger/models/characteraudit.py +101 -109
  64. ledger/models/corporationaudit.py +94 -49
  65. ledger/models/general.py +105 -211
  66. ledger/models/helpers/update_manager.py +302 -0
  67. ledger/models/planetary.py +60 -205
  68. ledger/providers.py +101 -0
  69. ledger/static/ledger/css/{ledger.css → aa-ledger.css} +54 -28
  70. ledger/static/ledger/js/aa-ledger.js +124 -0
  71. ledger/static/ledger/js/charts.js +25 -1
  72. ledger/static/ledger/js/view-alliance-ledger.js +383 -0
  73. ledger/static/ledger/js/view-character-ledger.js +388 -0
  74. ledger/static/ledger/js/view-corporation-ledger.js +402 -0
  75. ledger/static/ledger/js/view-planetary.js +492 -0
  76. ledger/static/ledger/libs/amCharts/5.14.4/js/flow.js +2 -0
  77. ledger/static/ledger/libs/amCharts/5.14.4/js/index.js +2 -0
  78. ledger/static/ledger/libs/amCharts/5.14.4/js/percent.js +2 -0
  79. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Animated.js +2 -0
  80. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Dark.js +2 -0
  81. ledger/static/ledger/libs/amCharts/5.14.4/js/xy.js +2 -0
  82. ledger/static/ledger/libs/datatables/2.3.5/css/dataTables.bootstrap5.css +610 -0
  83. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.bootstrap5.js +122 -0
  84. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.js +14127 -0
  85. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
  86. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.dataTables.css +529 -0
  87. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
  88. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3090 -0
  89. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
  90. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
  91. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
  92. ledger/tasks.py +157 -141
  93. ledger/templates/ledger/base.html +59 -21
  94. ledger/templates/ledger/bundles/aa-ledger-css.html +3 -0
  95. ledger/templates/ledger/bundles/aa-ledger-js.html +3 -0
  96. ledger/templates/ledger/bundles/view-alliance-ledger-js.html +14 -0
  97. ledger/templates/ledger/bundles/view-character-ledger-js.html +15 -0
  98. ledger/templates/ledger/bundles/view-character-planetary-css.html +3 -0
  99. ledger/templates/ledger/bundles/view-character-planetary-js.html +4 -0
  100. ledger/templates/ledger/bundles/view-corporation-ledger-js.html +15 -0
  101. ledger/templates/ledger/partials/modal/confirm.html +0 -1
  102. ledger/templates/ledger/partials/modal/request-accept-delete-alliance.html +38 -0
  103. ledger/templates/ledger/partials/modal/request-accept-delete-character.html +38 -0
  104. ledger/templates/ledger/partials/modal/request-accept-delete-corporation.html +38 -0
  105. ledger/templates/ledger/partials/modal/request-accept-switch-notification.html +38 -0
  106. ledger/templates/ledger/partials/modal/request-view-alliance-details.html +26 -0
  107. ledger/templates/ledger/partials/modal/request-view-character-details.html +26 -0
  108. ledger/templates/ledger/partials/modal/request-view-corporation-details.html +26 -0
  109. ledger/templates/ledger/partials/modal/request-view-extractor.html +32 -0
  110. ledger/templates/ledger/partials/modal/request-view-factory.html +31 -0
  111. ledger/templates/ledger/partials/{menu → navigation}/administration.html +8 -0
  112. ledger/templates/ledger/partials/{menu → navigation}/navigation.html +2 -2
  113. ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance_corporations.html +3 -3
  114. ledger/templates/ledger/partials/view-alliance-administration/dashboard.html +81 -0
  115. ledger/templates/ledger/partials/view-alliance-ledger/alliance-billboard.html +25 -0
  116. ledger/templates/ledger/partials/view-alliance-ledger/alliance-ledger-details.html +21 -0
  117. ledger/templates/ledger/partials/view-alliance-ledger/alliance-table.html +24 -0
  118. ledger/templates/ledger/partials/view-alliance-ledger/information/daily.html +18 -0
  119. ledger/templates/ledger/partials/view-alliance-ledger/information/hourly.html +18 -0
  120. ledger/templates/ledger/partials/view-alliance-ledger/information/summary.html +19 -0
  121. ledger/templates/ledger/partials/{administration → view-character-administration}/character.html +1 -9
  122. ledger/templates/ledger/partials/{administration → view-character-administration}/dashboard.html +0 -34
  123. ledger/templates/ledger/partials/view-character-ledger/character-billboard.html +25 -0
  124. ledger/templates/ledger/partials/view-character-ledger/character-ledger-details.html +21 -0
  125. ledger/templates/ledger/partials/view-character-ledger/character-table.html +25 -0
  126. ledger/templates/ledger/partials/view-character-ledger/information/daily.html +18 -0
  127. ledger/templates/ledger/partials/view-character-ledger/information/hourly.html +18 -0
  128. ledger/templates/ledger/partials/view-character-ledger/information/summary.html +19 -0
  129. ledger/templates/ledger/partials/view-character-planetary/extractor-table.html +24 -0
  130. ledger/templates/ledger/partials/view-character-planetary/factory-table.html +24 -0
  131. ledger/templates/ledger/partials/view-character-planetary/planetary-table.html +22 -0
  132. ledger/templates/ledger/partials/view-character-planetary/storage-table.html +23 -0
  133. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation.html +5 -13
  134. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation_characters.html +1 -1
  135. ledger/templates/ledger/partials/view-corporation-administration/dashboard.html +81 -0
  136. ledger/templates/ledger/partials/view-corporation-ledger/corporation-billboard.html +25 -0
  137. ledger/templates/ledger/partials/view-corporation-ledger/corporation-ledger-details.html +21 -0
  138. ledger/templates/ledger/partials/view-corporation-ledger/corporation-table.html +26 -0
  139. ledger/templates/ledger/partials/view-corporation-ledger/information/daily.html +18 -0
  140. ledger/templates/ledger/partials/view-corporation-ledger/information/hourly.html +18 -0
  141. ledger/templates/ledger/partials/view-corporation-ledger/information/summary.html +19 -0
  142. ledger/templates/ledger/view-administration.html +62 -0
  143. ledger/templates/ledger/view-alliance-administration.html +49 -0
  144. ledger/templates/ledger/view-alliance-ledger.html +72 -0
  145. ledger/templates/ledger/view-alliance-overview.html +131 -0
  146. ledger/templates/ledger/view-character-administration.html +42 -0
  147. ledger/templates/ledger/view-character-ledger.html +73 -0
  148. ledger/templates/ledger/view-character-overview.html +135 -0
  149. ledger/templates/ledger/view-character-planetary-overview.html +135 -0
  150. ledger/templates/ledger/view-character-planetary.html +73 -0
  151. ledger/templates/ledger/view-corporation-administration.html +42 -0
  152. ledger/templates/ledger/view-corporation-ledger.html +73 -0
  153. ledger/templates/ledger/view-corporation-overview.html +131 -0
  154. ledger/templatetags/ledger.py +3 -5
  155. ledger/tests/__init__.py +187 -0
  156. ledger/tests/test_admin.py +164 -68
  157. ledger/tests/test_auth_hook.py +31 -13
  158. ledger/tests/test_decarators.py +14 -79
  159. ledger/tests/test_discord_installed.py +0 -1
  160. ledger/tests/test_helpers/test_ledger_data.py +19 -0
  161. ledger/tests/test_managers/test_character_audit_manager.py +111 -69
  162. ledger/tests/test_managers/test_character_journal_manager.py +48 -208
  163. ledger/tests/test_managers/test_character_mining_manager.py +37 -16
  164. ledger/tests/test_managers/test_corporation_division_manager.py +66 -28
  165. ledger/tests/test_managers/test_corporation_journal_manager.py +39 -42
  166. ledger/tests/test_managers/test_general_manager.py +78 -18
  167. ledger/tests/test_managers/test_planetary_manager.py +73 -32
  168. ledger/tests/test_models/test_characteraudit.py +58 -74
  169. ledger/tests/test_models/test_characterminingledger.py +20 -26
  170. ledger/tests/test_models/test_characterwalletjournal.py +10 -33
  171. ledger/tests/test_models/test_corporationaudit.py +41 -35
  172. ledger/tests/test_models/test_corporationwalletjournal.py +35 -32
  173. ledger/tests/test_models/test_general.py +44 -11
  174. ledger/tests/test_models/test_planetary.py +14 -80
  175. ledger/tests/test_templatetags.py +2 -7
  176. ledger/tests/test_views/corporation/test_add_corp.py +16 -35
  177. ledger/tests/test_views/corporation/test_delete_corporation.py +66 -42
  178. ledger/tests/test_views/test_access.py +512 -545
  179. ledger/tests/test_views/test_add_ally.py +57 -46
  180. ledger/tests/test_views/test_add_char.py +21 -33
  181. ledger/tests/test_views/test_delete_character.py +24 -21
  182. ledger/tests/testdata/README_ESI_STUB.md +430 -0
  183. ledger/tests/testdata/esi_stub_openapi.py +511 -0
  184. ledger/tests/testdata/integrations/__init__.py +0 -0
  185. ledger/tests/testdata/{load_eveuniverse.py → integrations/eveuniverse.py} +0 -1
  186. ledger/tests/testdata/integrations/planetary.py +13 -0
  187. ledger/tests/testdata/json/factory.json +281 -0
  188. ledger/tests/testdata/json/inactive.json +281 -0
  189. ledger/tests/testdata/json/pins.json +175 -272
  190. ledger/tests/testdata/json/route.json +95 -528
  191. ledger/tests/testdata/test_esi_stub.py +468 -0
  192. ledger/tests/testdata/utils.py +601 -0
  193. ledger/thirdparty/charlink_hook.py +60 -30
  194. ledger/urls.py +0 -135
  195. ledger/views/alliance/add_ally.py +2 -4
  196. ledger/views/alliance/alliance_ledger.py +64 -147
  197. ledger/views/character/add_char.py +8 -10
  198. ledger/views/character/character_ledger.py +60 -126
  199. ledger/views/character/planetary.py +5 -98
  200. ledger/views/corporation/add_corp.py +10 -12
  201. ledger/views/corporation/corporation_ledger.py +65 -327
  202. ledger/views/index.py +92 -30
  203. aa_ledger-1.0.4.dist-info/RECORD +0 -236
  204. ledger/api/api_helper/planetary_helper.py +0 -107
  205. ledger/api/ledger/__init__.py +0 -7
  206. ledger/api/ledger/planetary.py +0 -231
  207. ledger/helpers/alliance.py +0 -317
  208. ledger/helpers/character.py +0 -251
  209. ledger/helpers/core.py +0 -665
  210. ledger/helpers/corporation.py +0 -427
  211. ledger/helpers/data_exporter.py +0 -452
  212. ledger/static/ledger/js/planetary-confirm.js +0 -66
  213. ledger/static/ledger/js/planetary.js +0 -143
  214. ledger/templates/ledger/admin.html +0 -43
  215. ledger/templates/ledger/allyledger/admin/alliance_administration.html +0 -46
  216. ledger/templates/ledger/allyledger/admin/alliance_overview.html +0 -108
  217. ledger/templates/ledger/allyledger/alliance_ledger.html +0 -86
  218. ledger/templates/ledger/bundles/character-ledger-bundles.html +0 -66
  219. ledger/templates/ledger/bundles/corporation-ledger-bundles.html +0 -75
  220. ledger/templates/ledger/bundles/ledger-bundles.html +0 -23
  221. ledger/templates/ledger/bundles/ledger-css.html +0 -3
  222. ledger/templates/ledger/bundles/planetary-bundles.html +0 -50
  223. ledger/templates/ledger/bundles/table-css.html +0 -3
  224. ledger/templates/ledger/charledger/admin/character_administration.html +0 -39
  225. ledger/templates/ledger/charledger/admin/character_overview.html +0 -106
  226. ledger/templates/ledger/charledger/character_ledger.html +0 -94
  227. ledger/templates/ledger/charledger/planetary/admin/planetary_overview.html +0 -123
  228. ledger/templates/ledger/charledger/planetary/planetary_ledger.html +0 -54
  229. ledger/templates/ledger/corpledger/admin/corporation_administration.html +0 -39
  230. ledger/templates/ledger/corpledger/admin/corporation_overview.html +0 -108
  231. ledger/templates/ledger/corpledger/corporation_ledger.html +0 -129
  232. ledger/templates/ledger/data-export.html +0 -78
  233. ledger/templates/ledger/error.html +0 -31
  234. ledger/templates/ledger/partials/form/error-message.html +0 -1
  235. ledger/templates/ledger/partials/information/daily.html +0 -56
  236. ledger/templates/ledger/partials/information/day.html +0 -48
  237. ledger/templates/ledger/partials/information/error.html +0 -8
  238. ledger/templates/ledger/partials/information/hourly.html +0 -53
  239. ledger/templates/ledger/partials/information/summary.html +0 -88
  240. ledger/templates/ledger/partials/information/view_character_content.html +0 -35
  241. ledger/templates/ledger/partials/modal/switchalarm_confirm.html +0 -39
  242. ledger/templates/ledger/partials/modal/view_extractor.html +0 -48
  243. ledger/templates/ledger/partials/modal/view_factory.html +0 -123
  244. ledger/templates/ledger/partials/table/char-ledger.html +0 -85
  245. ledger/templates/ledger/partials/table/corp-ledger.html +0 -66
  246. ledger/templates/ledger/partials/table/planetary.html +0 -18
  247. ledger/templates/ledger/partials/thirdparty/billboard.html +0 -22
  248. ledger/templates/ledger/partials/view/card.html +0 -160
  249. ledger/templates/ledger/permission.html +0 -2
  250. ledger/tests/test_helpers/test_billboard.py +0 -11
  251. ledger/tests/test_helpers/test_data_exporter.py +0 -207
  252. ledger/tests/test_tasks.py +0 -282
  253. ledger/tests/test_view_helpers/test_core.py +0 -47
  254. ledger/tests/test_views/corporation/test_corporation.py +0 -267
  255. ledger/tests/test_views/test_planetary.py +0 -137
  256. ledger/tests/testdata/esi_stub.py +0 -109
  257. ledger/tests/testdata/esi_stub_migration.py +0 -80
  258. ledger/tests/testdata/generate_characteraudit.py +0 -106
  259. ledger/tests/testdata/generate_corporationaudit.py +0 -74
  260. ledger/tests/testdata/generate_events.py +0 -31
  261. ledger/tests/testdata/generate_miningledger.py +0 -13
  262. ledger/tests/testdata/generate_planets.py +0 -48
  263. ledger/tests/testdata/generate_walletjournal.py +0 -42
  264. ledger/tests/testdata/json/czarno-pins.json +0 -240
  265. ledger/tests/testdata/json/czarno-routes.json +0 -165
  266. ledger/tests/testdata/json/pins2.json +0 -538
  267. {aa_ledger-1.0.4.dist-info → aa_ledger-2.0.0.dist-info}/WHEEL +0 -0
  268. {aa_ledger-1.0.4.dist-info → aa_ledger-2.0.0.dist-info}/licenses/LICENSE +0 -0
  269. /ledger/{tests/test_view_helpers → api/helpers}/__init__.py +0 -0
  270. /ledger/templates/ledger/bundles/{ally-administration-bundles.html → view-alliance-administration-js.html} +0 -0
  271. /ledger/templates/ledger/bundles/{char-administration-bundles.html → view-character-administration-js.html} +0 -0
  272. /ledger/templates/ledger/bundles/{corp-administration-bundles.html → view-corporation-administration-js.html} +0 -0
  273. /ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance.html +0 -0
  274. /ledger/tests/testdata/{esi.json → esi_test_data.json} +0 -0
  275. /ledger/tests/testdata/{allianceauth.json → integrations/allianceauth.json} +0 -0
  276. /ledger/tests/testdata/{load_allianceauth.py → integrations/allianceauth.py} +0 -0
  277. /ledger/tests/testdata/{eveentity.json → integrations/eveentity.json} +0 -0
  278. /ledger/tests/testdata/{load_eveentity.py → integrations/eveentity.py} +0 -0
  279. /ledger/tests/testdata/{eveuniverse.json → integrations/eveuniverse.json} +0 -0
  280. /ledger/tests/testdata/{planetary.json → integrations/planetary.json} +0 -0
ledger/models/general.py CHANGED
@@ -4,18 +4,13 @@ General Model
4
4
 
5
5
  # Standard Library
6
6
  import datetime
7
- from collections.abc import Callable
8
7
  from dataclasses import dataclass
9
8
  from typing import Any, NamedTuple
10
9
 
11
- # Third Party
12
- from aiopenapi3.errors import ContentTypeError, HTTPClientError, HTTPServerError
13
-
14
10
  # Django
15
11
  from django.core.validators import MinValueValidator
16
12
  from django.db import models
17
13
  from django.utils import timezone
18
- from django.utils.safestring import mark_safe
19
14
  from django.utils.translation import gettext_lazy as _
20
15
 
21
16
  # Alliance Auth
@@ -25,17 +20,18 @@ from allianceauth.eveonline.models import (
25
20
  EveCorporationInfo,
26
21
  )
27
22
  from allianceauth.services.hooks import get_extension_logger
28
- from esi.errors import TokenError
29
- from esi.exceptions import HTTPNotModified
30
-
31
- # Alliance Auth (External Libs)
32
- from app_utils.logging import LoggerAddTag
33
23
 
34
24
  # AA Ledger
35
25
  from ledger import __title__, app_settings
26
+ from ledger.helpers.eveonline import (
27
+ get_alliance_logo_url,
28
+ get_character_portrait_url,
29
+ get_corporation_logo_url,
30
+ )
36
31
  from ledger.managers.general_manager import EveEntityManager
32
+ from ledger.providers import AppLogger
37
33
 
38
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
34
+ logger = AppLogger(get_extension_logger(__name__), __title__)
39
35
 
40
36
  # Permission Manager
41
37
 
@@ -57,6 +53,11 @@ class General(models.Model):
57
53
  class EveEntity(models.Model):
58
54
  """An Eve entity like a corporation or a character"""
59
55
 
56
+ objects: EveEntityManager = EveEntityManager()
57
+
58
+ class Meta:
59
+ default_permissions = ()
60
+
60
61
  CATEGORY_ALLIANCE = "alliance"
61
62
  CATEGORY_CHARACTER = "character"
62
63
  CATEGORY_CORPORATION = "corporation"
@@ -92,8 +93,6 @@ class EveEntity(models.Model):
92
93
  )
93
94
  last_update = models.DateTimeField(auto_now=True)
94
95
 
95
- objects = EveEntityManager()
96
-
97
96
  def __str__(self) -> str:
98
97
  return str(self.name)
99
98
 
@@ -118,6 +117,41 @@ class EveEntity(models.Model):
118
117
  """Return True if entity is a character, else False."""
119
118
  return self.category == self.CATEGORY_CHARACTER
120
119
 
120
+ def get_portrait(self, size=64, as_html=False) -> str:
121
+ """
122
+ Get the portrait URL for this entity.
123
+
124
+ Args:
125
+ size (int, optional): The size of the portrait.
126
+ as_html (bool, optional): Whether to return the portrait as an HTML img tag.
127
+ Returns:
128
+ str: The URL of the portrait or an HTML img tag.
129
+ """
130
+ if self.category == self.CATEGORY_ALLIANCE:
131
+ return get_alliance_logo_url(
132
+ alliance_id=self.eve_id,
133
+ size=size,
134
+ alliance_name=self.name,
135
+ as_html=as_html,
136
+ )
137
+
138
+ if self.category == self.CATEGORY_CORPORATION:
139
+ return get_corporation_logo_url(
140
+ corporation_id=self.eve_id,
141
+ size=size,
142
+ corporation_name=self.name,
143
+ as_html=as_html,
144
+ )
145
+
146
+ if self.category == self.CATEGORY_CHARACTER:
147
+ return get_character_portrait_url(
148
+ character_id=self.eve_id,
149
+ size=size,
150
+ character_name=self.name,
151
+ as_html=as_html,
152
+ )
153
+ return ""
154
+
121
155
  def icon_url(self, size=128) -> str:
122
156
  """Url to an icon image for this organization."""
123
157
  if self.category == self.CATEGORY_ALLIANCE:
@@ -136,9 +170,6 @@ class EveEntity(models.Model):
136
170
  def needs_update(self):
137
171
  return self.last_update + datetime.timedelta(days=7) < timezone.now()
138
172
 
139
- class Meta:
140
- default_permissions = ()
141
-
142
173
 
143
174
  class UpdateSectionResult(NamedTuple):
144
175
  """A result of an attempted section update."""
@@ -165,200 +196,12 @@ class _NeedsUpdate:
165
196
  return self.section_map.get(section, False)
166
197
 
167
198
 
168
- class AuditBase(models.Model):
169
- """A base model for audit models."""
199
+ class UpdateStatusBaseModel(models.Model):
200
+ """A Model to track the status of the last update."""
170
201
 
171
202
  class Meta:
172
203
  abstract = True
173
-
174
- class UpdateStatus(models.TextChoices):
175
- DISABLED = "disabled", _("disabled")
176
- TOKEN_ERROR = "token_error", _("token error")
177
- ERROR = "error", _("error")
178
- OK = "ok", _("ok")
179
- INCOMPLETE = "incomplete", _("incomplete")
180
- IN_PROGRESS = "in_progress", _("in progress")
181
-
182
- def bootstrap_icon(self) -> str:
183
- """Return bootstrap corresponding icon class."""
184
- update_map = {
185
- status: mark_safe(
186
- f"<span class='{self.bootstrap_text_style_class()}' data-tooltip-toggle='ledger-tooltip' title='{self.description()}'>⬤</span>"
187
- )
188
- for status in [
189
- self.DISABLED,
190
- self.TOKEN_ERROR,
191
- self.ERROR,
192
- self.INCOMPLETE,
193
- self.IN_PROGRESS,
194
- self.OK,
195
- ]
196
- }
197
- return update_map.get(self, "")
198
-
199
- def bootstrap_text_style_class(self) -> str:
200
- """Return bootstrap corresponding bootstrap text style class."""
201
- update_map = {
202
- self.DISABLED: "text-muted",
203
- self.TOKEN_ERROR: "text-warning",
204
- self.INCOMPLETE: "text-warning",
205
- self.IN_PROGRESS: "text-info",
206
- self.ERROR: "text-danger",
207
- self.OK: "text-success",
208
- }
209
- return update_map.get(self, "")
210
-
211
- def description(self) -> str:
212
- """Return description for an enum object."""
213
- update_map = {
214
- self.DISABLED: _("Update is disabled"),
215
- self.TOKEN_ERROR: _("One section has a token error during update"),
216
- self.INCOMPLETE: _("One or more sections have not been updated"),
217
- self.IN_PROGRESS: _("Update is in progress"),
218
- self.ERROR: _("An error occurred during update"),
219
- self.OK: _("Updates completed successfully"),
220
- }
221
- return update_map.get(self, "")
222
-
223
- def update_section_if_changed(
224
- self,
225
- section: models.TextChoices,
226
- fetch_func: Callable,
227
- force_refresh: bool = False,
228
- ):
229
- """Update the status of a specific section if it has changed."""
230
- section = self.UpdateSection(section)
231
- try:
232
- data = fetch_func(audit=self, force_refresh=force_refresh)
233
- logger.debug("%s: Update has changed, section: %s", self, section.label)
234
- except HTTPServerError as exc:
235
- logger.debug("%s: Update has an HTTP internal server error: %s", self, exc)
236
- return UpdateSectionResult(is_changed=False, is_updated=False)
237
- except HTTPClientError as exc:
238
- error_message = f"{type(exc).__name__}: {str(exc)}"
239
- # TODO ADD DISCORD/AUTH NOTIFICATION?
240
- logger.error(
241
- "%s: %s: Update has Client Error: %s %s",
242
- self,
243
- section.label,
244
- error_message,
245
- exc.status_code,
246
- )
247
- return UpdateSectionResult(
248
- is_changed=False,
249
- is_updated=False,
250
- has_token_error=True,
251
- error_message=error_message,
252
- )
253
- except HTTPNotModified:
254
- logger.debug("%s: Update has not changed, section: %s", self, section.label)
255
- return UpdateSectionResult(is_changed=False, is_updated=False)
256
- except (OSError, ContentTypeError) as exc:
257
- logger.info(
258
- "%s Update has a %s error, section: %s: %s",
259
- self,
260
- type(exc).__name__,
261
- section.label,
262
- exc,
263
- )
264
- return UpdateSectionResult(is_changed=False, is_updated=False)
265
- return UpdateSectionResult(
266
- is_changed=True,
267
- is_updated=True,
268
- data=data,
269
- )
270
-
271
- def reset_has_token_error(self) -> None:
272
- """Reset the has_token_error flag for this character."""
273
- if self.get_status == self.UpdateStatus.TOKEN_ERROR:
274
- self.update_status.filter(
275
- has_token_error=True,
276
- ).update(
277
- has_token_error=False,
278
- )
279
- return True
280
- return False
281
-
282
- def reset_update_status(self, section):
283
- """Reset the status of a given update section and return it."""
284
- update_status_obj = self.update_status.get_or_create(
285
- section=section,
286
- )[0]
287
- update_status_obj.reset()
288
- return update_status_obj
289
-
290
- def perform_update_status(
291
- self, section, method: Callable, *args, **kwargs
292
- ) -> UpdateSectionResult:
293
- """Perform update status."""
294
- try:
295
- result = method(*args, **kwargs)
296
- except Exception as exc:
297
- error_message = f"{type(exc).__name__}: {str(exc)}"
298
- is_token_error = isinstance(exc, (TokenError))
299
- logger.error(
300
- "%s: %s: Error during update status: %s",
301
- self,
302
- section.label,
303
- error_message,
304
- exc_info=not is_token_error, # do not log token errors
305
- )
306
-
307
- # Update the status using the related manager name
308
- self.update_status.update_or_create(
309
- section=section,
310
- defaults={
311
- "is_success": False,
312
- "error_message": error_message,
313
- "has_token_error": is_token_error,
314
- "last_update_at": timezone.now(),
315
- },
316
- )
317
- raise exc
318
- return result
319
-
320
- def update_section_log(
321
- self,
322
- section: models.TextChoices,
323
- result: UpdateSectionResult,
324
- ) -> None:
325
- """Update the status of a specific section."""
326
- error_message = result.error_message if result.error_message else ""
327
- is_success = not result.has_token_error
328
- defaults = {
329
- "is_success": is_success,
330
- "error_message": error_message,
331
- "has_token_error": result.has_token_error,
332
- "last_run_finished_at": timezone.now(),
333
- }
334
- obj: UpdateStatus = self.update_status.update_or_create(
335
- section=section,
336
- defaults=defaults,
337
- )[0]
338
- if result.is_updated:
339
- obj.last_update_at = obj.last_run_at
340
- obj.last_update_finished_at = timezone.now()
341
- obj.save()
342
- status = "successfully" if is_success else "with errors"
343
- logger.info("%s: %s Update run completed %s", self, section.label, status)
344
-
345
- def calc_update_needed(self) -> _NeedsUpdate:
346
- """Calculate if an update is needed."""
347
- sections_needs_update = {
348
- section: True for section in self.UpdateSection.get_sections()
349
- }
350
- existing_sections: models.QuerySet[UpdateStatus] = self.update_status.all()
351
- needs_update = {
352
- obj.section: obj.need_update()
353
- for obj in existing_sections
354
- if obj.section in sections_needs_update
355
- }
356
- sections_needs_update.update(needs_update)
357
- return _NeedsUpdate(section_map=sections_needs_update)
358
-
359
-
360
- class UpdateStatus(models.Model):
361
- """A Model to track the status of the last update."""
204
+ default_permissions = ()
362
205
 
363
206
  is_success = models.BooleanField(default=None, null=True, db_index=True)
364
207
  error_message = models.TextField()
@@ -389,10 +232,6 @@ class UpdateStatus(models.Model):
389
232
  help_text="Last update has been successful finished at this time",
390
233
  )
391
234
 
392
- class Meta:
393
- abstract = True
394
- default_permissions = ()
395
-
396
235
  def need_update(self) -> bool:
397
236
  """Check if the update is needed."""
398
237
  if not self.is_success or not self.last_update_finished_at:
@@ -424,3 +263,58 @@ class UpdateStatus(models.Model):
424
263
  self.last_run_at = timezone.now()
425
264
  self.last_run_finished_at = None
426
265
  self.save()
266
+
267
+
268
+ class WalletJournalEntry(models.Model):
269
+ amount = models.DecimalField(
270
+ max_digits=20, decimal_places=2, null=True, default=None
271
+ )
272
+ balance = models.DecimalField(
273
+ max_digits=20, decimal_places=2, null=True, default=None
274
+ )
275
+ context_id = models.BigIntegerField(null=True, default=None)
276
+
277
+ class ContextType(models.TextChoices):
278
+ STRUCTURE_ID = "structure_id"
279
+ STATION_ID = "station_id"
280
+ MARKET_TRANSACTION_ID = "market_transaction_id"
281
+ CHARACTER_ID = "character_id"
282
+ CORPORATION_ID = "corporation_id"
283
+ ALLIANCE_ID = "alliance_id"
284
+ EVE_SYSTEM = "eve_system"
285
+ INDUSTRY_JOB_ID = "industry_job_id"
286
+ CONTRACT_ID = "contract_id"
287
+ PLANET_ID = "planet_id"
288
+ SYSTEM_ID = "system_id"
289
+ TYPE_ID = "type_id"
290
+
291
+ context_id_type = models.CharField(
292
+ max_length=30, choices=ContextType.choices, null=True, default=None
293
+ )
294
+ date = models.DateTimeField()
295
+ description = models.CharField(max_length=500)
296
+ first_party = models.ForeignKey(
297
+ EveEntity,
298
+ on_delete=models.SET_DEFAULT,
299
+ default=None,
300
+ null=True,
301
+ blank=True,
302
+ related_name="+",
303
+ )
304
+ entry_id = models.BigIntegerField()
305
+ reason = models.CharField(max_length=500, null=True, default=None)
306
+ ref_type = models.CharField(max_length=72)
307
+ second_party = models.ForeignKey(
308
+ EveEntity,
309
+ on_delete=models.SET_DEFAULT,
310
+ default=None,
311
+ null=True,
312
+ blank=True,
313
+ related_name="+",
314
+ )
315
+ tax = models.DecimalField(max_digits=20, decimal_places=2, null=True, default=None)
316
+ tax_receiver_id = models.IntegerField(null=True, default=None)
317
+
318
+ class Meta:
319
+ abstract = True
320
+ default_permissions = ()
@@ -0,0 +1,302 @@
1
+ # Standard Library
2
+ from typing import TYPE_CHECKING, Union
3
+
4
+ # Django
5
+ from django.db import models
6
+ from django.utils import timezone
7
+ from django.utils.safestring import mark_safe
8
+ from django.utils.translation import gettext_lazy as _
9
+
10
+ # Alliance Auth
11
+ from allianceauth.services.hooks import get_extension_logger
12
+ from esi.exceptions import HTTPClientError, HTTPNotModified, HTTPServerError
13
+
14
+ # AA Ledger
15
+ from ledger import __title__
16
+ from ledger.models.general import (
17
+ UpdateSectionResult,
18
+ _NeedsUpdate,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ # AA Ledger
23
+ from ledger.models.characteraudit import CharacterOwner, CharacterUpdateStatus
24
+ from ledger.models.corporationaudit import CorporationOwner, CorporationUpdateStatus
25
+
26
+ # AA Ledger
27
+ from ledger.providers import AppLogger
28
+
29
+ logger = AppLogger(get_extension_logger(__name__), __title__)
30
+
31
+
32
+ class UpdateStatus(models.TextChoices):
33
+ DISABLED = "disabled", _("Disabled")
34
+ TOKEN_ERROR = "token_error", _("Token Error")
35
+ ERROR = "error", _("Error")
36
+ OK = "ok", _("OK")
37
+ INCOMPLETE = "incomplete", _("Incomplete")
38
+ IN_PROGRESS = "in_progress", _("In Progress")
39
+
40
+ def bootstrap_icon(self) -> str:
41
+ """Return bootstrap corresponding icon class."""
42
+ update_map = {
43
+ status: mark_safe(
44
+ f"<span class='{self.bootstrap_text_style_class()}' data-tooltip-toggle='ledger-tooltip' title='{self.description()}'>⬤</span>"
45
+ )
46
+ for status in [
47
+ self.DISABLED,
48
+ self.TOKEN_ERROR,
49
+ self.ERROR,
50
+ self.INCOMPLETE,
51
+ self.IN_PROGRESS,
52
+ self.OK,
53
+ ]
54
+ }
55
+ return update_map.get(self, "")
56
+
57
+ def bootstrap_text_style_class(self) -> str:
58
+ """Return bootstrap corresponding bootstrap text style class."""
59
+ update_map = {
60
+ self.DISABLED: "text-muted",
61
+ self.TOKEN_ERROR: "text-warning",
62
+ self.INCOMPLETE: "text-warning",
63
+ self.IN_PROGRESS: "text-info",
64
+ self.ERROR: "text-danger",
65
+ self.OK: "text-success",
66
+ }
67
+ return update_map.get(self, "")
68
+
69
+ def description(self) -> str:
70
+ """Return description for an enum object."""
71
+ update_map = {
72
+ self.DISABLED: _("Update is disabled"),
73
+ self.TOKEN_ERROR: _("One section has a token error during update"),
74
+ self.INCOMPLETE: _("One or more sections have not been updated"),
75
+ self.IN_PROGRESS: _("Update is in progress"),
76
+ self.ERROR: _("An error occurred during update"),
77
+ self.OK: _("Updates completed successfully"),
78
+ }
79
+ return update_map.get(self, "")
80
+
81
+
82
+ class CharacterUpdateSection(models.TextChoices):
83
+ WALLET_JOURNAL = "wallet_journal", _("Wallet Journal")
84
+ MINING_LEDGER = "mining_ledger", _("Mining Ledger")
85
+ PLANETS = "planets", _("Planets")
86
+ PLANETS_DETAILS = "planets_details", _("Planets Details")
87
+
88
+ @classmethod
89
+ def get_sections(cls) -> list[str]:
90
+ """Return list of section values."""
91
+ return [choice.value for choice in cls]
92
+
93
+ @property
94
+ def method_name(self) -> str:
95
+ """Return method name for this section."""
96
+ return f"update_{self.value}"
97
+
98
+
99
+ class CorporationUpdateSection(models.TextChoices):
100
+ WALLET_DIVISION_NAMES = "wallet_division_names", _("Divisions Names")
101
+ WALLET_DIVISION = "wallet_division", _("Divisions")
102
+ WALLET_JOURNAL = "wallet_journal", _("Wallet Journal")
103
+
104
+ @classmethod
105
+ def get_sections(cls) -> list[str]:
106
+ """Return list of section values."""
107
+ return [choice.value for choice in cls]
108
+
109
+ @property
110
+ def method_name(self) -> str:
111
+ """Return method name for this section."""
112
+ return f"update_{self.value}"
113
+
114
+
115
+ class UpdateManager:
116
+ """Manager class to handle update operations for CharacterOwner and CorporationOwner.
117
+ This class provides methods to manage and track update statuses for both character and corporation owners.
118
+
119
+ Args:
120
+ owner (CorporationOwner | CharacterOwner): The owner model (corporation or character)
121
+ update_section (CorporationUpdateSection | CharacterUpdateSection): The update section class (CorporationUpdateSection or CharacterUpdateSection)
122
+ update_status (CorporationUpdateStatus | CharacterUpdateStatus): The update status class (CorporationUpdateStatus or CharacterUpdateStatus)
123
+ """
124
+
125
+ def __init__(
126
+ self,
127
+ owner: Union["CorporationOwner", "CharacterOwner"],
128
+ update_section: CorporationUpdateSection | CharacterUpdateSection,
129
+ update_status: Union["CorporationUpdateStatus", "CharacterUpdateStatus"],
130
+ ):
131
+ self.owner = owner
132
+ self.update_section = update_section
133
+ self.update_status = update_status
134
+
135
+ # Shared methods
136
+ def calc_update_needed(self) -> _NeedsUpdate:
137
+ """
138
+ Calculate which sections need an update and save the results in a _NeedsUpdate object.
139
+
140
+ Returns:
141
+ _NeedsUpdate: An object containing a mapping of sections to their update needs.
142
+ """
143
+ sections_needs_update = {
144
+ section: True for section in self.update_section.get_sections()
145
+ }
146
+ existing_sections = self.update_status.objects.filter(owner=self.owner)
147
+ needs_update = {
148
+ obj.section: obj.need_update()
149
+ for obj in existing_sections
150
+ if obj.section in sections_needs_update
151
+ }
152
+ sections_needs_update.update(needs_update)
153
+ return _NeedsUpdate(section_map=sections_needs_update)
154
+
155
+ def reset_update_status(self, section: models.TextChoices):
156
+ """
157
+ Create or Reset the update status for a specific section.
158
+
159
+ Args:
160
+ section (models.TextChoices): The section to reset.
161
+ Returns:
162
+ UpdateStatus (Object): The reset update status object for the Owner Model.
163
+ """
164
+ update_status_obj = self.update_status.objects.get_or_create(
165
+ owner=self.owner,
166
+ section=section,
167
+ )[0]
168
+ update_status_obj.reset()
169
+ return update_status_obj
170
+
171
+ def reset_has_token_error(self) -> None:
172
+ """
173
+ Reset has_token_error for all sections.
174
+
175
+ Returns:
176
+ None
177
+ """
178
+ self.update_status.objects.filter(
179
+ has_token_error=True,
180
+ ).update(
181
+ has_token_error=False,
182
+ )
183
+
184
+ def update_section_if_changed(
185
+ self, section: models.TextChoices, fetch_func, force_refresh: bool = False
186
+ ):
187
+ """
188
+ Handle updating a specific section if there are changes.
189
+
190
+ Args:
191
+ section (models.TextChoices): The section to update.
192
+ fetch_func (Callable): The function to fetch the data for the section.
193
+ force_refresh (bool): Whether to force a refresh of the data.
194
+ Returns:
195
+ UpdateSectionResult: The result of the update operation.
196
+ Raises:
197
+ HTTPClientError: If there is a client error during the fetch.
198
+ HTTPServerError: If there is a server error during the fetch.
199
+ HTTPNotModified: If the data has not been modified.
200
+ """
201
+ section = self.update_section(section)
202
+ try:
203
+ data = fetch_func(owner=self.owner, force_refresh=force_refresh)
204
+ logger.debug(
205
+ "%s: Update has changed, section: %s", self.owner, section.label
206
+ )
207
+ except HTTPNotModified:
208
+ logger.debug(
209
+ "%s: Update has not changed, section: %s", self.owner, section.label
210
+ )
211
+ return UpdateSectionResult(is_changed=False, is_updated=False)
212
+ except HTTPClientError as exc:
213
+ error_message = f"{type(exc).__name__}: {str(exc)}"
214
+ logger.error(
215
+ "%s: %s: Update has Client Error: %s %s",
216
+ self.owner,
217
+ section.label,
218
+ error_message,
219
+ exc.status_code,
220
+ )
221
+ return UpdateSectionResult(
222
+ is_changed=False,
223
+ is_updated=False,
224
+ has_token_error=True,
225
+ error_message=error_message,
226
+ )
227
+ return UpdateSectionResult(
228
+ is_changed=True,
229
+ is_updated=True,
230
+ data=data,
231
+ )
232
+
233
+ def update_section_log(
234
+ self, section: models.TextChoices, result: UpdateSectionResult
235
+ ) -> None:
236
+ """
237
+ Update the status of a specific section.
238
+ Args:
239
+ section (models.TextChoices): The section to update.
240
+ result (UpdateSectionResult): The result of the update operation.
241
+ Returns:
242
+ None
243
+ """
244
+ error_message = result.error_message if result.error_message else ""
245
+ is_success = not result.has_token_error
246
+ defaults = {
247
+ "is_success": is_success,
248
+ "error_message": error_message,
249
+ "has_token_error": result.has_token_error,
250
+ "last_run_finished_at": timezone.now(),
251
+ }
252
+ obj = self.update_status.objects.update_or_create(
253
+ owner=self.owner,
254
+ section=section,
255
+ defaults=defaults,
256
+ )[0]
257
+ if result.is_updated:
258
+ obj.last_update_at = obj.last_run_at
259
+ obj.last_update_finished_at = timezone.now()
260
+ obj.save()
261
+ status = "successfully" if is_success else "with errors"
262
+ logger.info("%s: %s Update run completed %s", self.owner, section.label, status)
263
+
264
+ def perform_update_status(
265
+ self, section: models.TextChoices, method, *args, **kwargs
266
+ ):
267
+ """
268
+ Perform update status.
269
+ Args:
270
+ section (models.TextChoices): The section to update.
271
+ method (Callable): The method to perform the update.
272
+ *args: Positional arguments for the method.
273
+ **kwargs: Keyword arguments for the method.
274
+ Returns:
275
+ Any: The result of the method call.
276
+ Raises:
277
+ Exception: Reraises any exception encountered during the method call.
278
+ """
279
+ try:
280
+ result = method(*args, **kwargs)
281
+ except HTTPServerError as exc:
282
+ raise exc
283
+ except Exception as exc:
284
+ error_message = f"{type(exc).__name__}: {str(exc)}"
285
+ logger.error(
286
+ "%s: %s: Error during update status: %s",
287
+ self.owner,
288
+ section.label,
289
+ error_message,
290
+ )
291
+ self.update_status.objects.update_or_create(
292
+ owner=self.owner,
293
+ section=section,
294
+ defaults={
295
+ "is_success": False,
296
+ "error_message": error_message,
297
+ "has_token_error": False,
298
+ "last_update_at": timezone.now(),
299
+ },
300
+ )
301
+ raise exc
302
+ return result