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.
- {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/METADATA +6 -6
- aa_ledger-2.0.0.dist-info/RECORD +267 -0
- {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/WHEEL +1 -1
- ledger/__init__.py +2 -2
- ledger/admin.py +23 -18
- ledger/api/__init__.py +23 -7
- ledger/api/{ledger/admin.py → admin.py} +25 -31
- ledger/api/alliance.py +755 -0
- ledger/api/character.py +786 -0
- ledger/api/corporation.py +1141 -0
- ledger/api/{helpers.py → helpers/core.py} +33 -33
- ledger/api/helpers/icons.py +372 -0
- ledger/api/helpers/planetary_helper.py +354 -0
- ledger/api/planetary.py +354 -0
- ledger/api/schema.py +240 -15
- ledger/app_settings.py +18 -26
- ledger/auth_hooks.py +2 -2
- ledger/constants.py +50 -177
- ledger/decorators.py +2 -46
- ledger/forms.py +133 -39
- ledger/helpers/billboard.py +194 -144
- ledger/helpers/cache.py +105 -0
- ledger/helpers/discord.py +2 -4
- ledger/helpers/eveonline.py +160 -0
- ledger/helpers/ledger_data.py +23 -0
- ledger/helpers/ref_type.py +53 -78
- ledger/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- ledger/locale/cs_CZ/LC_MESSAGES/django.po +349 -193
- ledger/locale/de/LC_MESSAGES/django.mo +0 -0
- ledger/locale/de/LC_MESSAGES/django.po +528 -379
- ledger/locale/django.pot +717 -553
- ledger/locale/es/LC_MESSAGES/django.mo +0 -0
- ledger/locale/es/LC_MESSAGES/django.po +349 -194
- ledger/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- ledger/locale/fr_FR/LC_MESSAGES/django.po +349 -193
- ledger/locale/it_IT/LC_MESSAGES/django.mo +0 -0
- ledger/locale/it_IT/LC_MESSAGES/django.po +349 -193
- ledger/locale/ja/LC_MESSAGES/django.mo +0 -0
- ledger/locale/ja/LC_MESSAGES/django.po +348 -193
- ledger/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
- ledger/locale/ko_KR/LC_MESSAGES/django.po +349 -193
- ledger/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
- ledger/locale/nl_NL/LC_MESSAGES/django.po +349 -193
- ledger/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- ledger/locale/pl_PL/LC_MESSAGES/django.po +350 -193
- ledger/locale/ru/LC_MESSAGES/django.mo +0 -0
- ledger/locale/ru/LC_MESSAGES/django.po +348 -193
- ledger/locale/sk/LC_MESSAGES/django.mo +0 -0
- ledger/locale/sk/LC_MESSAGES/django.po +348 -193
- ledger/locale/uk/LC_MESSAGES/django.mo +0 -0
- ledger/locale/uk/LC_MESSAGES/django.po +348 -193
- ledger/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- ledger/locale/zh_Hans/LC_MESSAGES/django.po +348 -193
- ledger/managers/character_audit_manager.py +28 -20
- ledger/managers/character_journal_manager.py +187 -358
- ledger/managers/character_mining_manager.py +64 -30
- ledger/managers/character_planetary_manager.py +185 -138
- ledger/managers/corporation_audit_manager.py +36 -27
- ledger/managers/corporation_journal_manager.py +94 -57
- ledger/managers/general_manager.py +12 -8
- ledger/migrations/0018_remove_characterplanet_ledger_char_planet__58a5b6_idx_and_more.py +44 -0
- ledger/migrations/0019_rename_characteraudit_characterowner_and_more.py +48 -0
- ledger/models/__init__.py +5 -11
- ledger/models/characteraudit.py +101 -109
- ledger/models/corporationaudit.py +94 -49
- ledger/models/general.py +105 -211
- ledger/models/helpers/update_manager.py +302 -0
- ledger/models/planetary.py +60 -205
- ledger/providers.py +101 -0
- ledger/static/ledger/css/{ledger.css → aa-ledger.css} +54 -28
- ledger/static/ledger/js/aa-ledger.js +124 -0
- ledger/static/ledger/js/charts.js +25 -1
- ledger/static/ledger/js/view-alliance-ledger.js +383 -0
- ledger/static/ledger/js/view-character-ledger.js +388 -0
- ledger/static/ledger/js/view-corporation-ledger.js +402 -0
- ledger/static/ledger/js/view-planetary.js +492 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/flow.js +2 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/index.js +2 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/percent.js +2 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Animated.js +2 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/themes/Dark.js +2 -0
- ledger/static/ledger/libs/amCharts/5.14.4/js/xy.js +2 -0
- ledger/static/ledger/libs/datatables/2.3.5/css/dataTables.bootstrap5.css +610 -0
- ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.bootstrap5.js +122 -0
- ledger/static/ledger/libs/datatables/2.3.5/js/dataTables.js +14127 -0
- ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
- ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/css/columnControl.dataTables.css +529 -0
- ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
- ledger/static/ledger/libs/datatables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3090 -0
- ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
- ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
- ledger/static/ledger/libs/datatables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
- ledger/tasks.py +157 -146
- ledger/templates/ledger/base.html +59 -21
- ledger/templates/ledger/bundles/aa-ledger-css.html +3 -0
- ledger/templates/ledger/bundles/aa-ledger-js.html +3 -0
- ledger/templates/ledger/bundles/view-alliance-ledger-js.html +14 -0
- ledger/templates/ledger/bundles/view-character-ledger-js.html +15 -0
- ledger/templates/ledger/bundles/view-character-planetary-css.html +3 -0
- ledger/templates/ledger/bundles/view-character-planetary-js.html +4 -0
- ledger/templates/ledger/bundles/view-corporation-ledger-js.html +15 -0
- ledger/templates/ledger/partials/modal/confirm.html +0 -1
- ledger/templates/ledger/partials/modal/request-accept-delete-alliance.html +38 -0
- ledger/templates/ledger/partials/modal/request-accept-delete-character.html +38 -0
- ledger/templates/ledger/partials/modal/request-accept-delete-corporation.html +38 -0
- ledger/templates/ledger/partials/modal/request-accept-switch-notification.html +38 -0
- ledger/templates/ledger/partials/modal/request-view-alliance-details.html +26 -0
- ledger/templates/ledger/partials/modal/request-view-character-details.html +26 -0
- ledger/templates/ledger/partials/modal/request-view-corporation-details.html +26 -0
- ledger/templates/ledger/partials/modal/request-view-extractor.html +32 -0
- ledger/templates/ledger/partials/modal/request-view-factory.html +31 -0
- ledger/templates/ledger/partials/{menu → navigation}/administration.html +8 -0
- ledger/templates/ledger/partials/{menu → navigation}/navigation.html +2 -2
- ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance_corporations.html +3 -3
- ledger/templates/ledger/partials/view-alliance-administration/dashboard.html +81 -0
- ledger/templates/ledger/partials/view-alliance-ledger/alliance-billboard.html +25 -0
- ledger/templates/ledger/partials/view-alliance-ledger/alliance-ledger-details.html +21 -0
- ledger/templates/ledger/partials/view-alliance-ledger/alliance-table.html +24 -0
- ledger/templates/ledger/partials/view-alliance-ledger/information/daily.html +18 -0
- ledger/templates/ledger/partials/view-alliance-ledger/information/hourly.html +18 -0
- ledger/templates/ledger/partials/view-alliance-ledger/information/summary.html +19 -0
- ledger/templates/ledger/partials/{administration → view-character-administration}/character.html +1 -9
- ledger/templates/ledger/partials/{administration → view-character-administration}/dashboard.html +0 -34
- ledger/templates/ledger/partials/view-character-ledger/character-billboard.html +25 -0
- ledger/templates/ledger/partials/view-character-ledger/character-ledger-details.html +21 -0
- ledger/templates/ledger/partials/view-character-ledger/character-table.html +25 -0
- ledger/templates/ledger/partials/view-character-ledger/information/daily.html +18 -0
- ledger/templates/ledger/partials/view-character-ledger/information/hourly.html +18 -0
- ledger/templates/ledger/partials/view-character-ledger/information/summary.html +19 -0
- ledger/templates/ledger/partials/view-character-planetary/extractor-table.html +24 -0
- ledger/templates/ledger/partials/view-character-planetary/factory-table.html +24 -0
- ledger/templates/ledger/partials/view-character-planetary/planetary-table.html +22 -0
- ledger/templates/ledger/partials/view-character-planetary/storage-table.html +23 -0
- ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation.html +5 -13
- ledger/templates/ledger/partials/{administration → view-corporation-administration}/corporation_characters.html +1 -1
- ledger/templates/ledger/partials/view-corporation-administration/dashboard.html +81 -0
- ledger/templates/ledger/partials/view-corporation-ledger/corporation-billboard.html +25 -0
- ledger/templates/ledger/partials/view-corporation-ledger/corporation-ledger-details.html +21 -0
- ledger/templates/ledger/partials/view-corporation-ledger/corporation-table.html +26 -0
- ledger/templates/ledger/partials/view-corporation-ledger/information/daily.html +18 -0
- ledger/templates/ledger/partials/view-corporation-ledger/information/hourly.html +18 -0
- ledger/templates/ledger/partials/view-corporation-ledger/information/summary.html +19 -0
- ledger/templates/ledger/view-administration.html +62 -0
- ledger/templates/ledger/view-alliance-administration.html +49 -0
- ledger/templates/ledger/view-alliance-ledger.html +72 -0
- ledger/templates/ledger/view-alliance-overview.html +131 -0
- ledger/templates/ledger/view-character-administration.html +42 -0
- ledger/templates/ledger/view-character-ledger.html +73 -0
- ledger/templates/ledger/view-character-overview.html +135 -0
- ledger/templates/ledger/view-character-planetary-overview.html +135 -0
- ledger/templates/ledger/view-character-planetary.html +73 -0
- ledger/templates/ledger/view-corporation-administration.html +42 -0
- ledger/templates/ledger/view-corporation-ledger.html +73 -0
- ledger/templates/ledger/view-corporation-overview.html +131 -0
- ledger/templatetags/ledger.py +3 -5
- ledger/tests/__init__.py +187 -0
- ledger/tests/test_admin.py +164 -68
- ledger/tests/test_auth_hook.py +31 -13
- ledger/tests/test_decarators.py +14 -79
- ledger/tests/test_discord_installed.py +0 -1
- ledger/tests/test_helpers/test_ledger_data.py +19 -0
- ledger/tests/test_managers/test_character_audit_manager.py +111 -69
- ledger/tests/test_managers/test_character_journal_manager.py +48 -208
- ledger/tests/test_managers/test_character_mining_manager.py +37 -16
- ledger/tests/test_managers/test_corporation_division_manager.py +66 -28
- ledger/tests/test_managers/test_corporation_journal_manager.py +39 -42
- ledger/tests/test_managers/test_general_manager.py +78 -18
- ledger/tests/test_managers/test_planetary_manager.py +73 -32
- ledger/tests/test_models/test_characteraudit.py +58 -74
- ledger/tests/test_models/test_characterminingledger.py +20 -26
- ledger/tests/test_models/test_characterwalletjournal.py +10 -33
- ledger/tests/test_models/test_corporationaudit.py +41 -35
- ledger/tests/test_models/test_corporationwalletjournal.py +35 -32
- ledger/tests/test_models/test_general.py +44 -11
- ledger/tests/test_models/test_planetary.py +14 -80
- ledger/tests/test_templatetags.py +2 -7
- ledger/tests/test_views/corporation/test_add_corp.py +16 -35
- ledger/tests/test_views/corporation/test_delete_corporation.py +66 -42
- ledger/tests/test_views/test_access.py +512 -545
- ledger/tests/test_views/test_add_ally.py +57 -46
- ledger/tests/test_views/test_add_char.py +21 -33
- ledger/tests/test_views/test_delete_character.py +24 -21
- ledger/tests/testdata/README_ESI_STUB.md +430 -0
- ledger/tests/testdata/esi_stub_openapi.py +511 -0
- ledger/tests/testdata/integrations/__init__.py +0 -0
- ledger/tests/testdata/{load_eveuniverse.py → integrations/eveuniverse.py} +0 -1
- ledger/tests/testdata/integrations/planetary.py +13 -0
- ledger/tests/testdata/json/factory.json +281 -0
- ledger/tests/testdata/json/inactive.json +281 -0
- ledger/tests/testdata/json/pins.json +175 -272
- ledger/tests/testdata/json/route.json +95 -528
- ledger/tests/testdata/test_esi_stub.py +468 -0
- ledger/tests/testdata/utils.py +601 -0
- ledger/thirdparty/charlink_hook.py +60 -30
- ledger/urls.py +0 -135
- ledger/views/alliance/add_ally.py +2 -4
- ledger/views/alliance/alliance_ledger.py +64 -147
- ledger/views/character/add_char.py +8 -10
- ledger/views/character/character_ledger.py +60 -126
- ledger/views/character/planetary.py +5 -98
- ledger/views/corporation/add_corp.py +10 -12
- ledger/views/corporation/corporation_ledger.py +65 -327
- ledger/views/index.py +92 -30
- aa_ledger-1.0.3.dist-info/RECORD +0 -236
- ledger/api/api_helper/planetary_helper.py +0 -107
- ledger/api/ledger/__init__.py +0 -7
- ledger/api/ledger/planetary.py +0 -231
- ledger/helpers/alliance.py +0 -317
- ledger/helpers/character.py +0 -251
- ledger/helpers/core.py +0 -665
- ledger/helpers/corporation.py +0 -427
- ledger/helpers/data_exporter.py +0 -452
- ledger/static/ledger/js/planetary-confirm.js +0 -66
- ledger/static/ledger/js/planetary.js +0 -143
- ledger/templates/ledger/admin.html +0 -43
- ledger/templates/ledger/allyledger/admin/alliance_administration.html +0 -46
- ledger/templates/ledger/allyledger/admin/alliance_overview.html +0 -108
- ledger/templates/ledger/allyledger/alliance_ledger.html +0 -86
- ledger/templates/ledger/bundles/character-ledger-bundles.html +0 -66
- ledger/templates/ledger/bundles/corporation-ledger-bundles.html +0 -75
- ledger/templates/ledger/bundles/ledger-bundles.html +0 -23
- ledger/templates/ledger/bundles/ledger-css.html +0 -3
- ledger/templates/ledger/bundles/planetary-bundles.html +0 -50
- ledger/templates/ledger/bundles/table-css.html +0 -3
- ledger/templates/ledger/charledger/admin/character_administration.html +0 -39
- ledger/templates/ledger/charledger/admin/character_overview.html +0 -106
- ledger/templates/ledger/charledger/character_ledger.html +0 -94
- ledger/templates/ledger/charledger/planetary/admin/planetary_overview.html +0 -123
- ledger/templates/ledger/charledger/planetary/planetary_ledger.html +0 -54
- ledger/templates/ledger/corpledger/admin/corporation_administration.html +0 -39
- ledger/templates/ledger/corpledger/admin/corporation_overview.html +0 -108
- ledger/templates/ledger/corpledger/corporation_ledger.html +0 -129
- ledger/templates/ledger/data-export.html +0 -78
- ledger/templates/ledger/error.html +0 -31
- ledger/templates/ledger/partials/form/error-message.html +0 -1
- ledger/templates/ledger/partials/information/daily.html +0 -56
- ledger/templates/ledger/partials/information/day.html +0 -48
- ledger/templates/ledger/partials/information/error.html +0 -8
- ledger/templates/ledger/partials/information/hourly.html +0 -53
- ledger/templates/ledger/partials/information/summary.html +0 -88
- ledger/templates/ledger/partials/information/view_character_content.html +0 -35
- ledger/templates/ledger/partials/modal/switchalarm_confirm.html +0 -39
- ledger/templates/ledger/partials/modal/view_extractor.html +0 -48
- ledger/templates/ledger/partials/modal/view_factory.html +0 -123
- ledger/templates/ledger/partials/table/char-ledger.html +0 -85
- ledger/templates/ledger/partials/table/corp-ledger.html +0 -66
- ledger/templates/ledger/partials/table/planetary.html +0 -18
- ledger/templates/ledger/partials/thirdparty/billboard.html +0 -22
- ledger/templates/ledger/partials/view/card.html +0 -160
- ledger/templates/ledger/permission.html +0 -2
- ledger/tests/test_helpers/test_billboard.py +0 -11
- ledger/tests/test_helpers/test_data_exporter.py +0 -207
- ledger/tests/test_tasks.py +0 -282
- ledger/tests/test_view_helpers/test_core.py +0 -47
- ledger/tests/test_views/corporation/test_corporation.py +0 -267
- ledger/tests/test_views/test_planetary.py +0 -137
- ledger/tests/testdata/esi_stub.py +0 -109
- ledger/tests/testdata/esi_stub_migration.py +0 -80
- ledger/tests/testdata/generate_characteraudit.py +0 -106
- ledger/tests/testdata/generate_corporationaudit.py +0 -74
- ledger/tests/testdata/generate_events.py +0 -31
- ledger/tests/testdata/generate_miningledger.py +0 -13
- ledger/tests/testdata/generate_planets.py +0 -48
- ledger/tests/testdata/generate_walletjournal.py +0 -42
- ledger/tests/testdata/json/czarno-pins.json +0 -240
- ledger/tests/testdata/json/czarno-routes.json +0 -165
- ledger/tests/testdata/json/pins2.json +0 -538
- {aa_ledger-1.0.3.dist-info → aa_ledger-2.0.0.dist-info}/licenses/LICENSE +0 -0
- /ledger/{tests/test_view_helpers → api/helpers}/__init__.py +0 -0
- /ledger/templates/ledger/bundles/{ally-administration-bundles.html → view-alliance-administration-js.html} +0 -0
- /ledger/templates/ledger/bundles/{char-administration-bundles.html → view-character-administration-js.html} +0 -0
- /ledger/templates/ledger/bundles/{corp-administration-bundles.html → view-corporation-administration-js.html} +0 -0
- /ledger/templates/ledger/partials/{administration → view-alliance-administration}/alliance.html +0 -0
- /ledger/tests/testdata/{esi.json → esi_test_data.json} +0 -0
- /ledger/tests/testdata/{allianceauth.json → integrations/allianceauth.json} +0 -0
- /ledger/tests/testdata/{load_allianceauth.py → integrations/allianceauth.py} +0 -0
- /ledger/tests/testdata/{eveentity.json → integrations/eveentity.json} +0 -0
- /ledger/tests/testdata/{load_eveentity.py → integrations/eveentity.py} +0 -0
- /ledger/tests/testdata/{eveuniverse.json → integrations/eveuniverse.json} +0 -0
- /ledger/tests/testdata/{planetary.json → integrations/planetary.json} +0 -0
ledger/models/planetary.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
23
|
-
|
|
19
|
+
CharacterPlanetManager,
|
|
20
|
+
PlanetDetailsManager,
|
|
24
21
|
)
|
|
25
|
-
from ledger.models.characteraudit import
|
|
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 =
|
|
26
|
+
logger = AppLogger(get_extension_logger(__name__), __title__)
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
class CharacterPlanet(models.Model):
|
|
31
|
-
|
|
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
|
-
|
|
43
|
+
name = models.CharField(max_length=100, null=True, default=None)
|
|
36
44
|
|
|
37
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
69
|
-
section=
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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=
|
|
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
|
-
|
|
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
|
+
};
|