aa-ledger 1.0.3__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.3.dist-info → aa_ledger-2.0.0.dist-info}/METADATA +6 -6
  2. aa_ledger-2.0.0.dist-info/RECORD +267 -0
  3. {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/WHEEL +1 -1
  4. ledger/__init__.py +2 -2
  5. ledger/admin.py +23 -18
  6. ledger/api/__init__.py +23 -7
  7. ledger/api/{ledger/admin.py → admin.py} +25 -31
  8. ledger/api/alliance.py +755 -0
  9. ledger/api/character.py +786 -0
  10. ledger/api/corporation.py +1141 -0
  11. ledger/api/{helpers.py → helpers/core.py} +33 -33
  12. ledger/api/helpers/icons.py +372 -0
  13. ledger/api/helpers/planetary_helper.py +354 -0
  14. ledger/api/planetary.py +354 -0
  15. ledger/api/schema.py +240 -15
  16. ledger/app_settings.py +18 -26
  17. ledger/auth_hooks.py +2 -2
  18. ledger/constants.py +50 -177
  19. ledger/decorators.py +2 -46
  20. ledger/forms.py +133 -39
  21. ledger/helpers/billboard.py +194 -144
  22. ledger/helpers/cache.py +105 -0
  23. ledger/helpers/discord.py +2 -4
  24. ledger/helpers/eveonline.py +160 -0
  25. ledger/helpers/ledger_data.py +23 -0
  26. ledger/helpers/ref_type.py +53 -78
  27. ledger/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  28. ledger/locale/cs_CZ/LC_MESSAGES/django.po +349 -193
  29. ledger/locale/de/LC_MESSAGES/django.mo +0 -0
  30. ledger/locale/de/LC_MESSAGES/django.po +528 -379
  31. ledger/locale/django.pot +717 -553
  32. ledger/locale/es/LC_MESSAGES/django.mo +0 -0
  33. ledger/locale/es/LC_MESSAGES/django.po +349 -194
  34. ledger/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  35. ledger/locale/fr_FR/LC_MESSAGES/django.po +349 -193
  36. ledger/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  37. ledger/locale/it_IT/LC_MESSAGES/django.po +349 -193
  38. ledger/locale/ja/LC_MESSAGES/django.mo +0 -0
  39. ledger/locale/ja/LC_MESSAGES/django.po +348 -193
  40. ledger/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  41. ledger/locale/ko_KR/LC_MESSAGES/django.po +349 -193
  42. ledger/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  43. ledger/locale/nl_NL/LC_MESSAGES/django.po +349 -193
  44. ledger/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  45. ledger/locale/pl_PL/LC_MESSAGES/django.po +350 -193
  46. ledger/locale/ru/LC_MESSAGES/django.mo +0 -0
  47. ledger/locale/ru/LC_MESSAGES/django.po +348 -193
  48. ledger/locale/sk/LC_MESSAGES/django.mo +0 -0
  49. ledger/locale/sk/LC_MESSAGES/django.po +348 -193
  50. ledger/locale/uk/LC_MESSAGES/django.mo +0 -0
  51. ledger/locale/uk/LC_MESSAGES/django.po +348 -193
  52. ledger/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  53. ledger/locale/zh_Hans/LC_MESSAGES/django.po +348 -193
  54. ledger/managers/character_audit_manager.py +28 -20
  55. ledger/managers/character_journal_manager.py +187 -358
  56. ledger/managers/character_mining_manager.py +64 -30
  57. ledger/managers/character_planetary_manager.py +185 -138
  58. ledger/managers/corporation_audit_manager.py +36 -27
  59. ledger/managers/corporation_journal_manager.py +94 -57
  60. ledger/managers/general_manager.py +12 -8
  61. ledger/migrations/0018_remove_characterplanet_ledger_char_planet__58a5b6_idx_and_more.py +44 -0
  62. ledger/migrations/0019_rename_characteraudit_characterowner_and_more.py +48 -0
  63. ledger/models/__init__.py +5 -11
  64. ledger/models/characteraudit.py +101 -109
  65. ledger/models/corporationaudit.py +94 -49
  66. ledger/models/general.py +105 -211
  67. ledger/models/helpers/update_manager.py +302 -0
  68. ledger/models/planetary.py +60 -205
  69. ledger/providers.py +101 -0
  70. ledger/static/ledger/css/{ledger.css → aa-ledger.css} +54 -28
  71. ledger/static/ledger/js/aa-ledger.js +124 -0
  72. ledger/static/ledger/js/charts.js +25 -1
  73. ledger/static/ledger/js/view-alliance-ledger.js +383 -0
  74. ledger/static/ledger/js/view-character-ledger.js +388 -0
  75. ledger/static/ledger/js/view-corporation-ledger.js +402 -0
  76. ledger/static/ledger/js/view-planetary.js +492 -0
  77. ledger/static/ledger/libs/amCharts/5.14.4/js/flow.js +2 -0
  78. ledger/static/ledger/libs/amCharts/5.14.4/js/index.js +2 -0
  79. ledger/static/ledger/libs/amCharts/5.14.4/js/percent.js +2 -0
  80. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Animated.js +2 -0
  81. ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Dark.js +2 -0
  82. ledger/static/ledger/libs/amCharts/5.14.4/js/xy.js +2 -0
  83. ledger/static/ledger/libs/datatables/2.3.5/css/dataTables.bootstrap5.css +610 -0
  84. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.bootstrap5.js +122 -0
  85. ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.js +14127 -0
  86. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
  87. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.dataTables.css +529 -0
  88. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
  89. ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3090 -0
  90. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
  91. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
  92. ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
  93. ledger/tasks.py +157 -146
  94. ledger/templates/ledger/base.html +59 -21
  95. ledger/templates/ledger/bundles/aa-ledger-css.html +3 -0
  96. ledger/templates/ledger/bundles/aa-ledger-js.html +3 -0
  97. ledger/templates/ledger/bundles/view-alliance-ledger-js.html +14 -0
  98. ledger/templates/ledger/bundles/view-character-ledger-js.html +15 -0
  99. ledger/templates/ledger/bundles/view-character-planetary-css.html +3 -0
  100. ledger/templates/ledger/bundles/view-character-planetary-js.html +4 -0
  101. ledger/templates/ledger/bundles/view-corporation-ledger-js.html +15 -0
  102. ledger/templates/ledger/partials/modal/confirm.html +0 -1
  103. ledger/templates/ledger/partials/modal/request-accept-delete-alliance.html +38 -0
  104. ledger/templates/ledger/partials/modal/request-accept-delete-character.html +38 -0
  105. ledger/templates/ledger/partials/modal/request-accept-delete-corporation.html +38 -0
  106. ledger/templates/ledger/partials/modal/request-accept-switch-notification.html +38 -0
  107. ledger/templates/ledger/partials/modal/request-view-alliance-details.html +26 -0
  108. ledger/templates/ledger/partials/modal/request-view-character-details.html +26 -0
  109. ledger/templates/ledger/partials/modal/request-view-corporation-details.html +26 -0
  110. ledger/templates/ledger/partials/modal/request-view-extractor.html +32 -0
  111. ledger/templates/ledger/partials/modal/request-view-factory.html +31 -0
  112. ledger/templates/ledger/partials/{menu → navigation}/administration.html +8 -0
  113. ledger/templates/ledger/partials/{menu → navigation}/navigation.html +2 -2
  114. ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance_corporations.html +3 -3
  115. ledger/templates/ledger/partials/view-alliance-administration/dashboard.html +81 -0
  116. ledger/templates/ledger/partials/view-alliance-ledger/alliance-billboard.html +25 -0
  117. ledger/templates/ledger/partials/view-alliance-ledger/alliance-ledger-details.html +21 -0
  118. ledger/templates/ledger/partials/view-alliance-ledger/alliance-table.html +24 -0
  119. ledger/templates/ledger/partials/view-alliance-ledger/information/daily.html +18 -0
  120. ledger/templates/ledger/partials/view-alliance-ledger/information/hourly.html +18 -0
  121. ledger/templates/ledger/partials/view-alliance-ledger/information/summary.html +19 -0
  122. ledger/templates/ledger/partials/{administration → view-character-administration}/character.html +1 -9
  123. ledger/templates/ledger/partials/{administration → view-character-administration}/dashboard.html +0 -34
  124. ledger/templates/ledger/partials/view-character-ledger/character-billboard.html +25 -0
  125. ledger/templates/ledger/partials/view-character-ledger/character-ledger-details.html +21 -0
  126. ledger/templates/ledger/partials/view-character-ledger/character-table.html +25 -0
  127. ledger/templates/ledger/partials/view-character-ledger/information/daily.html +18 -0
  128. ledger/templates/ledger/partials/view-character-ledger/information/hourly.html +18 -0
  129. ledger/templates/ledger/partials/view-character-ledger/information/summary.html +19 -0
  130. ledger/templates/ledger/partials/view-character-planetary/extractor-table.html +24 -0
  131. ledger/templates/ledger/partials/view-character-planetary/factory-table.html +24 -0
  132. ledger/templates/ledger/partials/view-character-planetary/planetary-table.html +22 -0
  133. ledger/templates/ledger/partials/view-character-planetary/storage-table.html +23 -0
  134. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation.html +5 -13
  135. ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation_characters.html +1 -1
  136. ledger/templates/ledger/partials/view-corporation-administration/dashboard.html +81 -0
  137. ledger/templates/ledger/partials/view-corporation-ledger/corporation-billboard.html +25 -0
  138. ledger/templates/ledger/partials/view-corporation-ledger/corporation-ledger-details.html +21 -0
  139. ledger/templates/ledger/partials/view-corporation-ledger/corporation-table.html +26 -0
  140. ledger/templates/ledger/partials/view-corporation-ledger/information/daily.html +18 -0
  141. ledger/templates/ledger/partials/view-corporation-ledger/information/hourly.html +18 -0
  142. ledger/templates/ledger/partials/view-corporation-ledger/information/summary.html +19 -0
  143. ledger/templates/ledger/view-administration.html +62 -0
  144. ledger/templates/ledger/view-alliance-administration.html +49 -0
  145. ledger/templates/ledger/view-alliance-ledger.html +72 -0
  146. ledger/templates/ledger/view-alliance-overview.html +131 -0
  147. ledger/templates/ledger/view-character-administration.html +42 -0
  148. ledger/templates/ledger/view-character-ledger.html +73 -0
  149. ledger/templates/ledger/view-character-overview.html +135 -0
  150. ledger/templates/ledger/view-character-planetary-overview.html +135 -0
  151. ledger/templates/ledger/view-character-planetary.html +73 -0
  152. ledger/templates/ledger/view-corporation-administration.html +42 -0
  153. ledger/templates/ledger/view-corporation-ledger.html +73 -0
  154. ledger/templates/ledger/view-corporation-overview.html +131 -0
  155. ledger/templatetags/ledger.py +3 -5
  156. ledger/tests/__init__.py +187 -0
  157. ledger/tests/test_admin.py +164 -68
  158. ledger/tests/test_auth_hook.py +31 -13
  159. ledger/tests/test_decarators.py +14 -79
  160. ledger/tests/test_discord_installed.py +0 -1
  161. ledger/tests/test_helpers/test_ledger_data.py +19 -0
  162. ledger/tests/test_managers/test_character_audit_manager.py +111 -69
  163. ledger/tests/test_managers/test_character_journal_manager.py +48 -208
  164. ledger/tests/test_managers/test_character_mining_manager.py +37 -16
  165. ledger/tests/test_managers/test_corporation_division_manager.py +66 -28
  166. ledger/tests/test_managers/test_corporation_journal_manager.py +39 -42
  167. ledger/tests/test_managers/test_general_manager.py +78 -18
  168. ledger/tests/test_managers/test_planetary_manager.py +73 -32
  169. ledger/tests/test_models/test_characteraudit.py +58 -74
  170. ledger/tests/test_models/test_characterminingledger.py +20 -26
  171. ledger/tests/test_models/test_characterwalletjournal.py +10 -33
  172. ledger/tests/test_models/test_corporationaudit.py +41 -35
  173. ledger/tests/test_models/test_corporationwalletjournal.py +35 -32
  174. ledger/tests/test_models/test_general.py +44 -11
  175. ledger/tests/test_models/test_planetary.py +14 -80
  176. ledger/tests/test_templatetags.py +2 -7
  177. ledger/tests/test_views/corporation/test_add_corp.py +16 -35
  178. ledger/tests/test_views/corporation/test_delete_corporation.py +66 -42
  179. ledger/tests/test_views/test_access.py +512 -545
  180. ledger/tests/test_views/test_add_ally.py +57 -46
  181. ledger/tests/test_views/test_add_char.py +21 -33
  182. ledger/tests/test_views/test_delete_character.py +24 -21
  183. ledger/tests/testdata/README_ESI_STUB.md +430 -0
  184. ledger/tests/testdata/esi_stub_openapi.py +511 -0
  185. ledger/tests/testdata/integrations/__init__.py +0 -0
  186. ledger/tests/testdata/{load_eveuniverse.py → integrations/eveuniverse.py} +0 -1
  187. ledger/tests/testdata/integrations/planetary.py +13 -0
  188. ledger/tests/testdata/json/factory.json +281 -0
  189. ledger/tests/testdata/json/inactive.json +281 -0
  190. ledger/tests/testdata/json/pins.json +175 -272
  191. ledger/tests/testdata/json/route.json +95 -528
  192. ledger/tests/testdata/test_esi_stub.py +468 -0
  193. ledger/tests/testdata/utils.py +601 -0
  194. ledger/thirdparty/charlink_hook.py +60 -30
  195. ledger/urls.py +0 -135
  196. ledger/views/alliance/add_ally.py +2 -4
  197. ledger/views/alliance/alliance_ledger.py +64 -147
  198. ledger/views/character/add_char.py +8 -10
  199. ledger/views/character/character_ledger.py +60 -126
  200. ledger/views/character/planetary.py +5 -98
  201. ledger/views/corporation/add_corp.py +10 -12
  202. ledger/views/corporation/corporation_ledger.py +65 -327
  203. ledger/views/index.py +92 -30
  204. aa_ledger-1.0.3.dist-info/RECORD +0 -236
  205. ledger/api/api_helper/planetary_helper.py +0 -107
  206. ledger/api/ledger/__init__.py +0 -7
  207. ledger/api/ledger/planetary.py +0 -231
  208. ledger/helpers/alliance.py +0 -317
  209. ledger/helpers/character.py +0 -251
  210. ledger/helpers/core.py +0 -665
  211. ledger/helpers/corporation.py +0 -427
  212. ledger/helpers/data_exporter.py +0 -452
  213. ledger/static/ledger/js/planetary-confirm.js +0 -66
  214. ledger/static/ledger/js/planetary.js +0 -143
  215. ledger/templates/ledger/admin.html +0 -43
  216. ledger/templates/ledger/allyledger/admin/alliance_administration.html +0 -46
  217. ledger/templates/ledger/allyledger/admin/alliance_overview.html +0 -108
  218. ledger/templates/ledger/allyledger/alliance_ledger.html +0 -86
  219. ledger/templates/ledger/bundles/character-ledger-bundles.html +0 -66
  220. ledger/templates/ledger/bundles/corporation-ledger-bundles.html +0 -75
  221. ledger/templates/ledger/bundles/ledger-bundles.html +0 -23
  222. ledger/templates/ledger/bundles/ledger-css.html +0 -3
  223. ledger/templates/ledger/bundles/planetary-bundles.html +0 -50
  224. ledger/templates/ledger/bundles/table-css.html +0 -3
  225. ledger/templates/ledger/charledger/admin/character_administration.html +0 -39
  226. ledger/templates/ledger/charledger/admin/character_overview.html +0 -106
  227. ledger/templates/ledger/charledger/character_ledger.html +0 -94
  228. ledger/templates/ledger/charledger/planetary/admin/planetary_overview.html +0 -123
  229. ledger/templates/ledger/charledger/planetary/planetary_ledger.html +0 -54
  230. ledger/templates/ledger/corpledger/admin/corporation_administration.html +0 -39
  231. ledger/templates/ledger/corpledger/admin/corporation_overview.html +0 -108
  232. ledger/templates/ledger/corpledger/corporation_ledger.html +0 -129
  233. ledger/templates/ledger/data-export.html +0 -78
  234. ledger/templates/ledger/error.html +0 -31
  235. ledger/templates/ledger/partials/form/error-message.html +0 -1
  236. ledger/templates/ledger/partials/information/daily.html +0 -56
  237. ledger/templates/ledger/partials/information/day.html +0 -48
  238. ledger/templates/ledger/partials/information/error.html +0 -8
  239. ledger/templates/ledger/partials/information/hourly.html +0 -53
  240. ledger/templates/ledger/partials/information/summary.html +0 -88
  241. ledger/templates/ledger/partials/information/view_character_content.html +0 -35
  242. ledger/templates/ledger/partials/modal/switchalarm_confirm.html +0 -39
  243. ledger/templates/ledger/partials/modal/view_extractor.html +0 -48
  244. ledger/templates/ledger/partials/modal/view_factory.html +0 -123
  245. ledger/templates/ledger/partials/table/char-ledger.html +0 -85
  246. ledger/templates/ledger/partials/table/corp-ledger.html +0 -66
  247. ledger/templates/ledger/partials/table/planetary.html +0 -18
  248. ledger/templates/ledger/partials/thirdparty/billboard.html +0 -22
  249. ledger/templates/ledger/partials/view/card.html +0 -160
  250. ledger/templates/ledger/permission.html +0 -2
  251. ledger/tests/test_helpers/test_billboard.py +0 -11
  252. ledger/tests/test_helpers/test_data_exporter.py +0 -207
  253. ledger/tests/test_tasks.py +0 -282
  254. ledger/tests/test_view_helpers/test_core.py +0 -47
  255. ledger/tests/test_views/corporation/test_corporation.py +0 -267
  256. ledger/tests/test_views/test_planetary.py +0 -137
  257. ledger/tests/testdata/esi_stub.py +0 -109
  258. ledger/tests/testdata/esi_stub_migration.py +0 -80
  259. ledger/tests/testdata/generate_characteraudit.py +0 -106
  260. ledger/tests/testdata/generate_corporationaudit.py +0 -74
  261. ledger/tests/testdata/generate_events.py +0 -31
  262. ledger/tests/testdata/generate_miningledger.py +0 -13
  263. ledger/tests/testdata/generate_planets.py +0 -48
  264. ledger/tests/testdata/generate_walletjournal.py +0 -42
  265. ledger/tests/testdata/json/czarno-pins.json +0 -240
  266. ledger/tests/testdata/json/czarno-routes.json +0 -165
  267. ledger/tests/testdata/json/pins2.json +0 -538
  268. {aa_ledger-1.0.3.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
@@ -5,41 +5,49 @@ Planetary Model
5
5
  # Django
6
6
  from django.db import models
7
7
  from django.utils import timezone
8
- from django.utils.dateparse import parse_datetime
9
8
  from django.utils.translation import gettext_lazy as _
10
9
 
11
10
  # Alliance Auth
12
11
  from allianceauth.services.hooks import get_extension_logger
13
12
 
14
13
  # Alliance Auth (External Libs)
15
- from app_utils.logging import LoggerAddTag
16
- from eveuniverse.models import EvePlanet, EveType
14
+ from eveuniverse.models import EvePlanet
17
15
 
18
16
  # AA Ledger
19
17
  from ledger import __title__
20
- from ledger.constants import EXTRACTOR_CONTROL_UNIT, P0_PRODUCTS, SPACEPORTS
21
18
  from ledger.managers.character_planetary_manager import (
22
- PlanetaryDetailsManager,
23
- PlanetaryManager,
19
+ CharacterPlanetManager,
20
+ PlanetDetailsManager,
24
21
  )
25
- from ledger.models.characteraudit import CharacterAudit, CharacterUpdateStatus
22
+ from ledger.models.characteraudit import CharacterOwner, CharacterUpdateStatus
23
+ from ledger.models.helpers.update_manager import CharacterUpdateSection
24
+ from ledger.providers import AppLogger
26
25
 
27
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
26
+ logger = AppLogger(get_extension_logger(__name__), __title__)
28
27
 
29
28
 
30
29
  class CharacterPlanet(models.Model):
31
- objects = PlanetaryManager()
30
+ """Model to store the planetary data of a character"""
31
+
32
+ objects: CharacterPlanetManager = CharacterPlanetManager()
33
+
34
+ class Meta:
35
+ default_permissions = ()
36
+ indexes = [
37
+ models.Index(fields=["character"]),
38
+ models.Index(fields=["eve_planet"]),
39
+ ]
32
40
 
33
41
  id = models.AutoField(primary_key=True)
34
42
 
35
- planet_name = models.CharField(max_length=100, null=True, default=None)
43
+ name = models.CharField(max_length=100, null=True, default=None)
36
44
 
37
- planet = models.ForeignKey(
45
+ eve_planet = models.ForeignKey(
38
46
  EvePlanet, on_delete=models.CASCADE, related_name="ledger_planet"
39
47
  )
40
48
 
41
49
  character = models.ForeignKey(
42
- CharacterAudit, on_delete=models.CASCADE, related_name="ledger_character_planet"
50
+ CharacterOwner, on_delete=models.CASCADE, related_name="ledger_character_planet"
43
51
  )
44
52
 
45
53
  upgrade_level = models.IntegerField(
@@ -50,23 +58,16 @@ class CharacterPlanet(models.Model):
50
58
  default=0, help_text=_("Number of pins on the planet")
51
59
  )
52
60
 
53
- class Meta:
54
- default_permissions = ()
55
- indexes = [
56
- models.Index(fields=["character"]),
57
- models.Index(fields=["planet"]),
58
- ]
59
-
60
61
  def __str__(self):
61
- return f"Planet Data: {self.character.eve_character.character_name} - {self.planet.name}"
62
+ return f"Planet Data: {self.character.eve_character.character_name} - {self.eve_planet.name}"
62
63
 
63
64
  @property
64
65
  def last_update(self) -> timezone.datetime:
65
66
  """Return the last update time of the planet."""
66
67
  try:
67
68
  last_update = CharacterUpdateStatus.objects.get(
68
- character=self.character,
69
- section=CharacterAudit.UpdateSection.PLANETS,
69
+ owner=self.character,
70
+ section=CharacterUpdateSection.PLANETS,
70
71
  ).last_update_at
71
72
  except CharacterUpdateStatus.DoesNotExist:
72
73
  last_update = None
@@ -83,7 +84,13 @@ class CharacterPlanet(models.Model):
83
84
  class CharacterPlanetDetails(models.Model):
84
85
  """Model to store the details of a planet"""
85
86
 
86
- objects = PlanetaryDetailsManager()
87
+ objects: PlanetDetailsManager = PlanetDetailsManager()
88
+
89
+ class Meta:
90
+ default_permissions = ()
91
+ indexes = [
92
+ models.Index(fields=["planet"]),
93
+ ]
87
94
 
88
95
  id = models.AutoField(primary_key=True)
89
96
 
@@ -94,7 +101,7 @@ class CharacterPlanetDetails(models.Model):
94
101
  )
95
102
 
96
103
  character = models.ForeignKey(
97
- CharacterAudit,
104
+ CharacterOwner,
98
105
  on_delete=models.CASCADE,
99
106
  related_name="ledger_character_planet_details",
100
107
  )
@@ -102,196 +109,44 @@ class CharacterPlanetDetails(models.Model):
102
109
  links = models.JSONField(null=True, default=None, blank=True)
103
110
  pins = models.JSONField(null=True, default=None, blank=True)
104
111
  routes = models.JSONField(null=True, default=None, blank=True)
105
- facilitys = models.JSONField(null=True, default=None, blank=True)
112
+ factories = models.JSONField(null=True, default=None, blank=True)
106
113
 
107
114
  last_alert = models.DateTimeField(null=True, default=None, blank=True)
108
115
 
109
116
  notification = models.BooleanField(default=False)
110
117
  notification_sent = models.BooleanField(default=False)
111
118
 
112
- class Meta:
113
- default_permissions = ()
114
- indexes = [
115
- models.Index(fields=["planet"]),
116
- ]
117
-
118
119
  def __str__(self):
119
- return f"Planet Details Data: {self.planet.character.eve_character.character_name} - {self.planet.planet.name}"
120
-
121
- def count_extractors(self):
122
- return len(
123
- [pin for pin in self.pins if pin.get("type_id") in EXTRACTOR_CONTROL_UNIT]
124
- )
125
-
126
- def get_planet_install_date(self):
127
- install_times = [
128
- pin.get("install_time")
129
- for pin in self.pins
130
- if pin.get("install_time") and pin["install_time"] != "0"
131
- ]
132
- if install_times:
133
- install = timezone.datetime.fromisoformat(
134
- min(install_times).replace("Z", "+00:00")
135
- )
136
- return install
137
- return None
138
-
139
- def get_planet_expiry_date(self):
140
- if self.pins is None:
141
- return None
142
-
143
- expiry_times = [
144
- pin.get("expiry_time")
145
- for pin in self.pins
146
- if pin.get("expiry_time") and pin["expiry_time"] != "0"
147
- ]
148
- if expiry_times:
149
- alert = timezone.datetime.fromisoformat(
150
- min(expiry_times).replace("Z", "+00:00")
151
- )
152
- return alert
153
- return None
154
-
155
- def get_types(self) -> list:
156
- """Get the product types of the routes on the planet"""
157
- types = []
158
- for pin in self.routes:
159
- if pin.get("content_type_id") not in types:
160
- types.append(pin.get("content_type_id"))
161
- return types
162
-
163
- def allocate_products(self) -> dict:
164
- """Get the product types on the planet"""
165
- product_types = {}
166
- for c_type_id in self.routes:
167
- type_id = c_type_id.get("content_type_id")
168
- if type_id not in P0_PRODUCTS and type_id not in product_types:
169
- type_data, _ = EveType.objects.get_or_create_esi(id=type_id)
170
- product_types[type_id] = {
171
- "id": type_id,
172
- "name": type_data.name,
173
- "category": type_data.eve_group.name,
174
- }
175
- return product_types
176
-
177
- def allocate_extracts(self) -> dict:
178
- """Get the extractor raw product types on the planet"""
179
- product_types = {}
180
- for c_type_id in self.routes:
181
- type_id = c_type_id.get("content_type_id")
182
- if type_id in P0_PRODUCTS and type_id not in product_types:
183
- type_data, _ = EveType.objects.get_or_create_esi(id=type_id)
184
- product_types[type_id] = {
185
- "id": type_id,
186
- "name": type_data.name,
187
- "category": type_data.eve_group.name,
188
- }
189
- return product_types
190
-
191
- def allocate_overall_progress(self) -> dict:
192
- extractors = self.get_extractors_info()
193
-
194
- if not extractors:
195
- return 0
196
-
197
- total_install_time = 0
198
- total_expiry_time = 0
199
- current_time = timezone.now().timestamp() * 1000 # Current time in milliseconds
200
- extractor_count = len(extractors)
201
-
202
- for extractor in extractors.values():
203
- if not extractor.get("install_time") or not extractor.get("expiry_time"):
204
- continue
205
- install_time = (
206
- timezone.datetime.fromisoformat(extractor["install_time"]).timestamp()
207
- * 1000
208
- )
209
- expiry_time = (
210
- timezone.datetime.fromisoformat(extractor["expiry_time"]).timestamp()
211
- * 1000
212
- )
213
-
214
- total_install_time += install_time
215
- total_expiry_time += expiry_time
216
-
217
- average_install_time = total_install_time / extractor_count
218
- average_expiry_time = total_expiry_time / extractor_count
219
-
220
- total_duration = average_expiry_time - average_install_time
221
- elapsed_duration = current_time - average_install_time
222
- progress_percentage = min(
223
- max((elapsed_duration / total_duration) * 100, 0), 100
224
- )
225
-
226
- return progress_percentage
227
-
228
- def get_extractors_info(self) -> dict:
229
- extractors = {}
230
- current_time = timezone.now().timestamp()
231
-
232
- for pin in self.pins:
233
- extractor_details = pin.get("extractor_details")
234
- if extractor_details and "cycle_time" in extractor_details:
235
- type_id = extractor_details.get("product_type_id")
236
- type_data, _ = EveType.objects.get_or_create_esi(id=type_id)
237
-
238
- install_time_str = pin.get("install_time")
239
- expiry_time_str = pin.get("expiry_time")
240
-
241
- install_time = parse_datetime(install_time_str).timestamp()
242
- expiry_time = parse_datetime(expiry_time_str).timestamp()
243
-
244
- # Calculate progress percentage (0-100)
245
- if current_time >= expiry_time:
246
- progress_percentage = 100.0
247
- else:
248
- progress_percentage = round(
249
- ((current_time - install_time) / (expiry_time - install_time))
250
- * 100,
251
- 2,
252
- )
253
-
254
- extractors[pin.get("pin_id")] = {
255
- "install_time": install_time_str,
256
- "expiry_time": expiry_time_str,
257
- "item_id": type_id,
258
- "item_name": type_data.name,
259
- "progress_percentage": progress_percentage,
260
- }
261
- return extractors
262
-
263
- def get_storage_info(self) -> dict:
264
- storage = {}
265
- for pin in self.pins:
266
- type_id = pin.get("type_id")
267
- if type_id in SPACEPORTS:
268
- type_data, _ = EveType.objects.get_or_create_esi(id=type_id)
269
- contents_info = []
270
- for content in pin.get("contents", []):
271
- content_type_id = content.get("type_id")
272
- content_type_data, _ = EveType.objects.get_or_create_esi(
273
- id=content_type_id
274
- )
275
- contents_info.append(
276
- {
277
- "amount": content.get("amount"),
278
- "type_id": content_type_id,
279
- "product_name": content_type_data.name,
280
- }
281
- )
282
- storage[pin.get("pin_id")] = {
283
- "facility_id": type_id,
284
- "facility_name": type_data.name,
285
- "contents": contents_info,
286
- }
287
- return storage
120
+ return f"Planet Details Data: {self.planet.character.eve_character.character_name} - {self.planet.eve_planet.name}"
288
121
 
289
122
  @property
290
123
  def is_expired(self):
291
- expiry_date = self.get_planet_expiry_date()
292
- if expiry_date is None:
293
- return False
294
- return expiry_date < timezone.now()
124
+ """
125
+ Return False (not expired) when any extractor is running or when any
126
+ 'Processors' facility has an active resource. Otherwise return True.
127
+ """
128
+ # No factories means nothing is running, so expired
129
+ if not self.factories:
130
+ return True
131
+
132
+ # Check all facilities for running extractors or active processor ressources
133
+ try:
134
+ factories = self.factories.values()
135
+ except AttributeError:
136
+ return True
137
+
138
+ for factory in factories:
139
+ # Extractor running?
140
+ extractor = factory.get("extractor", {})
141
+ if extractor.get("is_running", False):
142
+ return False
143
+
144
+ # Processors: any resource with is_active True?
145
+ if factory.get("facility_type") == "Processors":
146
+ for ressource in factory.get("ressources", []) or []:
147
+ if ressource.get("is_active", False):
148
+ return False
149
+ return True
295
150
 
296
151
  @property
297
152
  def last_update(self) -> timezone.datetime:
@@ -299,7 +154,7 @@ class CharacterPlanetDetails(models.Model):
299
154
  try:
300
155
  last_update = CharacterUpdateStatus.objects.get(
301
156
  character=self.character,
302
- section=CharacterAudit.UpdateSection.PLANETS_DETAILS,
157
+ section=CharacterUpdateSection.PLANETS_DETAILS,
303
158
  ).last_update_at
304
159
  except CharacterUpdateStatus.DoesNotExist:
305
160
  last_update = None
ledger/providers.py CHANGED
@@ -1,6 +1,22 @@
1
1
  """Shared ESI client for Ledger."""
2
2
 
3
+ # Standard Library
4
+ import logging
5
+ import random
6
+ from contextlib import contextmanager
7
+ from http import HTTPStatus
8
+
9
+ # Third Party
10
+ from aiopenapi3 import RequestError
11
+ from celery import Task
12
+
3
13
  # Alliance Auth
14
+ from allianceauth.services.hooks import get_extension_logger
15
+ from esi.exceptions import (
16
+ ESIBucketLimitException,
17
+ ESIErrorLimitException,
18
+ HTTPServerError,
19
+ )
4
20
  from esi.openapi_clients import ESIClientProvider
5
21
 
6
22
  # AA Ledger
@@ -24,3 +40,88 @@ esi = ESIClientProvider(
24
40
  + __character_operations__
25
41
  + __universe_operations__,
26
42
  )
43
+
44
+
45
+ class AppLogger(logging.LoggerAdapter):
46
+ """
47
+ Custom logger adapter that adds a prefix to log messages.
48
+
49
+ Taken from the `allianceauth-app-utils` package.
50
+ Credits to: Erik Kalkoken
51
+ """
52
+
53
+ def __init__(self, my_logger, prefix):
54
+ """
55
+ Initializes the AppLogger with a logger and a prefix.
56
+
57
+ :param my_logger: Logger instance
58
+ :type my_logger: logging.Logger
59
+ :param prefix: Prefix string to add to log messages
60
+ :type prefix: str
61
+ """
62
+
63
+ super().__init__(my_logger, {})
64
+
65
+ self.prefix = prefix
66
+
67
+ def process(self, msg, kwargs):
68
+ """
69
+ Prepares the log message by adding the prefix.
70
+
71
+ :param msg: Original log message
72
+ :type msg: str
73
+ :param kwargs: Additional keyword arguments for logging
74
+ :type kwargs: dict
75
+ :return: Tuple of modified message and kwargs
76
+ :rtype: tuple
77
+ """
78
+ return f"[{self.prefix}] {msg}", kwargs
79
+
80
+
81
+ logger = AppLogger(my_logger=get_extension_logger(__name__), prefix=__title__)
82
+
83
+
84
+ @contextmanager
85
+ def retry_task_on_esi_error(task: Task):
86
+ """Retry Task when a ESI error occurs.
87
+
88
+ Taken from the `allianceauth-app-utils` package.
89
+ Credits to: Erik Kalkoken
90
+
91
+ Retries on:
92
+ - Error limits reached (ESIErrorLimitException)
93
+ - Rate limit errors (ESIBucketLimitException)
94
+ - HTTPError with status codes 502, 503, 504 (server errors)
95
+
96
+ :param task: Celery Task instance
97
+ :return: Context manager that retries the task on ESI errors.
98
+
99
+ """
100
+
101
+ def retry(exc: Exception, retry_after: float, issue: str):
102
+ backoff_jitter = int(random.uniform(2, 5) ** task.request.retries)
103
+ countdown = retry_after + backoff_jitter
104
+ logger.warning(
105
+ "ESI Error encountered: %s. Retrying after %.2f seconds. Issue: %s",
106
+ str(exc),
107
+ countdown,
108
+ issue,
109
+ )
110
+ raise task.retry(countdown=countdown, exc=exc)
111
+
112
+ try:
113
+ yield
114
+ except ESIErrorLimitException as exc:
115
+ retry(exc, exc.reset, "ESI Error Limit Reached")
116
+ except ESIBucketLimitException as exc:
117
+ retry(exc, exc.reset, f"ESI Bucket Limit Reached for {exc.bucket}")
118
+ except HTTPServerError as exc:
119
+ if exc.status_code in [
120
+ HTTPStatus.BAD_GATEWAY,
121
+ HTTPStatus.SERVICE_UNAVAILABLE,
122
+ HTTPStatus.GATEWAY_TIMEOUT,
123
+ ]:
124
+ retry(exc, 60, f"ESI seems to be down (HTTP {exc.status_code})")
125
+ raise exc
126
+ except RequestError as exc:
127
+ retry(exc, 60, "Request Error")
@@ -11,26 +11,6 @@
11
11
  animation-iteration-count: infinite;
12
12
  }
13
13
 
14
- .custom-line {
15
- height: 3px;
16
- border: none;
17
- background: linear-gradient(to right, rgb(0 170 255 / 100%) 0%, rgb(0 170 255 / 0%) 80%, rgb(0 170 255 / 0%) 100%);
18
- z-index: 1;
19
- }
20
-
21
- /* Style the Image Used to Trigger the Modal */
22
- .zoom {
23
- opacity: 0.7;
24
- border-radius: 10px;
25
- cursor: pointer;
26
- transition: 0.3s;
27
- }
28
-
29
- .zoom:hover {
30
- opacity: 1;
31
- transform: scale(1.1);
32
- }
33
-
34
14
  /* Custom CSS for tab mouseover */
35
15
  .nav-tabs .nav-link:hover {
36
16
  opacity: 0.8;
@@ -38,10 +18,6 @@
38
18
  border-color: white;
39
19
  }
40
20
 
41
- .aa-ledger-modal-header > h5 {
42
- border-bottom: 1px solid var(--bs-secondary-bg-subtle);
43
- }
44
-
45
21
  .dropdown-scrollable {
46
22
  max-height: 200px; /* Adjust the height as needed */
47
23
  overflow-y: auto;
@@ -52,10 +28,7 @@
52
28
  color: rgb(33 37 41); /* Dark text color */
53
29
  }
54
30
 
55
- .card-ledger {
56
- min-width: 1000px;
57
- }
58
-
31
+ /* Ratting Chart */
59
32
  .rattingchart {
60
33
  height: 500px;
61
34
  margin-left: 50px;
@@ -66,6 +39,59 @@
66
39
  .rattingbar {
67
40
  height: 500px;
68
41
  }
42
+
43
+ /* Ratting Table */
44
+ #ratting {
45
+ table-layout: fixed;
46
+ overflow-wrap: break-word;
47
+ }
48
+
49
+ #ratting th,
50
+ #ratting td {
51
+ padding: 0.75rem;
52
+ vertical-align: top;
53
+ }
54
+
55
+ #ratting thead th {
56
+ text-align: center;
57
+ }
58
+
59
+ #ratting thead th.col-main-character {
60
+ text-align: left;
61
+ }
62
+
63
+ #ratting tbody td {
64
+ text-align: right;
65
+ }
66
+
67
+ #ratting tbody td:nth-child(1) {
68
+ text-align: left;
69
+ }
70
+
71
+ #ratting tfoot th {
72
+ text-align: right;
73
+ border-top: 1px solid rgb(222 226 230);
74
+ }
75
+
76
+ #ratting tfoot th:nth-child(1) {
77
+ text-align: left;
78
+ }
79
+
80
+ .nav-tabs .nav-item .nav-link {
81
+ opacity: 0.3;
82
+ }
83
+
84
+ .nav-tabs .nav-item .nav-link.active {
85
+ opacity: 1.0;
86
+ }
87
+
88
+ /* CSV Export Button */
89
+ .aa-ledger-csv {
90
+ position: relative;
91
+ z-index: 1;
92
+ pointer-events: auto;
93
+ display: inline-block;
94
+ }
69
95
  }
