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
@@ -1,452 +0,0 @@
1
- # Standard Library
2
- import csv
3
- import gc
4
- import tempfile
5
- import zipfile
6
- from abc import ABC, abstractmethod
7
- from decimal import Decimal
8
- from pathlib import Path
9
-
10
- # Django
11
- from django.conf import settings
12
- from django.utils import timezone
13
-
14
- # Alliance Auth
15
- from allianceauth.eveonline.models import EveAllianceInfo
16
- from allianceauth.services.hooks import get_extension_logger
17
-
18
- # Alliance Auth (External Libs)
19
- from app_utils.logging import LoggerAddTag
20
-
21
- # AA Ledger
22
- from ledger import __title__
23
- from ledger.helpers.alliance import AllianceData
24
- from ledger.helpers.corporation import CorporationData
25
- from ledger.models.corporationaudit import (
26
- CorporationAudit,
27
- CorporationWalletJournalEntry,
28
- )
29
-
30
- logger = LoggerAddTag(get_extension_logger(__name__), __title__)
31
-
32
-
33
- def int_or_none(val) -> int | None:
34
- """Convert value to int or return None."""
35
- if val is None or val == "":
36
- return None
37
- try:
38
- return int(val)
39
- except (ValueError, TypeError):
40
- return None
41
-
42
-
43
- def file_to_zip(source_file: Path, destination: Path) -> Path:
44
- """Create a zip archive from a file."""
45
- destination.mkdir(parents=True, exist_ok=True)
46
- zip_file = (destination / source_file.name).with_suffix(".zip")
47
- with zipfile.ZipFile(
48
- file=zip_file, mode="w", compression=zipfile.ZIP_DEFLATED
49
- ) as my_zip:
50
- my_zip.write(filename=source_file, arcname=source_file.name)
51
- logger.info("Created export file: %s", zip_file)
52
- return zip_file
53
-
54
-
55
- def default_destination() -> Path:
56
- """Return default destination path."""
57
- return (
58
- Path(settings.BASE_DIR) / str(CorporationAudit._meta.app_label) / "data_exports"
59
- )
60
-
61
-
62
- def export_ledger_to_archive(
63
- ledger_type: str, entity_id: int, division_id: int, year: int, month: int
64
- ) -> str:
65
- """Export data for given ledger into a zipped file in destination."""
66
- exporter = LedgerCSVExporter.create_exporter(
67
- ledger_type, entity_id, division_id, year, month
68
- )
69
- try:
70
- with tempfile.TemporaryDirectory() as temp_dirname:
71
- csv_file = exporter.write_to_file(temp_dirname)
72
- destination = default_destination()
73
- zip_file_path = file_to_zip(csv_file, destination)
74
- except ValueError as ve:
75
- logger.debug("Error exporting ledger to archive: %s", ve)
76
- return f"{ve}"
77
- gc.collect()
78
- return str(zip_file_path)
79
-
80
-
81
- class LedgerCSVExporter(ABC):
82
- """CSV exporter for ledger data."""
83
-
84
- def __init__(self):
85
- # _now is available for subclasses if needed
86
- self._now = timezone.now()
87
- if not hasattr(self, "topic"):
88
- raise NotImplementedError("Subclasses must define 'topic' attribute")
89
-
90
- @property
91
- def output_basename(self) -> Path:
92
- """Return the base name for the output file."""
93
- app_label = str(CorporationAudit._meta.app_label)
94
- # Try to discover an entity id from known attributes set by exporters
95
- entity_id = (
96
- getattr(self, "corporation_id", None)
97
- or getattr(self, "alliance_id", None)
98
- or ""
99
- )
100
- division_id = getattr(self, "division_id", None) or ""
101
- year = getattr(self, "year", "") or ""
102
- month = getattr(self, "month", "") or ""
103
-
104
- export_key = self.encoder(entity_id, division_id, year, month)
105
- if not export_key:
106
- raise ValueError("Could not create export key for output basename")
107
- # pylint: disable=no-member
108
- return Path(f"{app_label}_{self.topic}_{export_key}")
109
-
110
- @staticmethod
111
- def encoder(
112
- entity_id: int, division_id: int = None, year: int = None, month: int = None
113
- ) -> str | None:
114
- """Create a compact export key: "<entity_id>:<division_id>:<year>:<month>" encoded as hex"""
115
- try:
116
- parts = [
117
- "" if v is None else str(v)
118
- for v in (entity_id, division_id, year, month)
119
- ]
120
- key = ":".join(parts)
121
- return key.encode("utf-8").hex()
122
- # pylint: disable=broad-except
123
- except Exception:
124
- return None
125
-
126
- @staticmethod
127
- def decoder(key: str) -> tuple[int | None, int | None, int | None, int | None]:
128
- """Decode a compact export key from hex to (entity_id, division_id, year, month).
129
-
130
- Returns ints when values are numeric, otherwise None for missing/empty values.
131
- """
132
- try:
133
- packed = bytes.fromhex(key).decode("utf-8")
134
- key_parts = packed.split(":")
135
-
136
- entity_id = int_or_none(key_parts[0]) if len(key_parts) > 0 else None
137
- division_id = int_or_none(key_parts[1]) if len(key_parts) > 1 else None
138
- year = int_or_none(key_parts[2]) if len(key_parts) > 2 else None
139
- month = int_or_none(key_parts[3]) if len(key_parts) > 3 else None
140
- return entity_id, division_id, year, month
141
- # pylint: disable=broad-except
142
- except Exception:
143
- return None, None, None, None
144
-
145
- @abstractmethod
146
- def create_data_export(self) -> list[dict]:
147
- """Generate data export."""
148
- raise NotImplementedError()
149
-
150
- @property
151
- @abstractmethod
152
- def has_data(self) -> bool:
153
- """Check if there is data to export."""
154
- raise NotImplementedError()
155
-
156
- def output_path(self, destination: str) -> Path:
157
- """Return output path for this export."""
158
- return Path(destination) / self.output_basename.with_suffix(".csv")
159
-
160
- def _extract_entity_info(self, item: dict) -> dict:
161
- """Extract entity information from the data item."""
162
- entity = item.get("entity")
163
- if not entity:
164
- return {"entity_id": "", "entity_name": "", "entity_type": ""}
165
-
166
- return {
167
- "entity_id": getattr(entity, "entity_id", ""),
168
- "entity_name": getattr(entity, "entity_name", ""),
169
- "entity_type": getattr(entity, "type", ""),
170
- }
171
-
172
- def _clean_decimal_value(self, value) -> str:
173
- """Clean decimal values from the Decimal() wrapper."""
174
- if (
175
- isinstance(value, str)
176
- and value.startswith('Decimal("')
177
- and value.endswith('")')
178
- ):
179
- # Extract the value from Decimal("value") format
180
- return value[9:-2] # Remove 'Decimal("' and '")')
181
- if isinstance(value, Decimal):
182
- return str(value)
183
- return str(value) if value is not None else "0"
184
-
185
- def _extract_ledger_data(self, item: dict) -> dict:
186
- """Extract ledger financial data from the item."""
187
- ledger = item.get("ledger", {})
188
-
189
- return {
190
- "bounty": self._clean_decimal_value(ledger.get("bounty", "0")),
191
- "ess": self._clean_decimal_value(ledger.get("ess", "0")),
192
- "miscellaneous": self._clean_decimal_value(
193
- ledger.get("miscellaneous", "0")
194
- ),
195
- "costs": self._clean_decimal_value(ledger.get("costs", "0")),
196
- "total": self._clean_decimal_value(ledger.get("total", "0")),
197
- "mining": self._clean_decimal_value(
198
- ledger.get("mining", "0")
199
- ), # Include mining if present
200
- }
201
-
202
- def write_to_file(self, destination: Path) -> Path:
203
- """Write ledger data to CSV file."""
204
- output_file = self.output_path(destination)
205
-
206
- # Define CSV headers
207
- headers = [
208
- "entity_id",
209
- "entity_name",
210
- "entity_type",
211
- "bounty",
212
- "ess",
213
- "miscellaneous",
214
- "costs",
215
- "mining",
216
- "total",
217
- ]
218
-
219
- if self.has_data is False:
220
- raise ValueError("No data to export")
221
-
222
- with output_file.open("w", newline="", encoding="utf-8") as csv_file:
223
- writer = csv.DictWriter(csv_file, fieldnames=headers)
224
- writer.writeheader()
225
-
226
- for item in self.create_data_export():
227
- if not item: # Skip empty items
228
- continue
229
-
230
- # Extract entity and ledger information
231
- entity_info = self._extract_entity_info(item)
232
- ledger_data = self._extract_ledger_data(item)
233
-
234
- # Combine all data into a single row
235
- row_data = {**entity_info, **ledger_data}
236
-
237
- writer.writerow(row_data)
238
- logger.info(
239
- f"Exported {len(self.create_data_export())} ledger entries to {output_file}"
240
- )
241
- return output_file
242
-
243
- @classmethod
244
- # pylint: disable=too-many-positional-arguments
245
- def create_exporter(
246
- cls,
247
- ledger_type: str,
248
- entity_id: int,
249
- division_id: int = None,
250
- year: int = None,
251
- month: int = None,
252
- ):
253
- """Create a LedgerCSVExporter instance."""
254
- if ledger_type == "corporation":
255
- if entity_id is None:
256
- raise ValueError("corporation_id must be provided")
257
- return CorporationExporter(
258
- entity_id=entity_id, division_id=division_id, year=year, month=month
259
- )
260
-
261
- if ledger_type == "alliance":
262
- if entity_id is None:
263
- raise ValueError("alliance_id must be provided")
264
- return AllianceExporter(entity_id=entity_id, year=year, month=month)
265
-
266
- raise ValueError("Invalid ledger_type")
267
-
268
- def gather_export_files(self) -> list[dict]:
269
- """Gather export files in the destination folder and parse metadata from filenames.
270
- Expected filename format (without suffix):
271
- <app_label>_<topic>_<export_key_hex>
272
-
273
- The export_key_hex is a hex encoding of the UTF-8 string
274
- "<entity_id>:<division_id>:<year>:<month>"
275
- Only files matching this new format are considered.
276
-
277
- This is an instance method and will filter files for the exporter instance
278
- by using the instance's corporation_id or alliance_id attribute.
279
- """
280
- # Determine the entity id for this exporter instance. Exporter subclasses
281
- instance_entity_id = getattr(self, "corporation_id", None) or getattr(
282
- self, "alliance_id", None
283
- )
284
- if instance_entity_id is None:
285
- raise ValueError(
286
- "Exporter instance must have 'corporation_id' or 'alliance_id' to gather files"
287
- )
288
-
289
- destination_folder = default_destination()
290
- results: list[dict] = []
291
-
292
- # If destination folder doesn't exist, return empty
293
- if not destination_folder.exists():
294
- return results
295
-
296
- existing_files = list(
297
- destination_folder.glob(f"{str(CorporationAudit._meta.app_label)}_*.zip")
298
- )
299
-
300
- if not existing_files:
301
- return results
302
-
303
- for file in existing_files:
304
- name = file.with_suffix("").name
305
- parts = name.split("_")
306
- topic = parts[1] if len(parts) > 1 else ""
307
-
308
- # Only attempt to parse the export hex key (third segment).
309
- if len(parts) <= 2:
310
- continue
311
-
312
- key = parts[2]
313
- entity_id, division_id, year, month = LedgerCSVExporter.decoder(key)
314
-
315
- # Only include files matching the requested entity id and topic
316
- if entity_id is None or entity_id != instance_entity_id:
317
- continue
318
-
319
- # Also filter by exporter topic so we don't mix corporation/alliance exports
320
- if topic != getattr(self, "topic", ""):
321
- continue
322
-
323
- try:
324
- last_updated = timezone.datetime.fromtimestamp(
325
- file.stat().st_mtime, tz=timezone.utc
326
- )
327
- # pylint: disable=broad-except
328
- except Exception:
329
- logger.debug("Could not get last modified time for file %s", file)
330
- last_updated = None
331
-
332
- results.append(
333
- {
334
- "topic": topic,
335
- "entity_id": entity_id,
336
- "division_id": (
337
- division_id if division_id is not None else "All Divisions"
338
- ),
339
- "year": year,
340
- "month": month if month is not None else "All Months",
341
- "last_updated": last_updated,
342
- "hash": key,
343
- }
344
- )
345
-
346
- return results
347
-
348
-
349
- class CorporationExporter(LedgerCSVExporter):
350
- """CSV Exporter for Corporation Ledger Data."""
351
-
352
- topic = "corporation-ledger"
353
-
354
- def __init__(
355
- self,
356
- entity_id: int,
357
- division_id: int = None,
358
- year: int = None,
359
- month: int = None,
360
- ):
361
- super().__init__()
362
- self.corporation_id = entity_id
363
- self.division_id = division_id
364
- self.year = year
365
- self.month = month
366
-
367
- @property
368
- def has_data(self) -> bool:
369
- """Check if there is data to export."""
370
- return CorporationWalletJournalEntry.objects.filter(
371
- division__corporation__corporation__corporation_id=self.corporation_id
372
- ).exists()
373
-
374
- def create_data_export(
375
- self,
376
- corporation_id: int = None,
377
- division_id: int = None,
378
- year: int = None,
379
- month: int = None,
380
- ) -> list[dict]:
381
- """Generate data export for corporation ledger."""
382
- report_corporation_id = (
383
- corporation_id if corporation_id is not None else self.corporation_id
384
- )
385
- report_division_id = (
386
- division_id if division_id is not None else self.division_id
387
- )
388
- report_year = year if year is not None else self.year
389
- report_month = month if month is not None else self.month
390
-
391
- try:
392
- corporation = CorporationAudit.objects.get(
393
- corporation__corporation_id=report_corporation_id
394
- )
395
- # Create CorporationData inside the task
396
- ledger_data = CorporationData(
397
- corporation=corporation,
398
- division_id=report_division_id,
399
- year=report_year,
400
- month=report_month,
401
- )
402
- return ledger_data.generate_data_export()
403
- except CorporationAudit.DoesNotExist as exc:
404
- raise ValueError("Corporation not found") from exc
405
-
406
-
407
- class AllianceExporter(LedgerCSVExporter):
408
- """CSV Exporter for Alliance Ledger Data."""
409
-
410
- topic = "alliance-ledger"
411
-
412
- def __init__(self, entity_id: int, year: int = None, month: int = None):
413
- super().__init__()
414
- self.alliance_id = entity_id
415
- self.year = year
416
- self.month = month
417
-
418
- @property
419
- def has_data(self) -> bool:
420
- """Check if there is data to export."""
421
- corporations = CorporationAudit.objects.filter(
422
- corporation__alliance__alliance_id=self.alliance_id
423
- ).values_list("corporation__corporation_id", flat=True)
424
-
425
- if not corporations:
426
- return False
427
-
428
- return CorporationWalletJournalEntry.objects.filter(
429
- division__corporation__corporation__corporation_id__in=corporations
430
- ).exists()
431
-
432
- def create_data_export(
433
- self, alliance_id: int = None, year: int = None, month: int = None
434
- ) -> list[dict]:
435
- """Generate data export for alliance ledger."""
436
- report_alliance_id = (
437
- alliance_id if alliance_id is not None else self.alliance_id
438
- )
439
- report_year = year if year is not None else self.year
440
- report_month = month if month is not None else self.month
441
-
442
- try:
443
- alliance = EveAllianceInfo.objects.get(alliance_id=report_alliance_id)
444
- ledger_data = AllianceData(
445
- alliance=alliance,
446
- year=report_year,
447
- month=report_month,
448
- request=None,
449
- )
450
- return ledger_data.generate_data_export()
451
- except EveAllianceInfo.DoesNotExist as exc:
452
- raise ValueError("Alliance not found") from exc
@@ -1,66 +0,0 @@
1
- $(document).ready(() => {
2
- /* global PlanetaryTable */
3
- const modalRequestApprove = $('#ledger-planetary-confirm');
4
- const modalErrorMessage = $('#modal-error-message');
5
-
6
- // Approve Request Modal
7
- modalRequestApprove.on('show.bs.modal', (event) => {
8
- const button = $(event.relatedTarget);
9
- const url = button.data('action');
10
-
11
- // Extract the title from the button
12
- const modalTitle = button.data('title');
13
- const modalTitleDiv = modalRequestApprove.find('#modal-title');
14
- modalTitleDiv.html(modalTitle);
15
-
16
- // Extract the text from the button
17
- const modalText = button.data('text');
18
- const modalDiv = modalRequestApprove.find('#modal-request-text');
19
- modalDiv.html(modalText);
20
-
21
- // Set the character_id in the hidden input field
22
- const characterId = button.data('character-id');
23
- modalRequestApprove.find('input[name="character_id"]').val(characterId);
24
-
25
- // Set the planet_id in the hidden input field
26
- const planetId = button.data('planet-id');
27
- modalRequestApprove.find('input[name="planet_id"]').val(planetId);
28
-
29
- $('#modal-button-confirm-confirm-request').on('click', () => {
30
- const form = modalRequestApprove.find('form');
31
- const csrfMiddlewareToken = form.find('input[name="csrfmiddlewaretoken"]').val();
32
-
33
- // Remove any existing error messages
34
- form.find('.alert-danger').remove();
35
-
36
- const posting = $.post(
37
- url,
38
- {
39
- character_id: characterId,
40
- planet_id: planetId,
41
- csrfmiddlewaretoken: csrfMiddlewareToken
42
- }
43
- );
44
-
45
- posting.done(() => {
46
- modalRequestApprove.modal('hide');
47
- PlanetaryTable.ajax.reload(); // Reload the DataTable
48
-
49
- }).fail((xhr, _, __) => {
50
- const response = JSON.parse(xhr.responseText);
51
- modalErrorMessage.text(response.message).removeClass('d-none'); // Show the error message
52
- modalErrorMessage.addClass('l-shake'); // Add the shake class
53
-
54
- // Remove the shake class after 2 seconds
55
- setTimeout(() => {
56
- modalErrorMessage.removeClass('l-shake');
57
- }, 2000);
58
- });
59
- });
60
- }).on('hide.bs.modal', () => {
61
- modalRequestApprove.find('.alert-danger').remove();
62
- $('#modal-button-confirm-confirm-request').unbind('click');
63
- modalErrorMessage.addClass('d-none');
64
- modalErrorMessage.val('');
65
- });
66
- });
@@ -1,143 +0,0 @@
1
- /* global planetarySettings, bootstrap */
2
-
3
- $(document).ready(() => {
4
- var switchAlarmText = planetarySettings.switchAlarmText;
5
- var switchAlarm = planetarySettings.switchAlarm;
6
- var alarmActivated = planetarySettings.alarmActivated;
7
- var alarmDeactivated = planetarySettings.alarmDeactivated;
8
- var viewSwitchAlarmUrl = planetarySettings.viewSwitchAlarmUrl;
9
-
10
- function viewFactoryUrl(characterId, planetId) {
11
- return planetarySettings.viewFactoryUrl.replace('1337', characterId).replace('1337', planetId);
12
- }
13
-
14
- function viewExtractorUrl(characterId, planetId) {
15
- return planetarySettings.viewExtractorUrl.replace('1337', characterId).replace('1337', planetId);
16
- }
17
-
18
-
19
-
20
- // Initialize DataTable
21
- const PlanetaryTable = $('#planets-details').DataTable({
22
- ajax: {
23
- url: planetarySettings.planetaryUrl,
24
- dataSrc: '',
25
- cache: false
26
- },
27
- columns: [
28
- {
29
- data: 'character_name',
30
- render: function(data, type, row) {
31
- return `
32
- <img src="https://images.evetech.net/characters/${row.character_id}/portrait?size=32" class="rounded-circle" style="margin-right: 5px;">
33
- ${data}
34
- `;
35
- }
36
- },
37
- {
38
- data: 'planet',
39
- render: function(data, type, row) {
40
- return `
41
- <img src="https://images.evetech.net/types/${row.planet_type_id}/icon?size=32" class="rounded-circle" style="margin-right: 5px;" data-tooltip-toggle="ledger" title="${row.planet}">
42
- ${data}
43
- <i class="fa-solid fa-bullhorn" style="margin-left: 5px; color: ${row.alarm ? 'green' : 'red'};" title="${row.alarm ? alarmActivated : alarmDeactivated}" data-tooltip-toggle="ledger"></i>
44
- `;
45
- }
46
- },
47
- {
48
- data: 'upgrade_level'
49
- },
50
- {
51
- data: 'products',
52
- render: function(data, type, row) {
53
- return Object.values(data.processed).map(product => `
54
- <img src="https://images.evetech.net/types/${product.id}/icon?size=32" data-tooltip-toggle="ledger" title="${product.name}">
55
- `).join(' ') + `
56
- <button class="btn btn-primary btn-sm btn-square"
57
- data-bs-toggle="modal"
58
- data-bs-target="#modalViewFactoryContainer"
59
- data-ajax_factory="${viewFactoryUrl(row.character_id, row.planet_id)}"
60
- data-tooltip-toggle="ledger"
61
- data-bs-placement="left"
62
- title="${row.character_name} - ${row.planet}"
63
- >
64
- <span class="fas fa-info"></span>
65
- </button>`;
66
- }
67
- },
68
- {
69
- data: 'percentage'
70
- },
71
- {
72
- data: 'extractors',
73
- render: function(data, type, row) {
74
- return Object.values(row.products.raw).map(product => `
75
- <img src="https://images.evetech.net/types/${product.id}/icon?size=32" data-tooltip-toggle="ledger" title="${product.name}">
76
- `).join(' ') + `
77
- <button class="btn btn-primary btn-sm btn-square"
78
- data-bs-toggle="modal"
79
- data-bs-target="#modalViewExtractorContainer"
80
- data-ajax_extractor="${viewExtractorUrl(row.character_id, row.planet_id)}"
81
- data-tooltip-toggle="ledger"
82
- data-bs-placement="left"
83
- title="${row.character_name} - ${row.planet}"
84
- >
85
- <span class="fas fa-info"></span>
86
- </button>`;
87
- }
88
- },
89
- {
90
- data: 'status',
91
- render: function(data, type, row) {
92
- return `
93
- <img src="/static/ledger/images/${row.expired ? 'red' : 'green'}.png" style="width: 24px; height: 24px;" title="${row.expired ? 'Expired' : 'Active'}" data-tooltip-toggle="ledger">
94
- `;
95
- }
96
- },
97
- {
98
- data: 'last_update',
99
- render: function(data, type, row) {
100
- if (data === null) {
101
- return `<span class="text-warning" data-tooltip-toggle="ledger">Not updated yet</span>`;
102
- }
103
- return new Date(data).toLocaleString();
104
- }
105
-
106
- },
107
- {
108
- data: 'actions',
109
- render: function(data, type, row) {
110
- return `
111
- <button type="button" class="btn btn-primary btn-sm btn-square me-2" data-bs-toggle="modal" data-tooltip-toggle="ledger" data-character-id="${row.character_id}" data-planet-id="${row.planet_id}" data-title="${switchAlarm}" data-text="${switchAlarmText} \n${row.character_name} - ${row.planet}?" data-bs-target="#ledger-planetary-confirm" data-action="${viewSwitchAlarmUrl}" aria-label="Toggle Alarm">
112
- <span class="fas fa-bullhorn"></span>
113
- </button>
114
- `;
115
- }
116
- }
117
- ],
118
- columnDefs: [
119
- {
120
- orderable: false,
121
- targets: 'no-sort',
122
- }
123
- ],
124
- order: [
125
- [4, 'desc']
126
- ],
127
- pageLength: 25,
128
- initComplete: function() {
129
- $('[data-tooltip-toggle="ledger"]').tooltip({
130
- trigger: 'hover',
131
- });
132
- $('#ledger-index').addClass('show');
133
- },
134
- drawCallback: function() {
135
- $('[data-tooltip-toggle="ledger"]').tooltip({
136
- trigger: 'hover',
137
- });
138
- }
139
- });
140
-
141
- // Make PlanetaryTable globally accessible
142
- window.PlanetaryTable = PlanetaryTable;
143
- });