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
@@ -4,21 +4,22 @@ from typing import TYPE_CHECKING
4
4
  # Django
5
5
  from django.db import models, transaction
6
6
  from django.utils import timezone
7
+ from django.utils.dateparse import parse_datetime
7
8
 
8
9
  # Alliance Auth
9
10
  from allianceauth.services.hooks import get_extension_logger
10
11
  from esi.exceptions import HTTPNotModified
11
12
 
12
13
  # Alliance Auth (External Libs)
13
- from app_utils.logging import LoggerAddTag
14
14
  from eveuniverse.models import EvePlanet, EveType
15
15
 
16
16
  # AA Ledger
17
17
  from ledger import __title__
18
- from ledger.constants import COMMAND_CENTER, EXTRACTOR_CONTROL_UNIT, SPACEPORTS
18
+ from ledger.app_settings import LEDGER_BULK_BATCH_SIZE
19
19
  from ledger.decorators import log_timing
20
- from ledger.models.characteraudit import CharacterAudit
21
- from ledger.providers import esi
20
+ from ledger.models.characteraudit import CharacterOwner
21
+ from ledger.models.helpers.update_manager import CharacterUpdateSection
22
+ from ledger.providers import AppLogger, esi
22
23
 
23
24
  if TYPE_CHECKING: # pragma: no cover
24
25
  # Alliance Auth
@@ -27,9 +28,10 @@ if TYPE_CHECKING: # pragma: no cover
27
28
 
28
29
  # AA Ledger
29
30
  from ledger.models.general import UpdateSectionResult
30
- from ledger.models.planetary import CharacterPlanet, CharacterPlanetDetails
31
+ from ledger.models.planetary import CharacterPlanet as PlanetContext
32
+ from ledger.models.planetary import CharacterPlanetDetails as PlanetDetailsContext
31
33
 
32
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
34
+ logger = AppLogger(get_extension_logger(__name__), __title__)
33
35
 
34
36
 
35
37
  def to_json_serializable(data):
@@ -44,50 +46,46 @@ def to_json_serializable(data):
44
46
  return data
45
47
 
46
48
 
47
- class PlanetaryQuerySet(models.QuerySet):
48
- pass
49
-
50
-
51
- class PlanetaryManagerBase(models.Manager):
49
+ class CharacterPlanetManager(models.Manager["PlanetContext"]):
52
50
  @log_timing(logger)
53
51
  def update_or_create_esi(
54
- self, character: "CharacterAudit", force_refresh: bool = False
52
+ self, owner: CharacterOwner, force_refresh: bool = False
55
53
  ) -> "UpdateSectionResult":
56
54
  """Update or Create a planets entry from ESI data."""
