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/helpers/alliance.py
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
"""PvE Views"""
|
|
2
|
-
|
|
3
|
-
# Standard Library
|
|
4
|
-
from decimal import Decimal
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
# Django
|
|
8
|
-
from django.core.handlers.wsgi import WSGIRequest
|
|
9
|
-
from django.db.models import DecimalField, F, Q, QuerySet, Sum
|
|
10
|
-
from django.utils.translation import gettext as _
|
|
11
|
-
|
|
12
|
-
# Alliance Auth
|
|
13
|
-
from allianceauth.eveonline.models import EveAllianceInfo
|
|
14
|
-
from allianceauth.services.hooks import get_extension_logger
|
|
15
|
-
|
|
16
|
-
# Alliance Auth (External Libs)
|
|
17
|
-
from app_utils.logging import LoggerAddTag
|
|
18
|
-
|
|
19
|
-
# AA Ledger
|
|
20
|
-
from ledger import __title__
|
|
21
|
-
from ledger.helpers.billboard import BillboardSystem
|
|
22
|
-
from ledger.helpers.core import LedgerCore, LedgerEntity
|
|
23
|
-
from ledger.helpers.ref_type import RefTypeManager
|
|
24
|
-
from ledger.models.corporationaudit import (
|
|
25
|
-
CorporationAudit,
|
|
26
|
-
CorporationWalletJournalEntry,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
logger = LoggerAddTag(get_extension_logger(__name__), __title__)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class AllianceData(LedgerCore):
|
|
33
|
-
"""Class to hold alliance data for the ledger."""
|
|
34
|
-
|
|
35
|
-
# pylint: disable=too-many-positional-arguments
|
|
36
|
-
def __init__(
|
|
37
|
-
self,
|
|
38
|
-
alliance: EveAllianceInfo,
|
|
39
|
-
request: WSGIRequest = None,
|
|
40
|
-
year=None,
|
|
41
|
-
month=None,
|
|
42
|
-
day=None,
|
|
43
|
-
section=None,
|
|
44
|
-
):
|
|
45
|
-
super().__init__(year, month, day)
|
|
46
|
-
self.request = request
|
|
47
|
-
self.alliance = alliance
|
|
48
|
-
self.entity_id = self.alliance.alliance_id
|
|
49
|
-
self.section = section
|
|
50
|
-
self.corporations = CorporationAudit.objects.filter(
|
|
51
|
-
corporation__alliance__alliance_id=self.alliance.alliance_id
|
|
52
|
-
).values_list("corporation__corporation_id", flat=True)
|
|
53
|
-
self.auth_char_ids = self.auth_character_ids
|
|
54
|
-
self.billboard = BillboardSystem()
|
|
55
|
-
self.queryset = (
|
|
56
|
-
self._get_journal_queryset()
|
|
57
|
-
) # Base queryset filtered by date and alliance
|
|
58
|
-
|
|
59
|
-
def _get_journal_queryset(self) -> QuerySet[CorporationWalletJournalEntry]:
|
|
60
|
-
"""Return the base queryset filtered by the current date range and corporation division."""
|
|
61
|
-
return CorporationWalletJournalEntry.objects.filter(
|
|
62
|
-
self.filter_date,
|
|
63
|
-
division__corporation__corporation__alliance__alliance_id=self.alliance.alliance_id,
|
|
64
|
-
).exclude(
|
|
65
|
-
Q(ref_type="corporation_account_withdrawal")
|
|
66
|
-
& Q(first_party_id=F("second_party_id"))
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def _compute_entities(
|
|
70
|
-
self, journal: QuerySet[CorporationWalletJournalEntry]
|
|
71
|
-
) -> set:
|
|
72
|
-
"""Return a set of all entity IDs (first and second parties) present in the current journal."""
|
|
73
|
-
return set(journal.values_list("second_party_id", flat=True)) | set(
|
|
74
|
-
journal.values_list("first_party_id", flat=True)
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
def _compute_journal_values(
|
|
78
|
-
self, journal: QuerySet[CorporationWalletJournalEntry]
|
|
79
|
-
) -> QuerySet[dict[str, Any]]:
|
|
80
|
-
"""Return the journal values for the current journal."""
|
|
81
|
-
return journal.values(
|
|
82
|
-
"first_party_id",
|
|
83
|
-
"second_party_id",
|
|
84
|
-
"pk",
|
|
85
|
-
"ref_type",
|
|
86
|
-
"division__corporation__corporation__corporation_id",
|
|
87
|
-
).annotate(
|
|
88
|
-
bounty=Sum(
|
|
89
|
-
"amount",
|
|
90
|
-
filter=Q(ref_type__in=RefTypeManager.BOUNTY_PRIZES),
|
|
91
|
-
output_field=DecimalField(),
|
|
92
|
-
),
|
|
93
|
-
ess=Sum(
|
|
94
|
-
"amount",
|
|
95
|
-
filter=Q(ref_type__in=RefTypeManager.ESS_TRANSFER),
|
|
96
|
-
output_field=DecimalField(),
|
|
97
|
-
),
|
|
98
|
-
costs=Sum(
|
|
99
|
-
"amount",
|
|
100
|
-
filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__lt=0),
|
|
101
|
-
output_field=DecimalField(),
|
|
102
|
-
),
|
|
103
|
-
miscellaneous=Sum(
|
|
104
|
-
"amount",
|
|
105
|
-
filter=Q(ref_type__in=RefTypeManager.all_ref_types(), amount__gt=0),
|
|
106
|
-
output_field=DecimalField(),
|
|
107
|
-
),
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
# pylint: disable=duplicate-code
|
|
111
|
-
def create_entity_data(
|
|
112
|
-
self,
|
|
113
|
-
entity: LedgerEntity,
|
|
114
|
-
) -> dict:
|
|
115
|
-
"""Create the URL for entity details based on the view type."""
|
|
116
|
-
used_pks = set()
|
|
117
|
-
bounty = Decimal(0)
|
|
118
|
-
ess = Decimal(0)
|
|
119
|
-
miscellaneous = Decimal(0)
|
|
120
|
-
costs = Decimal(0)
|
|
121
|
-
|
|
122
|
-
for pk, rows in list(self.entries.items()):
|
|
123
|
-
for row in rows:
|
|
124
|
-
if (
|
|
125
|
-
row["division__corporation__corporation__corporation_id"]
|
|
126
|
-
== entity.entity_id
|
|
127
|
-
):
|
|
128
|
-
if RefTypeManager.special_cases(
|
|
129
|
-
row, ids=[], account_char_ids=self.auth_char_ids
|
|
130
|
-
):
|
|
131
|
-
continue
|
|
132
|
-
bounty += row.get("bounty") or Decimal(0)
|
|
133
|
-
ess += row.get("ess") or Decimal(0)
|
|
134
|
-
miscellaneous += row.get("miscellaneous") or Decimal(0)
|
|
135
|
-
costs += row.get("costs") or Decimal(0)
|
|
136
|
-
used_pks.add(pk)
|
|
137
|
-
|
|
138
|
-
# Remove Used Pks from Entries
|
|
139
|
-
# This is to prevent the entries from being used in the future
|
|
140
|
-
for pk in used_pks:
|
|
141
|
-
self.entries.pop(pk, None)
|
|
142
|
-
|
|
143
|
-
misc = miscellaneous
|
|
144
|
-
total = sum([bounty, ess, miscellaneous, costs])
|
|
145
|
-
|
|
146
|
-
if total == 0:
|
|
147
|
-
return None
|
|
148
|
-
|
|
149
|
-
entity_ledger_info = {
|
|
150
|
-
"entity": entity,
|
|
151
|
-
"ledger": {
|
|
152
|
-
"bounty": bounty,
|
|
153
|
-
"ess": ess,
|
|
154
|
-
"miscellaneous": misc,
|
|
155
|
-
"costs": costs,
|
|
156
|
-
"total": total,
|
|
157
|
-
},
|
|
158
|
-
"type": entity.type,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return entity_ledger_info
|
|
162
|
-
|
|
163
|
-
# pylint: disable=duplicate-code
|
|
164
|
-
def generate_ledger_data(self) -> dict:
|
|
165
|
-
"""Generate the ledger data for the alliance."""
|
|
166
|
-
# Compute all entities in the journal
|
|
167
|
-
self.entities = self._compute_entities(self.queryset)
|
|
168
|
-
# Compute journal values
|
|
169
|
-
journal = self._compute_journal_values(self.queryset)
|
|
170
|
-
|
|
171
|
-
# Caching
|
|
172
|
-
ledger_hash = self.get_ledger_journal_hash(journal.values_list("pk"))
|
|
173
|
-
cache_key = f"{self.entity_id}"
|
|
174
|
-
|
|
175
|
-
# Get Cached Data if available
|
|
176
|
-
ledger, finished_entities = self.get_cache_ledger(
|
|
177
|
-
ledger_hash=ledger_hash, cache_key=cache_key
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
if finished_entities is False or ledger is False:
|
|
181
|
-
ledger = []
|
|
182
|
-
finished_entities = set()
|
|
183
|
-
|
|
184
|
-
# Build the entries from the journal
|
|
185
|
-
self.entries = {}
|
|
186
|
-
for row in journal:
|
|
187
|
-
self.entries.setdefault(row["pk"], []).append(row)
|
|
188
|
-
|
|
189
|
-
# Build Data for each corporation
|
|
190
|
-
for corporation_id in self.corporations:
|
|
191
|
-
# Create Details URL for the entity
|
|
192
|
-
details_url = self.create_url(
|
|
193
|
-
viewname="corporation_details",
|
|
194
|
-
corporation_id=corporation_id,
|
|
195
|
-
entity_id=corporation_id,
|
|
196
|
-
section="summary",
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
# Create the LedgerEntity object for the entity
|
|
200
|
-
entity_obj = LedgerEntity(
|
|
201
|
-
entity_id=corporation_id,
|
|
202
|
-
details_url=details_url,
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
corp_data = self.create_entity_data(
|
|
206
|
-
entity=entity_obj,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
if corp_data is None:
|
|
210
|
-
continue
|
|
211
|
-
|
|
212
|
-
ledger.append(corp_data)
|
|
213
|
-
finished_entities.add(corporation_id)
|
|
214
|
-
|
|
215
|
-
# Create Cache
|
|
216
|
-
self.set_cache_ledger(
|
|
217
|
-
ledger_hash=ledger_hash,
|
|
218
|
-
cache_key=cache_key,
|
|
219
|
-
ledger=ledger,
|
|
220
|
-
finished_entities=finished_entities,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
# Create the billboard data
|
|
224
|
-
self.billboard.change_view(self.get_view_mode())
|
|
225
|
-
self.create_rattingbar(journal=journal, entities_ids=list(finished_entities))
|
|
226
|
-
self.create_chord(ledger)
|
|
227
|
-
return ledger
|
|
228
|
-
|
|
229
|
-
def generate_data_export(self) -> dict:
|
|
230
|
-
"""Generate the data export for the corporation."""
|
|
231
|
-
# Compute all entities in the journal
|
|
232
|
-
self.entities = self._compute_entities(self.queryset)
|
|
233
|
-
# Compute journal values
|
|
234
|
-
journal = self._compute_journal_values(self.queryset)
|
|
235
|
-
|
|
236
|
-
ledger = []
|
|
237
|
-
|
|
238
|
-
# Build the entries from the journal
|
|
239
|
-
self.entries = {}
|
|
240
|
-
for row in journal:
|
|
241
|
-
self.entries.setdefault(row["pk"], []).append(row)
|
|
242
|
-
|
|
243
|
-
# Build Data for each corporation
|
|
244
|
-
for corporation_id in self.corporations:
|
|
245
|
-
# Create the LedgerEntity object for the entity
|
|
246
|
-
entity_obj = LedgerEntity(
|
|
247
|
-
entity_id=corporation_id,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
# Create Ledger Data for the entity
|
|
251
|
-
corp_data = self.create_entity_data(
|
|
252
|
-
entity=entity_obj,
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
if corp_data is None:
|
|
256
|
-
continue
|
|
257
|
-
|
|
258
|
-
ledger.append(corp_data)
|
|
259
|
-
return ledger
|
|
260
|
-
|
|
261
|
-
def create_rattingbar(
|
|
262
|
-
self,
|
|
263
|
-
journal: QuerySet[CorporationWalletJournalEntry],
|
|
264
|
-
entities_ids: list = None,
|
|
265
|
-
):
|
|
266
|
-
"""Create the ratting bar for the view."""
|
|
267
|
-
if not entities_ids:
|
|
268
|
-
return
|
|
269
|
-
|
|
270
|
-
# Create the timeline for the ratting bar
|
|
271
|
-
rattingbar_timeline = self.billboard.create_timeline(journal)
|
|
272
|
-
|
|
273
|
-
# Annotate the timeline with the relevant data
|
|
274
|
-
rattingbar = (
|
|
275
|
-
rattingbar_timeline.annotate_bounty_income()
|
|
276
|
-
.annotate_ess_income()
|
|
277
|
-
.annotate_miscellaneous()
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
# Generate the XY series for the ratting bar
|
|
281
|
-
self.billboard.create_or_update_results(rattingbar)
|
|
282
|
-
series, categories = self.billboard.generate_xy_series()
|
|
283
|
-
if series and categories:
|
|
284
|
-
# Create the ratting bar chart
|
|
285
|
-
self.billboard.create_xy_chart(
|
|
286
|
-
title=_("Ratting Bar"), categories=categories, series=series
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
def create_chord(self, ledger_data: list[dict]):
|
|
290
|
-
"""Create the chord chart for the view."""
|
|
291
|
-
if not ledger_data:
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
for entry in ledger_data:
|
|
295
|
-
entity_name = entry["entity"].entity_name
|
|
296
|
-
ledger = entry["ledger"]
|
|
297
|
-
self.billboard.chord_add_data(
|
|
298
|
-
chord_from=entity_name,
|
|
299
|
-
chord_to=_("Bounty (Wallet)"),
|
|
300
|
-
value=ledger.get("bounty", 0),
|
|
301
|
-
)
|
|
302
|
-
self.billboard.chord_add_data(
|
|
303
|
-
chord_from=entity_name,
|
|
304
|
-
chord_to=_("ESS (Wallet)"),
|
|
305
|
-
value=ledger.get("ess", 0),
|
|
306
|
-
)
|
|
307
|
-
self.billboard.chord_add_data(
|
|
308
|
-
chord_from=entity_name,
|
|
309
|
-
chord_to=_("Costs (Wallet)"),
|
|
310
|
-
value=abs(ledger.get("costs", 0)),
|
|
311
|
-
)
|
|
312
|
-
self.billboard.chord_add_data(
|
|
313
|
-
chord_from=entity_name,
|
|
314
|
-
chord_to=_("Miscellaneous (Wallet)"),
|
|
315
|
-
value=abs(ledger.get("miscellaneous", 0)),
|
|
316
|
-
)
|
|
317
|
-
self.billboard.chord_handle_overflow()
|
ledger/helpers/character.py
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
"""PvE Views"""
|
|
2
|
-
|
|
3
|
-
# Standard Library
|
|
4
|
-
from decimal import Decimal
|
|
5
|
-
|
|
6
|
-
# Django
|
|
7
|
-
from django.core.handlers.wsgi import WSGIRequest
|
|
8
|
-
from django.db.models import QuerySet
|
|
9
|
-
from django.utils.translation import gettext as _
|
|
10
|
-
|
|
11
|
-
# Alliance Auth
|
|
12
|
-
from allianceauth.services.hooks import get_extension_logger
|
|
13
|
-
|
|
14
|
-
# Alliance Auth (External Libs)
|
|
15
|
-
from app_utils.logging import LoggerAddTag
|
|
16
|
-
|
|
17
|
-
# AA Ledger
|
|
18
|
-
from ledger import __title__
|
|
19
|
-
from ledger.helpers.billboard import BillboardSystem
|
|
20
|
-
from ledger.helpers.core import LedgerCore
|
|
21
|
-
from ledger.models.characteraudit import (
|
|
22
|
-
CharacterAudit,
|
|
23
|
-
CharacterMiningLedger,
|
|
24
|
-
CharacterWalletJournalEntry,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
logger = LoggerAddTag(get_extension_logger(__name__), __title__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class CharacterData(LedgerCore):
|
|
31
|
-
"""Class to hold character data for the ledger."""
|
|
32
|
-
|
|
33
|
-
# pylint: disable=too-many-positional-arguments
|
|
34
|
-
def __init__(
|
|
35
|
-
self,
|
|
36
|
-
request: WSGIRequest,
|
|
37
|
-
character: CharacterAudit,
|
|
38
|
-
year=None,
|
|
39
|
-
month=None,
|
|
40
|
-
day=None,
|
|
41
|
-
section=None,
|
|
42
|
-
):
|
|
43
|
-
super().__init__(year, month, day)
|
|
44
|
-
self.request = request
|
|
45
|
-
self.character = character
|
|
46
|
-
self.alts_ids = self.get_alt_ids
|
|
47
|
-
self.characters = CharacterAudit.objects.filter(
|
|
48
|
-
eve_character__character_id__in=self.alts_ids
|
|
49
|
-
).select_related("eve_character")
|
|
50
|
-
self.section = section
|
|
51
|
-
self.billboard = BillboardSystem()
|
|
52
|
-
self.queryset = character.ledger_character_journal.filter(self.filter_date)
|
|
53
|
-
self.mining = character.ledger_character_mining.filter(self.filter_date)
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def get_alt_ids(self):
|
|
57
|
-
return self.character.alts.values_list("character_id", flat=True)
|
|
58
|
-
|
|
59
|
-
@property
|
|
60
|
-
def is_old_ess(self):
|
|
61
|
-
"""
|
|
62
|
-
Compatibility check for old ESS income calculation.
|
|
63
|
-
Since Swagger ESI has added ESS Ref Type to the Character Journal Endpoint
|
|
64
|
-
"""
|
|
65
|
-
try:
|
|
66
|
-
if self.month is None and self.year is None:
|
|
67
|
-
return False
|
|
68
|
-
if self.year >= 2025 and self.month >= 6:
|
|
69
|
-
return False
|
|
70
|
-
except TypeError:
|
|
71
|
-
return True
|
|
72
|
-
return True
|
|
73
|
-
|
|
74
|
-
def filter_character_journal(
|
|
75
|
-
self, character: CharacterAudit
|
|
76
|
-
) -> tuple[QuerySet[CharacterWalletJournalEntry], QuerySet[CharacterMiningLedger]]:
|
|
77
|
-
"""Filter the journal entries for the character and its alts."""
|
|
78
|
-
if self.section == "summary":
|
|
79
|
-
journal = CharacterWalletJournalEntry.objects.filter(
|
|
80
|
-
self.filter_date,
|
|
81
|
-
character__eve_character__character_id__in=self.alts_ids,
|
|
82
|
-
)
|
|
83
|
-
mining = CharacterMiningLedger.objects.filter(
|
|
84
|
-
self.filter_date,
|
|
85
|
-
character__eve_character__character_id__in=self.alts_ids,
|
|
86
|
-
)
|
|
87
|
-
return journal, mining
|
|
88
|
-
# Get Journal Entries for the Character
|
|
89
|
-
journal = character.ledger_character_journal.filter(self.filter_date)
|
|
90
|
-
mining = character.ledger_character_mining.filter(self.filter_date)
|
|
91
|
-
return journal, mining
|
|
92
|
-
|
|
93
|
-
def generate_ledger_data(self) -> dict:
|
|
94
|
-
"""Generate the ledger data for the character and its alts."""
|
|
95
|
-
# Only show the character if 'single' is set in the request
|
|
96
|
-
if self.section == "single":
|
|
97
|
-
self.ledger_type = "single"
|
|
98
|
-
characters = CharacterAudit.objects.filter(
|
|
99
|
-
eve_character=self.character.eve_character
|
|
100
|
-
).select_related("eve_character")
|
|
101
|
-
character_data = self._create_character_data(character=self.character)
|
|
102
|
-
ledger = character_data
|
|
103
|
-
else:
|
|
104
|
-
ledger = []
|
|
105
|
-
characters = self.characters
|
|
106
|
-
for character in self.characters:
|
|
107
|
-
character_data = self._create_character_data(character=character)
|
|
108
|
-
if character_data:
|
|
109
|
-
ledger.append(character_data)
|
|
110
|
-
|
|
111
|
-
# Billboard
|
|
112
|
-
self.billboard.change_view(self.get_view_mode())
|
|
113
|
-
# Create the ratting bar for the view
|
|
114
|
-
self.create_rattingbar(
|
|
115
|
-
is_old_ess=self.is_old_ess,
|
|
116
|
-
character_ids=characters.values_list(
|
|
117
|
-
"eve_character__character_id", flat=True
|
|
118
|
-
),
|
|
119
|
-
)
|
|
120
|
-
return ledger
|
|
121
|
-
|
|
122
|
-
def _create_character_data(
|
|
123
|
-
self,
|
|
124
|
-
character: CharacterAudit,
|
|
125
|
-
):
|
|
126
|
-
"""Create a dictionary with character data and update billboard/ledger."""
|
|
127
|
-
journal, mining = self.filter_character_journal(character)
|
|
128
|
-
|
|
129
|
-
# If no journal or mining data exists, return None
|
|
130
|
-
if not journal.exists() and not mining.exists():
|
|
131
|
-
return None
|
|
132
|
-
|
|
133
|
-
bounty = journal.aggregate_bounty()
|
|
134
|
-
ess = (
|
|
135
|
-
journal.aggregate_bounty() * Decimal(0.667)
|
|
136
|
-
if self.is_old_ess
|
|
137
|
-
else journal.aggregate_ess()
|
|
138
|
-
)
|
|
139
|
-
mining_val = mining.aggregate_mining()
|
|
140
|
-
costs = journal.aggregate_costs(second_party=self.alts_ids)
|
|
141
|
-
miscellaneous = journal.aggregate_miscellaneous(first_party=self.alts_ids)
|
|
142
|
-
|
|
143
|
-
total = sum(
|
|
144
|
-
[
|
|
145
|
-
bounty,
|
|
146
|
-
ess,
|
|
147
|
-
costs,
|
|
148
|
-
miscellaneous,
|
|
149
|
-
]
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
# If total is 0, we do not need to create a character data entry
|
|
153
|
-
if int(total) + int(mining_val) == 0:
|
|
154
|
-
return None
|
|
155
|
-
|
|
156
|
-
update_states = {}
|
|
157
|
-
|
|
158
|
-
for status in character.ledger_update_status.all():
|
|
159
|
-
update_states[status.section] = {
|
|
160
|
-
"is_success": status.is_success,
|
|
161
|
-
"last_update_finished_at": status.last_update_finished_at,
|
|
162
|
-
"last_run_finished_at": status.last_run_finished_at,
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
char_data = {
|
|
166
|
-
"character": character,
|
|
167
|
-
"ledger": {
|
|
168
|
-
"bounty": bounty,
|
|
169
|
-
"ess": ess,
|
|
170
|
-
"mining": mining_val,
|
|
171
|
-
"costs": costs,
|
|
172
|
-
"miscellaneous": miscellaneous,
|
|
173
|
-
"total": total,
|
|
174
|
-
},
|
|
175
|
-
"update_states": update_states,
|
|
176
|
-
"single_url": self.create_url(
|
|
177
|
-
viewname="character_ledger",
|
|
178
|
-
character_id=character.eve_character.character_id,
|
|
179
|
-
section="single",
|
|
180
|
-
),
|
|
181
|
-
"details_url": self.create_url(
|
|
182
|
-
viewname="character_details",
|
|
183
|
-
character_id=character.eve_character.character_id,
|
|
184
|
-
section="single",
|
|
185
|
-
),
|
|
186
|
-
}
|
|
187
|
-
self.billboard.change_view(self.get_view_mode())
|
|
188
|
-
# Create the chord data for the billboard
|
|
189
|
-
self.billboard.chord_add_data(
|
|
190
|
-
chord_from=character.eve_character.character_name,
|
|
191
|
-
chord_to=_("Bounty"),
|
|
192
|
-
value=bounty,
|
|
193
|
-
)
|
|
194
|
-
self.billboard.chord_add_data(
|
|
195
|
-
chord_from=character.eve_character.character_name,
|
|
196
|
-
chord_to=_("ESS"),
|
|
197
|
-
value=ess,
|
|
198
|
-
)
|
|
199
|
-
self.billboard.chord_add_data(
|
|
200
|
-
chord_from=character.eve_character.character_name,
|
|
201
|
-
chord_to=_("Mining"),
|
|
202
|
-
value=mining_val,
|
|
203
|
-
)
|
|
204
|
-
self.billboard.chord_add_data(
|
|
205
|
-
chord_from=character.eve_character.character_name,
|
|
206
|
-
chord_to=_("Miscellaneous"),
|
|
207
|
-
value=miscellaneous,
|
|
208
|
-
)
|
|
209
|
-
self.billboard.chord_add_data(
|
|
210
|
-
chord_from=character.eve_character.character_name,
|
|
211
|
-
chord_to=_("Costs"),
|
|
212
|
-
value=abs(costs),
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
return char_data
|
|
216
|
-
|
|
217
|
-
def create_rattingbar(self, character_ids: list = None, is_old_ess: bool = False):
|
|
218
|
-
"""Create the ratting bar for the view."""
|
|
219
|
-
if not character_ids:
|
|
220
|
-
return
|
|
221
|
-
|
|
222
|
-
# Create the timeline for the ratting bar
|
|
223
|
-
rattingbar_timeline = self.billboard.create_timeline(
|
|
224
|
-
CharacterWalletJournalEntry.objects.filter(
|
|
225
|
-
self.filter_date,
|
|
226
|
-
character__eve_character__character_id__in=character_ids,
|
|
227
|
-
)
|
|
228
|
-
)
|
|
229
|
-
rattingbar_mining_timeline = self.billboard.create_timeline(
|
|
230
|
-
CharacterMiningLedger.objects.filter(
|
|
231
|
-
self.filter_date,
|
|
232
|
-
character__eve_character__character_id__in=character_ids,
|
|
233
|
-
)
|
|
234
|
-
)
|
|
235
|
-
# Annotate the timeline with the relevant data
|
|
236
|
-
rattingbar = (
|
|
237
|
-
rattingbar_timeline.annotate_bounty_income()
|
|
238
|
-
.annotate_ess_income()
|
|
239
|
-
.annotate_miscellaneous_exclude_donations(exclude=self.alts_ids)
|
|
240
|
-
)
|
|
241
|
-
rattingbar_mining = rattingbar_mining_timeline.annotate_mining(with_period=True)
|
|
242
|
-
|
|
243
|
-
# Generate the XY series for the ratting bar
|
|
244
|
-
self.billboard.create_or_update_results(rattingbar, is_old_ess=is_old_ess)
|
|
245
|
-
self.billboard.add_category(rattingbar_mining, category="mining")
|
|
246
|
-
series, categories = self.billboard.generate_xy_series()
|
|
247
|
-
if series and categories:
|
|
248
|
-
# Create the ratting bar chart
|
|
249
|
-
self.billboard.create_xy_chart(
|
|
250
|
-
title=_("Ratting Bar"), categories=categories, series=series
|
|
251
|
-
)
|