70
96
 
71
97
 
@@ -0,0 +1,124 @@
1
+ /* global aaLedgerDefaultSettings, aaLedgerSettingsOverride, objectDeepMerge, bootstrap */
2
+
3
+ /**
4
+ * Default settings for aa-ledger
5
+ * Settings can be overridden by defining aaLedgerSettingsOverride before this script is loaded.
6
+ */
7
+ const aaLedgerSettings = (typeof aaLedgerSettingsOverride !== 'undefined')
8
+ ? objectDeepMerge(aaLedgerDefaultSettings, aaLedgerSettingsOverride) // jshint ignore:line
9
+ : aaLedgerDefaultSettings;
10
+
11
+ /**
12
+ * Bootstrap tooltip by (@ppfeufer)
13
+ *
14
+ * @param {string} [selector=body] Selector for the tooltip elements, defaults to 'body'
15
+ * to apply to all elements with the data-bs-tooltip attribute.
16
+ * Example: 'body', '.my-tooltip-class', '#my-tooltip-id'
17
+ * If you want to apply it to a specific element, use that element's selector.
18
+ * If you want to apply it to all elements with the data-bs-tooltip attribute,
19
+ * use 'body' or leave it empty.
20
+ * @param {string} [namespace=aa-ledger] Namespace for the tooltip
21
+ * @param {string} [trigger=hover] Trigger for the tooltip ('hover', 'click', etc.)
22
+ * @returns {void}
23
+ */
24
+ const _bootstrapTooltip = ({selector = 'body', namespace = 'aa-ledger', trigger = 'hover'} = {}) => {
25
+ document.querySelectorAll(`${selector} [data-bs-tooltip="${namespace}"]`)
26
+ .forEach((tooltipTriggerEl) => {
27
+ // Dispose existing tooltip instance if it exists
28
+ const existing = bootstrap.Tooltip.getInstance(tooltipTriggerEl);
29
+ if (existing) {
30
+ existing.dispose();
31
+ }
32
+
33
+ // Remove any leftover tooltip elements
34
+ $('.bs-tooltip-auto').remove();
35
+
36
+ // Create new tooltip instance
37
+ return new bootstrap.Tooltip(tooltipTriggerEl, { trigger });
38
+ });
39
+ };
40
+
41
+ const _bootstrapPopOver = ({selector = 'body', namespace = 'aa-ledger', trigger = 'hover'} = {}) => {
42
+ document.querySelectorAll(`${selector} [data-bs-popover="${namespace}"]`)
43
+ .forEach((popoverTriggerEl) => {
44
+ // Dispose existing popover instance if it exists
45
+ const existing = bootstrap.Popover.getInstance(popoverTriggerEl);
46
+ if (existing) {
47
+ existing.dispose();
48
+ }
49
+
50
+ // Remove any leftover popover elements
51
+ $('.bs-popover-auto').remove();
52
+
53
+ // Create new popover instance
54
+ return new bootstrap.Popover(popoverTriggerEl, { trigger });
55
+ });
56
+ };
57
+
58
+ /**
59
+ * Export HTML table to CSV file (DataTables-only)
60
+ *
61
+ * This function exports the contents of a DataTables-enhanced HTML table to a CSV file.
62
+ *
63
+ * @param {HTMLElement} table HTML table element enhanced by DataTables
64
+ * @param {number} columnIndex Index of the column to exclude from export (e.g., actions column)
65
+ * @param {string} filename Name of the CSV file to be downloaded
66
+ * @returns {void}
67
+ */
68
+ const _exportTableToCSV = (dt, columnIndex, filename = 'ledger.csv') => {
69
+ const csv = [];
70
+
71
+ let headerCells = [];
72
+ // Get header cells from DataTables API
73
+ try {
74
+ headerCells = dt.columns().header().toArray();
75
+ } catch (e) {
76
+ console.log('Error retrieving DataTables column headers for CSV export:', e);
77
+ }
78
+
79
+ // Determine columns to export (exclude specified column)
80
+ const colCount = dt.columns().count();
81
+ let exportColIndexes = [];
82
+ for (let i = 0; i < colCount; i++) exportColIndexes.push(i);
83
+ exportColIndexes = exportColIndexes.filter(i => i !== columnIndex);
84
+
85
+ // Header row
86
+ const headerRow = [];
87
+ for (let ci = 0; ci < exportColIndexes.length; ci++) {
88
+ const colIndex = exportColIndexes[ci];
89
+ const cell = headerCells[colIndex];
90
+ const raw = cell ? (cell.innerText || cell.textContent || '') : '';
91
+ headerRow.push(raw);
92
+ }
93
+ csv.push(headerRow.join(','));
94
+
95
+ // Process rows by index and download CSV (use sort cell values)
96
+ const rowIndexes = dt.rows({ search: 'applied', page: 'all' }).indexes().toArray();
97
+ for (let ri = 0; ri < rowIndexes.length; ri++) {
98
+ const rowIndex = rowIndexes[ri];
99
+ const row = [];
100
+ for (let k = 0; k < exportColIndexes.length; k++) {
101
+ const ci = exportColIndexes[k];
102
+ let raw = '';
103
+ try {
104
+ const renderMode = 'sort';
105
+ raw = dt.cell(rowIndex, ci).render(renderMode);
106
+ } catch (e) {
107
+ console.log(`Error retrieving cell data for row ${rowIndex}, column ${ci}:`, e);
108
+ raw = '';
109
+ }
110
+ row.push(raw);
111
+ }
112
+ csv.push(row.join(','));
113
+ }
114
+
115
+ // Download CSV file
116
+ const csvFile = new Blob([csv.join('\n')], { type: 'text/csv' });
117
+ const downloadLink = document.createElement('a');
118
+ downloadLink.download = filename;
119
+ downloadLink.href = window.URL.createObjectURL(csvFile);
120
+ downloadLink.style.display = 'none';
121
+ document.body.appendChild(downloadLink);
122
+ downloadLink.click();
123
+ document.body.removeChild(downloadLink);
124
+ };