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/general.py
CHANGED
|
@@ -4,18 +4,13 @@ General Model
|
|
|
4
4
|
|
|
5
5
|
# Standard Library
|
|
6
6
|
import datetime
|
|
7
|
-
from collections.abc import Callable
|
|
8
7
|
from dataclasses import dataclass
|
|
9
8
|
from typing import Any, NamedTuple
|
|
10
9
|
|
|
11
|
-
# Third Party
|
|
12
|
-
from aiopenapi3.errors import ContentTypeError, HTTPClientError, HTTPServerError
|
|
13
|
-
|
|
14
10
|
# Django
|
|
15
11
|
from django.core.validators import MinValueValidator
|
|
16
12
|
from django.db import models
|
|
17
13
|
from django.utils import timezone
|
|
18
|
-
from django.utils.safestring import mark_safe
|
|
19
14
|
from django.utils.translation import gettext_lazy as _
|
|
20
15
|
|
|
21
16
|
# Alliance Auth
|
|
@@ -25,17 +20,18 @@ from allianceauth.eveonline.models import (
|
|
|
25
20
|
EveCorporationInfo,
|
|
26
21
|
)
|
|
27
22
|
from allianceauth.services.hooks import get_extension_logger
|
|
28
|
-
from esi.errors import TokenError
|
|
29
|
-
from esi.exceptions import HTTPNotModified
|
|
30
|
-
|
|
31
|
-
# Alliance Auth (External Libs)
|
|
32
|
-
from app_utils.logging import LoggerAddTag
|
|
33
23
|
|
|
34
24
|
# AA Ledger
|
|
35
25
|
from ledger import __title__, app_settings
|
|
26
|
+
from ledger.helpers.eveonline import (
|
|
27
|
+
get_alliance_logo_url,
|
|
28
|
+
get_character_portrait_url,
|
|
29
|
+
get_corporation_logo_url,
|
|
30
|
+
)
|
|
36
31
|
from ledger.managers.general_manager import EveEntityManager
|
|
32
|
+
from ledger.providers import AppLogger
|
|
37
33
|
|
|
38
|
-
logger =
|
|
34
|
+
logger = AppLogger(get_extension_logger(__name__), __title__)
|
|
39
35
|
|
|
40
36
|
# Permission Manager
|
|
41
37
|
|
|
@@ -57,6 +53,11 @@ class General(models.Model):
|
|
|
57
53
|
class EveEntity(models.Model):
|
|
58
54
|
"""An Eve entity like a corporation or a character"""
|
|
59
55
|
|
|
56
|
+
objects: EveEntityManager = EveEntityManager()
|
|
57
|
+
|
|
58
|
+
class Meta:
|
|
59
|
+
default_permissions = ()
|
|
60
|
+
|
|
60
61
|
CATEGORY_ALLIANCE = "alliance"
|
|
61
62
|
CATEGORY_CHARACTER = "character"
|
|
62
63
|
CATEGORY_CORPORATION = "corporation"
|
|
@@ -92,8 +93,6 @@ class EveEntity(models.Model):
|
|
|
92
93
|
)
|
|
93
94
|
last_update = models.DateTimeField(auto_now=True)
|
|
94
95
|
|
|
95
|
-
objects = EveEntityManager()
|
|
96
|
-
|
|
97
96
|
def __str__(self) -> str:
|
|
98
97
|
return str(self.name)
|
|
99
98
|
|
|
@@ -118,6 +117,41 @@ class EveEntity(models.Model):
|
|
|
118
117
|
"""Return True if entity is a character, else False."""
|
|
119
118
|
return self.category == self.CATEGORY_CHARACTER
|
|
120
119
|
|
|
120
|
+
def get_portrait(self, size=64, as_html=False) -> str:
|
|
121
|
+
"""
|
|
122
|
+
Get the portrait URL for this entity.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
size (int, optional): The size of the portrait.
|
|
126
|
+
as_html (bool, optional): Whether to return the portrait as an HTML img tag.
|
|
127
|
+
Returns:
|
|
128
|
+
str: The URL of the portrait or an HTML img tag.
|
|
129
|
+
"""
|
|
130
|
+
if self.category == self.CATEGORY_ALLIANCE:
|
|
131
|
+
return get_alliance_logo_url(
|
|
132
|
+
alliance_id=self.eve_id,
|
|
133
|
+
size=size,
|
|
134
|
+
alliance_name=self.name,
|
|
135
|
+
as_html=as_html,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if self.category == self.CATEGORY_CORPORATION:
|
|
139
|
+
return get_corporation_logo_url(
|
|
140
|
+
corporation_id=self.eve_id,
|
|
141
|
+
size=size,
|
|
142
|
+
corporation_name=self.name,
|
|
143
|
+
as_html=as_html,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if self.category == self.CATEGORY_CHARACTER:
|
|
147
|
+
return get_character_portrait_url(
|
|
148
|
+
character_id=self.eve_id,
|
|
149
|
+
size=size,
|
|
150
|
+
character_name=self.name,
|
|
151
|
+
as_html=as_html,
|
|
152
|
+
)
|
|
153
|
+
return ""
|
|
154
|
+
|
|
121
155
|
def icon_url(self, size=128) -> str:
|
|
122
156
|
"""Url to an icon image for this organization."""
|
|
123
157
|
if self.category == self.CATEGORY_ALLIANCE:
|
|
@@ -136,9 +170,6 @@ class EveEntity(models.Model):
|
|
|
136
170
|
def needs_update(self):
|
|
137
171
|
return self.last_update + datetime.timedelta(days=7) < timezone.now()
|
|
138
172
|
|
|
139
|
-
class Meta:
|
|
140
|
-
default_permissions = ()
|
|
141
|
-
|
|
142
173
|
|
|
143
174
|
class UpdateSectionResult(NamedTuple):
|
|
144
175
|
"""A result of an attempted section update."""
|
|
@@ -165,200 +196,12 @@ class _NeedsUpdate:
|
|
|
165
196
|
return self.section_map.get(section, False)
|
|
166
197
|
|
|
167
198
|
|
|
168
|
-
class
|
|
169
|
-
"""A
|
|
199
|
+
class UpdateStatusBaseModel(models.Model):
|
|
200
|
+
"""A Model to track the status of the last update."""
|
|
170
201
|
|
|
171
202
|
class Meta:
|
|
172
203
|
abstract = True
|
|
173
|
-
|
|
174
|
-
class UpdateStatus(models.TextChoices):
|
|
175
|
-
DISABLED = "disabled", _("disabled")
|
|
176
|
-
TOKEN_ERROR = "token_error", _("token error")
|
|
177
|
-
ERROR = "error", _("error")
|
|
178
|
-
OK = "ok", _("ok")
|
|
179
|
-
INCOMPLETE = "incomplete", _("incomplete")
|
|
180
|
-
IN_PROGRESS = "in_progress", _("in progress")
|
|
181
|
-
|
|
182
|
-
def bootstrap_icon(self) -> str:
|
|
183
|
-
"""Return bootstrap corresponding icon class."""
|
|
184
|
-
update_map = {
|
|
185
|
-
status: mark_safe(
|
|
186
|
-
f"<span class='{self.bootstrap_text_style_class()}' data-tooltip-toggle='ledger-tooltip' title='{self.description()}'>⬤</span>"
|
|
187
|
-
)
|
|
188
|
-
for status in [
|
|
189
|
-
self.DISABLED,
|
|
190
|
-
self.TOKEN_ERROR,
|
|
191
|
-
self.ERROR,
|
|
192
|
-
self.INCOMPLETE,
|
|
193
|
-
self.IN_PROGRESS,
|
|
194
|
-
self.OK,
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
return update_map.get(self, "")
|
|
198
|
-
|
|
199
|
-
def bootstrap_text_style_class(self) -> str:
|
|
200
|
-
"""Return bootstrap corresponding bootstrap text style class."""
|
|
201
|
-
update_map = {
|
|
202
|
-
self.DISABLED: "text-muted",
|
|
203
|
-
self.TOKEN_ERROR: "text-warning",
|
|
204
|
-
self.INCOMPLETE: "text-warning",
|
|
205
|
-
self.IN_PROGRESS: "text-info",
|
|
206
|
-
self.ERROR: "text-danger",
|
|
207
|
-
self.OK: "text-success",
|
|
208
|
-
}
|
|
209
|
-
return update_map.get(self, "")
|
|
210
|
-
|
|
211
|
-
def description(self) -> str:
|
|
212
|
-
"""Return description for an enum object."""
|
|
213
|
-
update_map = {
|
|
214
|
-
self.DISABLED: _("Update is disabled"),
|
|
215
|
-
self.TOKEN_ERROR: _("One section has a token error during update"),
|
|
216
|
-
self.INCOMPLETE: _("One or more sections have not been updated"),
|
|
217
|
-
self.IN_PROGRESS: _("Update is in progress"),
|
|
218
|
-
self.ERROR: _("An error occurred during update"),
|
|
219
|
-
self.OK: _("Updates completed successfully"),
|
|
220
|
-
}
|
|
221
|
-
return update_map.get(self, "")
|
|
222
|
-
|
|
223
|
-
def update_section_if_changed(
|
|
224
|
-
self,
|
|
225
|
-
section: models.TextChoices,
|
|
226
|
-
fetch_func: Callable,
|
|
227
|
-
force_refresh: bool = False,
|
|
228
|
-
):
|
|
229
|
-
"""Update the status of a specific section if it has changed."""
|
|
230
|
-
section = self.UpdateSection(section)
|
|
231
|
-
try:
|
|
232
|
-
data = fetch_func(audit=self, force_refresh=force_refresh)
|
|
233
|
-
logger.debug("%s: Update has changed, section: %s", self, section.label)
|
|
234
|
-
except HTTPServerError as exc:
|
|
235
|
-
logger.debug("%s: Update has an HTTP internal server error: %s", self, exc)
|
|
236
|
-
return UpdateSectionResult(is_changed=False, is_updated=False)
|
|
237
|
-
except HTTPClientError as exc:
|
|
238
|
-
error_message = f"{type(exc).__name__}: {str(exc)}"
|
|
239
|
-
# TODO ADD DISCORD/AUTH NOTIFICATION?
|
|
240
|
-
logger.error(
|
|
241
|
-
"%s: %s: Update has Client Error: %s %s",
|
|
242
|
-
self,
|
|
243
|
-
section.label,
|
|
244
|
-
error_message,
|
|
245
|
-
exc.status_code,
|
|
246
|
-
)
|
|
247
|
-
return UpdateSectionResult(
|
|
248
|
-
is_changed=False,
|
|
249
|
-
is_updated=False,
|
|
250
|
-
has_token_error=True,
|
|
251
|
-
error_message=error_message,
|
|
252
|
-
)
|
|
253
|
-
except HTTPNotModified:
|
|
254
|
-
logger.debug("%s: Update has not changed, section: %s", self, section.label)
|
|
255
|
-
return UpdateSectionResult(is_changed=False, is_updated=False)
|
|
256
|
-
except (OSError, ContentTypeError) as exc:
|
|
257
|
-
logger.info(
|
|
258
|
-
"%s Update has a %s error, section: %s: %s",
|
|
259
|
-
self,
|
|
260
|
-
type(exc).__name__,
|
|
261
|
-
section.label,
|
|
262
|
-
exc,
|
|
263
|
-
)
|
|
264
|
-
return UpdateSectionResult(is_changed=False, is_updated=False)
|
|
265
|
-
return UpdateSectionResult(
|
|
266
|
-
is_changed=True,
|
|
267
|
-
is_updated=True,
|
|
268
|
-
data=data,
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
def reset_has_token_error(self) -> None:
|
|
272
|
-
"""Reset the has_token_error flag for this character."""
|
|
273
|
-
if self.get_status == self.UpdateStatus.TOKEN_ERROR:
|
|
274
|
-
self.update_status.filter(
|
|
275
|
-
has_token_error=True,
|
|
276
|
-
).update(
|
|
277
|
-
has_token_error=False,
|
|
278
|
-
)
|
|
279
|
-
return True
|
|
280
|
-
return False
|
|
281
|
-
|
|
282
|
-
def reset_update_status(self, section):
|
|
283
|
-
"""Reset the status of a given update section and return it."""
|
|
284
|
-
update_status_obj = self.update_status.get_or_create(
|
|
285
|
-
section=section,
|
|
286
|
-
)[0]
|
|
287
|
-
update_status_obj.reset()
|
|
288
|
-
return update_status_obj
|
|
289
|
-
|
|
290
|
-
def perform_update_status(
|
|
291
|
-
self, section, method: Callable, *args, **kwargs
|
|
292
|
-
) -> UpdateSectionResult:
|
|
293
|
-
"""Perform update status."""
|
|
294
|
-
try:
|
|
295
|
-
result = method(*args, **kwargs)
|
|
296
|
-
except Exception as exc:
|
|
297
|
-
error_message = f"{type(exc).__name__}: {str(exc)}"
|
|
298
|
-
is_token_error = isinstance(exc, (TokenError))
|
|
299
|
-
logger.error(
|
|
300
|
-
"%s: %s: Error during update status: %s",
|
|
301
|
-
self,
|
|
302
|
-
section.label,
|
|
303
|
-
error_message,
|
|
304
|
-
exc_info=not is_token_error, # do not log token errors
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
# Update the status using the related manager name
|
|
308
|
-
self.update_status.update_or_create(
|
|
309
|
-
section=section,
|
|
310
|
-
defaults={
|
|
311
|
-
"is_success": False,
|
|
312
|
-
"error_message": error_message,
|
|
313
|
-
"has_token_error": is_token_error,
|
|
314
|
-
"last_update_at": timezone.now(),
|
|
315
|
-
},
|
|
316
|
-
)
|
|
317
|
-
raise exc
|
|
318
|
-
return result
|
|
319
|
-
|
|
320
|
-
def update_section_log(
|
|
321
|
-
self,
|
|
322
|
-
section: models.TextChoices,
|
|
323
|
-
result: UpdateSectionResult,
|
|
324
|
-
) -> None:
|
|
325
|
-
"""Update the status of a specific section."""
|
|
326
|
-
error_message = result.error_message if result.error_message else ""
|
|
327
|
-
is_success = not result.has_token_error
|
|
328
|
-
defaults = {
|
|
329
|
-
"is_success": is_success,
|
|
330
|
-
"error_message": error_message,
|
|
331
|
-
"has_token_error": result.has_token_error,
|
|
332
|
-
"last_run_finished_at": timezone.now(),
|
|
333
|
-
}
|
|
334
|
-
obj: UpdateStatus = self.update_status.update_or_create(
|
|
335
|
-
section=section,
|
|
336
|
-
defaults=defaults,
|
|
337
|
-
)[0]
|
|
338
|
-
if result.is_updated:
|
|
339
|
-
obj.last_update_at = obj.last_run_at
|
|
340
|
-
obj.last_update_finished_at = timezone.now()
|
|
341
|
-
obj.save()
|
|
342
|
-
status = "successfully" if is_success else "with errors"
|
|
343
|
-
logger.info("%s: %s Update run completed %s", self, section.label, status)
|
|
344
|
-
|
|
345
|
-
def calc_update_needed(self) -> _NeedsUpdate:
|
|
346
|
-
"""Calculate if an update is needed."""
|
|
347
|
-
sections_needs_update = {
|
|
348
|
-
section: True for section in self.UpdateSection.get_sections()
|
|
349
|
-
}
|
|
350
|
-
existing_sections: models.QuerySet[UpdateStatus] = self.update_status.all()
|
|
351
|
-
needs_update = {
|
|
352
|
-
obj.section: obj.need_update()
|
|
353
|
-
for obj in existing_sections
|
|
354
|
-
if obj.section in sections_needs_update
|
|
355
|
-
}
|
|
356
|
-
sections_needs_update.update(needs_update)
|
|
357
|
-
return _NeedsUpdate(section_map=sections_needs_update)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
class UpdateStatus(models.Model):
|
|
361
|
-
"""A Model to track the status of the last update."""
|
|
204
|
+
default_permissions = ()
|
|
362
205
|
|
|
363
206
|
is_success = models.BooleanField(default=None, null=True, db_index=True)
|
|
364
207
|
error_message = models.TextField()
|
|
@@ -389,10 +232,6 @@ class UpdateStatus(models.Model):
|
|
|
389
232
|
help_text="Last update has been successful finished at this time",
|
|
390
233
|
)
|
|
391
234
|
|
|
392
|
-
class Meta:
|
|
393
|
-
abstract = True
|
|
394
|
-
default_permissions = ()
|
|
395
|
-
|
|
396
235
|
def need_update(self) -> bool:
|
|
397
236
|
"""Check if the update is needed."""
|
|
398
237
|
if not self.is_success or not self.last_update_finished_at:
|
|
@@ -424,3 +263,58 @@ class UpdateStatus(models.Model):
|
|
|
424
263
|
self.last_run_at = timezone.now()
|
|
425
264
|
self.last_run_finished_at = None
|
|
426
265
|
self.save()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class WalletJournalEntry(models.Model):
|
|
269
|
+
amount = models.DecimalField(
|
|
270
|
+
max_digits=20, decimal_places=2, null=True, default=None
|
|
271
|
+
)
|
|
272
|
+
balance = models.DecimalField(
|
|
273
|
+
max_digits=20, decimal_places=2, null=True, default=None
|
|
274
|
+
)
|
|
275
|
+
context_id = models.BigIntegerField(null=True, default=None)
|
|
276
|
+
|
|
277
|
+
class ContextType(models.TextChoices):
|
|
278
|
+
STRUCTURE_ID = "structure_id"
|
|
279
|
+
STATION_ID = "station_id"
|
|
280
|
+
MARKET_TRANSACTION_ID = "market_transaction_id"
|
|
281
|
+
CHARACTER_ID = "character_id"
|
|
282
|
+
CORPORATION_ID = "corporation_id"
|
|
283
|
+
ALLIANCE_ID = "alliance_id"
|
|
284
|
+
EVE_SYSTEM = "eve_system"
|
|
285
|
+
INDUSTRY_JOB_ID = "industry_job_id"
|
|
286
|
+
CONTRACT_ID = "contract_id"
|
|
287
|
+
PLANET_ID = "planet_id"
|
|
288
|
+
SYSTEM_ID = "system_id"
|
|
289
|
+
TYPE_ID = "type_id"
|
|
290
|
+
|
|
291
|
+
context_id_type = models.CharField(
|
|
292
|
+
max_length=30, choices=ContextType.choices, null=True, default=None
|
|
293
|
+
)
|
|
294
|
+
date = models.DateTimeField()
|
|
295
|
+
description = models.CharField(max_length=500)
|
|
296
|
+
first_party = models.ForeignKey(
|
|
297
|
+
EveEntity,
|
|
298
|
+
on_delete=models.SET_DEFAULT,
|
|
299
|
+
default=None,
|
|
300
|
+
null=True,
|
|
301
|
+
blank=True,
|
|
302
|
+
related_name="+",
|
|
303
|
+
)
|
|
304
|
+
entry_id = models.BigIntegerField()
|
|
305
|
+
reason = models.CharField(max_length=500, null=True, default=None)
|
|
306
|
+
ref_type = models.CharField(max_length=72)
|
|
307
|
+
second_party = models.ForeignKey(
|
|
308
|
+
EveEntity,
|
|
309
|
+
on_delete=models.SET_DEFAULT,
|
|
310
|
+
default=None,
|
|
311
|
+
null=True,
|
|
312
|
+
blank=True,
|
|
313
|
+
related_name="+",
|
|
314
|
+
)
|
|
315
|
+
tax = models.DecimalField(max_digits=20, decimal_places=2, null=True, default=None)
|
|
316
|
+
tax_receiver_id = models.IntegerField(null=True, default=None)
|
|
317
|
+
|
|
318
|
+
class Meta:
|
|
319
|
+
abstract = True
|
|
320
|
+
default_permissions = ()
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Standard Library
|
|
2
|
+
from typing import TYPE_CHECKING, Union
|
|
3
|
+
|
|
4
|
+
# Django
|
|
5
|
+
from django.db import models
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
from django.utils.safestring import mark_safe
|
|
8
|
+
from django.utils.translation import gettext_lazy as _
|
|
9
|
+
|
|
10
|
+
# Alliance Auth
|
|
11
|
+
from allianceauth.services.hooks import get_extension_logger
|
|
12
|
+
from esi.exceptions import HTTPClientError, HTTPNotModified, HTTPServerError
|
|
13
|
+
|
|
14
|
+
# AA Ledger
|
|
15
|
+
from ledger import __title__
|
|
16
|
+
from ledger.models.general import (
|
|
17
|
+
UpdateSectionResult,
|
|
18
|
+
_NeedsUpdate,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
# AA Ledger
|
|
23
|
+
from ledger.models.characteraudit import CharacterOwner, CharacterUpdateStatus
|
|
24
|
+
from ledger.models.corporationaudit import CorporationOwner, CorporationUpdateStatus
|
|
25
|
+
|
|
26
|
+
# AA Ledger
|
|
27
|
+
from ledger.providers import AppLogger
|
|
28
|
+
|
|
29
|
+
logger = AppLogger(get_extension_logger(__name__), __title__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UpdateStatus(models.TextChoices):
|
|
33
|
+
DISABLED = "disabled", _("Disabled")
|
|
34
|
+
TOKEN_ERROR = "token_error", _("Token Error")
|
|
35
|
+
ERROR = "error", _("Error")
|
|
36
|
+
OK = "ok", _("OK")
|
|
37
|
+
INCOMPLETE = "incomplete", _("Incomplete")
|
|
38
|
+
IN_PROGRESS = "in_progress", _("In Progress")
|
|
39
|
+
|
|
40
|
+
def bootstrap_icon(self) -> str:
|
|
41
|
+
"""Return bootstrap corresponding icon class."""
|
|
42
|
+
update_map = {
|
|
43
|
+
status: mark_safe(
|
|
44
|
+
f"<span class='{self.bootstrap_text_style_class()}' data-tooltip-toggle='ledger-tooltip' title='{self.description()}'>⬤</span>"
|
|
45
|
+
)
|
|
46
|
+
for status in [
|
|
47
|
+
self.DISABLED,
|
|
48
|
+
self.TOKEN_ERROR,
|
|
49
|
+
self.ERROR,
|
|
50
|
+
self.INCOMPLETE,
|
|
51
|
+
self.IN_PROGRESS,
|
|
52
|
+
self.OK,
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
return update_map.get(self, "")
|
|
56
|
+
|
|
57
|
+
def bootstrap_text_style_class(self) -> str:
|
|
58
|
+
"""Return bootstrap corresponding bootstrap text style class."""
|
|
59
|
+
update_map = {
|
|
60
|
+
self.DISABLED: "text-muted",
|
|
61
|
+
self.TOKEN_ERROR: "text-warning",
|
|
62
|
+
self.INCOMPLETE: "text-warning",
|
|
63
|
+
self.IN_PROGRESS: "text-info",
|
|
64
|
+
self.ERROR: "text-danger",
|
|
65
|
+
self.OK: "text-success",
|
|
66
|
+
}
|
|
67
|
+
return update_map.get(self, "")
|
|
68
|
+
|
|
69
|
+
def description(self) -> str:
|
|
70
|
+
"""Return description for an enum object."""
|
|
71
|
+
update_map = {
|
|
72
|
+
self.DISABLED: _("Update is disabled"),
|
|
73
|
+
self.TOKEN_ERROR: _("One section has a token error during update"),
|
|
74
|
+
self.INCOMPLETE: _("One or more sections have not been updated"),
|
|
75
|
+
self.IN_PROGRESS: _("Update is in progress"),
|
|
76
|
+
self.ERROR: _("An error occurred during update"),
|
|
77
|
+
self.OK: _("Updates completed successfully"),
|
|
78
|
+
}
|
|
79
|
+
return update_map.get(self, "")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class CharacterUpdateSection(models.TextChoices):
|
|
83
|
+
WALLET_JOURNAL = "wallet_journal", _("Wallet Journal")
|
|
84
|
+
MINING_LEDGER = "mining_ledger", _("Mining Ledger")
|
|
85
|
+
PLANETS = "planets", _("Planets")
|
|
86
|
+
PLANETS_DETAILS = "planets_details", _("Planets Details")
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def get_sections(cls) -> list[str]:
|
|
90
|
+
"""Return list of section values."""
|
|
91
|
+
return [choice.value for choice in cls]
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def method_name(self) -> str:
|
|
95
|
+
"""Return method name for this section."""
|
|
96
|
+
return f"update_{self.value}"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class CorporationUpdateSection(models.TextChoices):
|
|
100
|
+
WALLET_DIVISION_NAMES = "wallet_division_names", _("Divisions Names")
|
|
101
|
+
WALLET_DIVISION = "wallet_division", _("Divisions")
|
|
102
|
+
WALLET_JOURNAL = "wallet_journal", _("Wallet Journal")
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def get_sections(cls) -> list[str]:
|
|
106
|
+
"""Return list of section values."""
|
|
107
|
+
return [choice.value for choice in cls]
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def method_name(self) -> str:
|
|
111
|
+
"""Return method name for this section."""
|
|
112
|
+
return f"update_{self.value}"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class UpdateManager:
|
|
116
|
+
"""Manager class to handle update operations for CharacterOwner and CorporationOwner.
|
|
117
|
+
This class provides methods to manage and track update statuses for both character and corporation owners.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
owner (CorporationOwner | CharacterOwner): The owner model (corporation or character)
|
|
121
|
+
update_section (CorporationUpdateSection | CharacterUpdateSection): The update section class (CorporationUpdateSection or CharacterUpdateSection)
|
|
122
|
+
update_status (CorporationUpdateStatus | CharacterUpdateStatus): The update status class (CorporationUpdateStatus or CharacterUpdateStatus)
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
owner: Union["CorporationOwner", "CharacterOwner"],
|
|
128
|
+
update_section: CorporationUpdateSection | CharacterUpdateSection,
|
|
129
|
+
update_status: Union["CorporationUpdateStatus", "CharacterUpdateStatus"],
|
|
130
|
+
):
|
|
131
|
+
self.owner = owner
|
|
132
|
+
self.update_section = update_section
|
|
133
|
+
self.update_status = update_status
|
|
134
|
+
|
|
135
|
+
# Shared methods
|
|
136
|
+
def calc_update_needed(self) -> _NeedsUpdate:
|
|
137
|
+
"""
|
|
138
|
+
Calculate which sections need an update and save the results in a _NeedsUpdate object.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
_NeedsUpdate: An object containing a mapping of sections to their update needs.
|
|
142
|
+
"""
|
|
143
|
+
sections_needs_update = {
|
|
144
|
+
section: True for section in self.update_section.get_sections()
|
|
145
|
+
}
|
|
146
|
+
existing_sections = self.update_status.objects.filter(owner=self.owner)
|
|
147
|
+
needs_update = {
|
|
148
|
+
obj.section: obj.need_update()
|
|
149
|
+
for obj in existing_sections
|
|
150
|
+
if obj.section in sections_needs_update
|
|
151
|
+
}
|
|
152
|
+
sections_needs_update.update(needs_update)
|
|
153
|
+
return _NeedsUpdate(section_map=sections_needs_update)
|
|
154
|
+
|
|
155
|
+
def reset_update_status(self, section: models.TextChoices):
|
|
156
|
+
"""
|
|
157
|
+
Create or Reset the update status for a specific section.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
section (models.TextChoices): The section to reset.
|
|
161
|
+
Returns:
|
|
162
|
+
UpdateStatus (Object): The reset update status object for the Owner Model.
|
|
163
|
+
"""
|
|
164
|
+
update_status_obj = self.update_status.objects.get_or_create(
|
|
165
|
+
owner=self.owner,
|
|
166
|
+
section=section,
|
|
167
|
+
)[0]
|
|
168
|
+
update_status_obj.reset()
|
|
169
|
+
return update_status_obj
|
|
170
|
+
|
|
171
|
+
def reset_has_token_error(self) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Reset has_token_error for all sections.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
None
|
|
177
|
+
"""
|
|
178
|
+
self.update_status.objects.filter(
|
|
179
|
+
has_token_error=True,
|
|
180
|
+
).update(
|
|
181
|
+
has_token_error=False,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def update_section_if_changed(
|
|
185
|
+
self, section: models.TextChoices, fetch_func, force_refresh: bool = False
|
|
186
|
+
):
|
|
187
|
+
"""
|
|
188
|
+
Handle updating a specific section if there are changes.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
section (models.TextChoices): The section to update.
|
|
192
|
+
fetch_func (Callable): The function to fetch the data for the section.
|
|
193
|
+
force_refresh (bool): Whether to force a refresh of the data.
|
|
194
|
+
Returns:
|
|
195
|
+
UpdateSectionResult: The result of the update operation.
|
|
196
|
+
Raises:
|
|
197
|
+
HTTPClientError: If there is a client error during the fetch.
|
|
198
|
+
HTTPServerError: If there is a server error during the fetch.
|
|
199
|
+
HTTPNotModified: If the data has not been modified.
|
|
200
|
+
"""
|
|
201
|
+
section = self.update_section(section)
|
|
202
|
+
try:
|
|
203
|
+
data = fetch_func(owner=self.owner, force_refresh=force_refresh)
|
|
204
|
+
logger.debug(
|
|
205
|
+
"%s: Update has changed, section: %s", self.owner, section.label
|
|
206
|
+
)
|
|
207
|
+
except HTTPNotModified:
|
|
208
|
+
logger.debug(
|
|
209
|
+
"%s: Update has not changed, section: %s", self.owner, section.label
|
|
210
|
+
)
|
|
211
|
+
return UpdateSectionResult(is_changed=False, is_updated=False)
|
|
212
|
+
except HTTPClientError as exc:
|
|
213
|
+
error_message = f"{type(exc).__name__}: {str(exc)}"
|
|
214
|
+
logger.error(
|
|
215
|
+
"%s: %s: Update has Client Error: %s %s",
|
|
216
|
+
self.owner,
|
|
217
|
+
section.label,
|
|
218
|
+
error_message,
|
|
219
|
+
exc.status_code,
|
|
220
|
+
)
|
|
221
|
+
return UpdateSectionResult(
|
|
222
|
+
is_changed=False,
|
|
223
|
+
is_updated=False,
|
|
224
|
+
has_token_error=True,
|
|
225
|
+
error_message=error_message,
|
|
226
|
+
)
|
|
227
|
+
return UpdateSectionResult(
|
|
228
|
+
is_changed=True,
|
|
229
|
+
is_updated=True,
|
|
230
|
+
data=data,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def update_section_log(
|
|
234
|
+
self, section: models.TextChoices, result: UpdateSectionResult
|
|
235
|
+
) -> None:
|
|
236
|
+
"""
|
|
237
|
+
Update the status of a specific section.
|
|
238
|
+
Args:
|
|
239
|
+
section (models.TextChoices): The section to update.
|
|
240
|
+
result (UpdateSectionResult): The result of the update operation.
|
|
241
|
+
Returns:
|
|
242
|
+
None
|
|
243
|
+
"""
|
|
244
|
+
error_message = result.error_message if result.error_message else ""
|
|
245
|
+
is_success = not result.has_token_error
|
|
246
|
+
defaults = {
|
|
247
|
+
"is_success": is_success,
|
|
248
|
+
"error_message": error_message,
|
|
249
|
+
"has_token_error": result.has_token_error,
|
|
250
|
+
"last_run_finished_at": timezone.now(),
|
|
251
|
+
}
|
|
252
|
+
obj = self.update_status.objects.update_or_create(
|
|
253
|
+
owner=self.owner,
|
|
254
|
+
section=section,
|
|
255
|
+
defaults=defaults,
|
|
256
|
+
)[0]
|
|
257
|
+
if result.is_updated:
|
|
258
|
+
obj.last_update_at = obj.last_run_at
|
|
259
|
+
obj.last_update_finished_at = timezone.now()
|
|
260
|
+
obj.save()
|
|
261
|
+
status = "successfully" if is_success else "with errors"
|
|
262
|
+
logger.info("%s: %s Update run completed %s", self.owner, section.label, status)
|
|
263
|
+
|
|
264
|
+
def perform_update_status(
|
|
265
|
+
self, section: models.TextChoices, method, *args, **kwargs
|
|
266
|
+
):
|
|
267
|
+
"""
|
|
268
|
+
Perform update status.
|
|
269
|
+
Args:
|
|
270
|
+
section (models.TextChoices): The section to update.
|
|
271
|
+
method (Callable): The method to perform the update.
|
|
272
|
+
*args: Positional arguments for the method.
|
|
273
|
+
**kwargs: Keyword arguments for the method.
|
|
274
|
+
Returns:
|
|
275
|
+
Any: The result of the method call.
|
|
276
|
+
Raises:
|
|
277
|
+
Exception: Reraises any exception encountered during the method call.
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
result = method(*args, **kwargs)
|
|
281
|
+
except HTTPServerError as exc:
|
|
282
|
+
raise exc
|
|
283
|
+
except Exception as exc:
|
|
284
|
+
error_message = f"{type(exc).__name__}: {str(exc)}"
|
|
285
|
+
logger.error(
|
|
286
|
+
"%s: %s: Error during update status: %s",
|
|
287
|
+
self.owner,
|
|
288
|
+
section.label,
|
|
289
|
+
error_message,
|
|
290
|
+
)
|
|
291
|
+
self.update_status.objects.update_or_create(
|
|
292
|
+
owner=self.owner,
|
|
293
|
+
section=section,
|
|
294
|
+
defaults={
|
|
295
|
+
"is_success": False,
|
|
296
|
+
"error_message": error_message,
|
|
297
|
+
"has_token_error": False,
|
|
298
|
+
"last_update_at": timezone.now(),
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
raise exc
|
|
302
|
+
return result
|