57
- return character.update_section_if_changed(
58
- section=character.UpdateSection.PLANETS,
55
+ return owner.update_manager.update_section_if_changed(
56
+ section=CharacterUpdateSection.PLANETS,
59
57
  fetch_func=self._fetch_esi_data,
60
58
  force_refresh=force_refresh,
61
59
  )
62
60
 
63
61
  def _fetch_esi_data(
64
- self, audit: "CharacterAudit", force_refresh: bool = False
62
+ self, owner: CharacterOwner, force_refresh: bool = False
65
63
  ) -> None:
66
64
  """Fetch planetary entries from ESI data."""
67
65
  req_scopes = ["esi-planets.manage_planets.v1"]
68
- token = audit.get_token(scopes=req_scopes)
66
+ token = owner.get_token(scopes=req_scopes)
69
67
 
70
68
  # Make the ESI request
71
69
  operation = esi.client.Planetary_Interaction.GetCharactersCharacterIdPlanets(
72
- character_id=audit.eve_character.character_id,
70
+ character_id=owner.eve_character.character_id,
73
71
  token=token,
74
72
  )
75
73
 
76
74
  planets_items = operation.results(force_refresh=force_refresh)
77
75
 
78
- self._update_or_create_objs(character=audit, objs=planets_items)
76
+ self._update_or_create_objs(owner=owner, objs=planets_items)
79
77
 
80
78
  @transaction.atomic()
81
79
  def _update_or_create_objs(
82
- self, character: "CharacterAudit", objs: list["PlanetGetItem"]
80
+ self, owner: CharacterOwner, objs: list["PlanetGetItem"]
83
81
  ) -> None:
84
82
  """Update or Create planets entries from objs data."""
85
83
  # pylint: disable=import-outside-toplevel
86
84
  # AA Ledger
87
85
  from ledger.models.planetary import CharacterPlanetDetails
88
86
 
89
- _current_planets = self.filter(character=character).values_list(
90
- "planet_id", flat=True
87
+ _current_planets = self.filter(character=owner).values_list(
88
+ "eve_planet_id", flat=True
91
89
  )
92
90
 
93
91
  _planets_ids = []
@@ -99,16 +97,16 @@ class PlanetaryManagerBase(models.Manager):
99
97
  _planets_ids.append(eve_planet.id)
100
98
 
101
99
  try:
102
- e_planet = self.get(character=character, planet=eve_planet)
100
+ e_planet = self.get(character=owner, eve_planet=eve_planet)
103
101
  prim_key = e_planet.id
104
102
  except self.model.DoesNotExist:
105
103
  prim_key = None
106
104
 
107
105
  planet = self.model(
108
106
  id=prim_key,
109
- character=character,
110
- planet=eve_planet,
111
- planet_name=eve_planet.name,
107
+ name=eve_planet.name,
108
+ character=owner,
109
+ eve_planet=eve_planet,
112
110
  upgrade_level=planet.upgrade_level,
113
111
  num_pins=planet.num_pins,
114
112
  )
@@ -119,45 +117,32 @@ class PlanetaryManagerBase(models.Manager):
119
117
  _planets_update.append(planet)
120
118
 
121
119
  if _planets_new:
122
- self.bulk_create(_planets_new)
120
+ self.bulk_create(_planets_new, batch_size=LEDGER_BULK_BATCH_SIZE)
123
121
  if _planets_update:
124
- self.bulk_update(_planets_update, fields=["upgrade_level", "num_pins"])
122
+ self.bulk_update(
123
+ _planets_update,
124
+ fields=["upgrade_level", "num_pins"],
125
+ batch_size=LEDGER_BULK_BATCH_SIZE,
126
+ )
125
127
 
126
128
  # Delete Planets that are no longer in the list
127
129
  obsolete_planets = set(_current_planets) - set(_planets_ids)
128
- self.filter(character=character, planet_id__in=obsolete_planets).delete()
130
+ self.filter(character=owner, eve_planet_id__in=obsolete_planets).delete()
129
131
  # Delete Planet Details that are no longer in the list
130
132
  CharacterPlanetDetails.objects.filter(
131
- planet__character=character, planet__planet_id__in=obsolete_planets
133
+ planet__character=owner, planet__eve_planet_id__in=obsolete_planets
132
134
  ).delete()
133
135
 
134
136
 
135
- PlanetaryManager = PlanetaryManagerBase.from_queryset(PlanetaryQuerySet)
136
-
137
-
138
- class PlanetaryDetailsQuerySet(models.QuerySet):
139
- def get_or_create_facilitys(self, planet: "CharacterPlanet"):
140
- """Get or Create Facilitys for a given Planet"""
141
- return self._get_or_create_facilitys(planet=planet)
142
-
143
- def _get_or_create_facilitys(self, planet: "CharacterPlanet"):
144
- try:
145
- facilitys = self.get(planet=planet)
146
- facility = facilitys.facilitys
147
- created = False
148
- return facility, created
149
- except self.model.DoesNotExist:
150
- facility, created = self.update_or_create_facilitys(planet=planet)
151
- return facility, created
152
-
137
+ class PlanetDetailsQuerySet(models.QuerySet):
153
138
  def update_or_create_layout(
154
139
  self,
155
- character: CharacterAudit,
156
- planet: "CharacterPlanetDetails",
140
+ owner: CharacterOwner,
141
+ planet: "PlanetDetailsContext",
157
142
  objs: list["PlanetDetailsItem"],
158
143
  ):
159
144
  """Update or Create Layout for a given Planet"""
160
- return self._update_or_create(character=character, planet=planet, objs=objs)
145
+ return self._update_or_create(owner=owner, planet=planet, objs=objs)
161
146
 
162
147
  def _convert_to_dict(
163
148
  self,
@@ -171,13 +156,14 @@ class PlanetaryDetailsQuerySet(models.QuerySet):
171
156
 
172
157
  def _update_or_create(
173
158
  self,
174
- character: "CharacterAudit",
175
- planet: "CharacterPlanetDetails",
159
+ owner: CharacterOwner,
160
+ planet: "PlanetDetailsContext",
176
161
  objs: list["PlanetDetailsItem"],
177
- ):
162
+ ) -> tuple["PlanetDetailsContext", bool]:
178
163
  """Update or Create Layout for a given Planet"""
179
164
  if not isinstance(objs, list):
180
165
  objs = [objs]
166
+
181
167
  for result in objs:
182
168
  links = self._convert_to_dict(result.links)
183
169
  pins = self._convert_to_dict(result.pins)
@@ -185,66 +171,147 @@ class PlanetaryDetailsQuerySet(models.QuerySet):
185
171
 
186
172
  planetdetails, created = self.update_or_create(
187
173
  planet=planet,
188
- character=character,
174
+ character=owner,
189
175
  defaults={
190
176
  "links": links,
191
177
  "pins": pins,
192
178
  "routes": routes,
193
- "facilitys": None,
179
+ "factories": None,
194
180
  },
195
181
  )
196
182
 
197
- self._update_facility(planetdetails)
183
+ self._update_factory(planetdetails)
198
184
 
199
- logger.debug("Planet %s Facilitys Updated", planetdetails)
185
+ logger.debug("Planet %s Factories Updated", planetdetails)
200
186
 
201
187
  return planetdetails, created
202
188
 
203
- def _update_facility(self, planet: "CharacterPlanetDetails"):
204
- facility_info = self.get_facility_info(planet)
205
- planet.facilitys = facility_info
206
- planet.save()
207
- return planet
189
+ def _update_factory(self, planet: "PlanetDetailsContext"):
208
190
 
209
- # TODO make code easier or split it to peaces for better readable
210
- def get_facility_info(self, planet: "CharacterPlanetDetails"):
211
- facility_info = {}
191
+ factory_dict = {}
212
192
 
213
193
  # Process pins
214
194
  for pin in planet.pins:
215
195
  pin_id = pin["pin_id"]
216
196
  item_type, _ = EveType.objects.get_or_create_esi(id=pin["type_id"])
217
- if (
218
- pin["type_id"] in SPACEPORTS
219
- or pin["type_id"] in EXTRACTOR_CONTROL_UNIT
220
- or pin["type_id"] in COMMAND_CENTER
197
+
198
+ # Skip Command Center
199
+ if pin["type_id"] in EveType.objects.filter(eve_group_id=1027).values_list(
200
+ "id", flat=True
221
201
  ):
222
202
  continue
223
- facility_info[pin_id] = {
203
+
204
+ # Create Facility Pin Entry
205
+ factory_dict[pin_id] = {
224
206
  "facility_id": pin["type_id"],
225
207
  "facility_name": item_type.name,
226
- "resources": [],
227
- "storage": {
228
- content["type_id"]: content["amount"]
229
- for content in pin.get("contents", [])
230
- },
208
+ "facility_type": (
209
+ item_type.eve_group.name if item_type.eve_group else "N/A"
210
+ ),
211
+ "ressources": [],
231
212
  }
232
213
 
233
- self._facility_production_chain(planet, facility_info)
214
+ # Spaceport and Storage Facility
215
+ if pin["type_id"] in EveType.objects.filter(
216
+ eve_group_id__in=[1029, 1030]
217
+ ).values_list("id", flat=True):
218
+ factory_dict[pin_id]["storage"] = {}
219
+ self._storage_info(pin, factory_dict[pin_id])
234
220
 
235
- self._facility_produce_depend(planet, facility_info)
221
+ # Extractor
222
+ if pin["type_id"] in EveType.objects.filter(eve_group_id=1063).values_list(
223
+ "id", flat=True
224
+ ):
225
+ factory_dict[pin_id]["extractor"] = {}
226
+ self._extractor_info(pin, factory_dict[pin_id])
227
+
228
+ self._factory_production_chain(planet, factory_dict)
229
+
230
+ planet.factories = factory_dict
231
+ planet.save()
232
+ return planet
233
+
234
+ def _storage_info(self, pin: dict, facility_dict: dict):
235
+ """Update per-facility dict and add all Storage Information."""
236
+ for content in pin.get("contents", []):
237
+ item_type, _ = EveType.objects.get_or_create_esi(id=content["type_id"])
236
238
 
237
- return facility_info
239
+ item_type_id = content["type_id"]
240
+ item_amount = content["amount"]
238
241
 
239
- def _facility_production_chain(
240
- self, planet: "CharacterPlanetDetails", facility_info: dict
242
+ if item_type_id in facility_dict["storage"]:
243
+ facility_dict["storage"][item_type_id]["amount"] += item_amount
244
+ else:
245
+ facility_dict["storage"][item_type_id] = {
246
+ "item_id": item_type_id,
247
+ "item_name": item_type.name,
248
+ "amount": item_amount,
249
+ }
250
+ return facility_dict
251
+
252
+ def _extractor_info(self, pin: dict, factory_dict: dict):
253
+ """Update dict and add all Extractor Information to the given dict"""
254
+ # Update Extractor Information (ensure None is treated as empty dict)
255
+ extractor_details = pin.get("extractor_details") or {}
256
+ product_type_id = extractor_details.get("product_type_id")
257
+ product_type_name = "N/A"
258
+
259
+ # Get Product Eve Type
260
+ if product_type_id:
261
+ item_type, _ = EveType.objects.get_or_create_esi(id=product_type_id)
262
+ product_type_name = item_type.name
263
+
264
+ # Use timezone-aware datetimes and compute elapsed/total seconds
265
+ current_time = timezone.now()
266
+
267
+ install_time = None
268
+ if pin.get("install_time") is not None:
269
+ install_time = parse_datetime(pin.get("install_time"))
270
+ expiry_time = None
271
+ if pin.get("expiry_time") is not None:
272
+ expiry_time = parse_datetime(pin.get("expiry_time"))
273
+
274
+ # Create Extractor Info Entry on the per-facility dict
275
+ factory_dict["extractor"] = {
276
+ "head_count": extractor_details.get("head_count"),
277
+ "product_type_id": product_type_id,
278
+ "product_type_name": product_type_name,
279
+ "install_time": str(install_time),
280
+ "expiry_time": str(expiry_time),
281
+ "cycle_time": extractor_details.get("cycle_time"),
282
+ "progress_percentage": None,
283
+ }
284
+
285
+ is_running = False
286
+ # Calculate progress as percentage (0-100) based on elapsed time
287
+ if install_time and expiry_time:
288
+ total_seconds = (expiry_time - install_time).total_seconds()
289
+ if total_seconds > 0:
290
+ elapsed_seconds = (current_time - install_time).total_seconds()
291
+ progress = (elapsed_seconds / total_seconds) * 100.0
292
+ progress = max(0.0, min(progress, 100.0))
293
+ factory_dict["extractor"]["progress_percentage"] = round(progress, 2)
294
+
295
+ # Extractor is running
296
+ if progress < 100.0:
297
+ is_running = True
298
+
299
+ # Store is_running status
300
+ factory_dict["extractor"]["is_running"] = is_running
301
+
302
+ # pylint: disable=too-many-locals
303
+ def _factory_production_chain(
304
+ self, planet: "PlanetDetailsContext", factory_dict: dict
241
305
  ):
242
306
  """Update dict and add all Production Information to the given dict"""
307
+ item_ids = set()
308
+ for facility in factory_dict.values():
309
+ for storage_item in facility.get("storage", {}).values():
310
+ item_ids.add(storage_item["item_id"])
243
311
 
244
- extractors = planet.get_extractors_info()
245
- extractors_item_ids = [
246
- extractor["item_id"] for extractor in extractors.values()
247
- ]
312
+ extractor_product = facility.get("extractor", {}).get("product_type_id")
313
+ if extractor_product:
314
+ item_ids.add(extractor_product)
248
315
 
249
316
  for route in planet.routes:
250
317
  destination_pin_id = route["destination_pin_id"]
@@ -253,69 +320,54 @@ class PlanetaryDetailsQuerySet(models.QuerySet):
253
320
  id=route["content_type_id"]
254
321
  )
255
322
 
256
- if destination_pin_id in facility_info:
323
+ if destination_pin_id in factory_dict:
257
324
  req_quantity = route["quantity"]
258
- current_quantity = facility_info[destination_pin_id]["storage"].get(
259
- content_type.id, 0
260
- )
261
- missing_quantity = req_quantity - current_quantity
325
+ storage = factory_dict[destination_pin_id].get("storage", {})
326
+ current_quantity = storage.get(content_type.id, {}).get("amount", 0)
327
+ missing_quantity = max((req_quantity - current_quantity), 0)
328
+
262
329
  still_producing = (
263
- content_type.id in extractors_item_ids
264
- if not planet.is_expired
265
- else False
330
+ content_type.id in item_ids if not planet.is_expired else False
266
331
  )
267
332
 
333
+ is_active = still_producing and missing_quantity > 0
334
+
268
335
  resource = {
269
336
  "item_id": content_type.id,
270
337
  "item_name": content_type.name,
271
338
  "req_quantity": req_quantity,
272
339
  "current_quantity": current_quantity,
273
- "missing_quantity": max(missing_quantity, 0),
274
- "still_producing": still_producing,
340
+ "missing_quantity": missing_quantity,
341
+ "is_active": is_active,
275
342
  }
276
- facility_info[destination_pin_id]["resources"].append(resource)
343
+ factory_dict[destination_pin_id]["ressources"].append(resource)
277
344
 
278
- if source_pin_id in facility_info:
279
- facility_info[source_pin_id]["output_product"] = {
345
+ if source_pin_id in factory_dict:
346
+ factory_dict[source_pin_id]["output_product"] = {
280
347
  "item_id": content_type.id,
281
348
  "item_name": content_type.name,
282
349
  "output_quantity": route["quantity"],
283
350
  }
284
- return facility_info
285
-
286
- def _facility_produce_depend(self, planet, facility_info):
287
- """Check if Facility is Producing"""
288
- for facility in facility_info.values():
289
- for resource in facility["resources"]:
290
- self._facility_still_producing(planet, resource, facility_info)
291
- return facility_info
292
-
293
- def _facility_still_producing(self, planet, resource, facility_info) -> bool:
294
- """Check if ressource exist in Production Chain and change State"""
295
- for other_facility in facility_info.values():
296
- if (
297
- "output_product" in other_facility
298
- and other_facility["output_product"]["item_id"] == resource["item_id"]
299
- and not planet.is_expired
300
- ):
301
- if other_facility["output_product"]["output_quantity"] > 0:
302
- resource["still_producing"] = True
351
+ return factory_dict
352
+
303
353
 
354
+ class PlanetDetailsManager(models.Manager["PlanetDetailsContext"]):
355
+ def get_queryset(self):
356
+ return PlanetDetailsQuerySet(self.model, using=self._db)
304
357
 
305
- class PlanetaryDetailsManagerBase(models.Manager):
306
358
  @log_timing(logger)
307
359
  def update_or_create_esi(
308
- self, character: "CharacterAudit", force_refresh: bool = False
360
+ self, owner: CharacterOwner, force_refresh: bool = False
309
361
  ) -> "UpdateSectionResult":
310
362
  """Update or Create a planets details entry from ESI data."""
311
- return character.update_section_if_changed(
312
- section=character.UpdateSection.PLANETS_DETAILS,
363
+ return owner.update_manager.update_section_if_changed(
364
+ section=CharacterUpdateSection.PLANETS_DETAILS,
313
365
  fetch_func=self._fetch_esi_data,
314
366
  force_refresh=force_refresh,
315
367
  )
316
368
 
317
369
  def _fetch_esi_data(
318
- self, audit: "CharacterAudit", force_refresh: bool = False
370
+ self, owner: CharacterOwner, force_refresh: bool = False
319
371
  ) -> None:
320
372
  """Fetch planets details entries from ESI data."""
321
373
  # pylint: disable=import-outside-toplevel
@@ -324,17 +376,17 @@ class PlanetaryDetailsManagerBase(models.Manager):
324
376
 
325
377
  req_scopes = ["esi-planets.manage_planets.v1"]
326
378
 
327
- token = audit.get_token(scopes=req_scopes)
379
+ token = owner.get_token(scopes=req_scopes)
328
380
 
329
- planets_ids = CharacterPlanet.objects.filter(character=audit).values_list(
330
- "planet_id", flat=True
381
+ planets_ids = CharacterPlanet.objects.filter(character=owner).values_list(
382
+ "eve_planet_id", flat=True
331
383
  )
332
384
  is_updated = False
333
385
 
334
386
  for planet_id in planets_ids:
335
387
  # Make the ESI request
336
388
  operation = esi.client.Planetary_Interaction.GetCharactersCharacterIdPlanetsPlanetId(
337
- character_id=audit.eve_character.character_id,
389
+ character_id=owner.eve_character.character_id,
338
390
  planet_id=planet_id,
339
391
  token=token,
340
392
  )
@@ -346,7 +398,7 @@ class PlanetaryDetailsManagerBase(models.Manager):
346
398
  continue
347
399
 
348
400
  self._update_or_create_objs(
349
- character=audit,
401
+ owner=owner,
350
402
  objs=planets_details_items,
351
403
  planet_id=planet_id,
352
404
  )
@@ -357,7 +409,7 @@ class PlanetaryDetailsManagerBase(models.Manager):
357
409
  @transaction.atomic()
358
410
  def _update_or_create_objs(
359
411
  self,
360
- character: "CharacterAudit",
412
+ owner: CharacterOwner,
361
413
  objs: list["PlanetDetailsItem"],
362
414
  planet_id: int,
363
415
  ) -> None:
@@ -368,18 +420,18 @@ class PlanetaryDetailsManagerBase(models.Manager):
368
420
 
369
421
  try:
370
422
  character_planet = CharacterPlanet.objects.get(
371
- character=character, planet_id=planet_id
423
+ character=owner, eve_planet_id=planet_id
372
424
  )
373
425
  except CharacterPlanet.DoesNotExist:
374
426
  logger.warning(
375
427
  "Planet %s not found for character %s",
376
428
  planet_id,
377
- character.eve_character.character_name,
429
+ owner.eve_character.character_name,
378
430
  )
379
431
  return
380
432
 
381
- planet_details, created = self.update_or_create_layout(
382
- character=character,
433
+ planet_details, created = self.get_queryset().update_or_create_layout(
434
+ owner=owner,
383
435
  planet=character_planet,
384
436
  objs=objs,
385
437
  )
@@ -389,7 +441,7 @@ class PlanetaryDetailsManagerBase(models.Manager):
389
441
  if planet_details.is_expired and planet_details.last_alert is None:
390
442
  logger.debug(
391
443
  "Planet %s Extractor Heads Expired for: %s",
392
- planet_details.planet.planet.name,
444
+ planet_details.planet.eve_planet.name,
393
445
  planet_details.planet.character.eve_character.character_name,
394
446
  )
395
447
  planet_details.last_alert = timezone.now()
@@ -403,14 +455,9 @@ class PlanetaryDetailsManagerBase(models.Manager):
403
455
  logger.debug(
404
456
  "Notification Reseted for %s Planet: %s",
405
457
  planet_details.planet.character.eve_character.character_name,
406
- planet_details.planet.planet.name,
458
+ planet_details.planet.eve_planet.name,
407
459
  )
408
460
  planet_details.last_alert = None
409
461
  planet_details.notification_sent = False
410
462
 
411
463
  planet_details.save()
412
-
413
-
414
- PlanetaryDetailsManager = PlanetaryDetailsManagerBase.from_queryset(
415
- PlanetaryDetailsQuerySet
416
- )