aa-alumni 1.0.0b1__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_alumni-1.0.0b1.dist-info/METADATA +100 -0
- aa_alumni-1.0.0b1.dist-info/RECORD +24 -0
- aa_alumni-1.0.0b1.dist-info/WHEEL +4 -0
- aa_alumni-1.0.0b1.dist-info/licenses/LICENSE +21 -0
- alumni/__init__.py +10 -0
- alumni/admin.py +37 -0
- alumni/app_settings.py +9 -0
- alumni/apps.py +10 -0
- alumni/locale/de/LC_MESSAGES/django.mo +0 -0
- alumni/locale/de/LC_MESSAGES/django.po +53 -0
- alumni/locale/en/LC_MESSAGES/django.mo +0 -0
- alumni/locale/en/LC_MESSAGES/django.po +43 -0
- alumni/management/commands/__init__.py +0 -0
- alumni/management/commands/alumni_state.py +24 -0
- alumni/migrations/0001_initial.py +69 -0
- alumni/migrations/0002_auto_20211230_0147.py +23 -0
- alumni/migrations/0003_alter_alumnisetup_options_and_more.py +27 -0
- alumni/migrations/0004_corporationupdatetimestamp_alter_alumnisetup_options_and_more.py +51 -0
- alumni/migrations/__init__.py +0 -0
- alumni/models.py +80 -0
- alumni/providers.py +38 -0
- alumni/signals.py +24 -0
- alumni/tasks.py +288 -0
- alumni/tests/__init__.py +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aa-alumni
|
|
3
|
+
Version: 1.0.0b1
|
|
4
|
+
Summary: Integration with Alliance Auths State System
|
|
5
|
+
Keywords: allianceauth,eveonline
|
|
6
|
+
Author-email: Joel Falknau <joel.falknau@gmail.com>
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Environment :: Web Environment
|
|
10
|
+
Classifier: Framework :: Celery
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Framework :: Django :: 4.2
|
|
13
|
+
Classifier: Framework :: Django :: 5.2
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: allianceauth>=4.6.4,<6
|
|
29
|
+
Requires-Dist: django-esi>=8,<9
|
|
30
|
+
Project-URL: Homepage, https://gitlab.com/tactical-supremacy/aa-alumni
|
|
31
|
+
Project-URL: Source, https://gitlab.com/tactical-supremacy/aa-alumni
|
|
32
|
+
Project-URL: Tracker, https://gitlab.com/tactical-supremacy/aa-alumni/-/issues
|
|
33
|
+
|
|
34
|
+
# Alliance Auth - Alumni
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- Integration with Alliance Auth's State System, creates and maintains an Alumni State for past members of an Alliance and/or Corporation.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
### Step 1 - Prepare Auth
|
|
43
|
+
|
|
44
|
+
Remove/Promote any state with a priority of `1`, Alumni is considered slightly better than the built in Guest State.
|
|
45
|
+
|
|
46
|
+
### Step 2 - Install from pip
|
|
47
|
+
|
|
48
|
+
```shell
|
|
49
|
+
pip install aa-alumni
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 3 - Configure Auth settings
|
|
53
|
+
|
|
54
|
+
Configure your Auth settings (`local.py`) as follows:
|
|
55
|
+
|
|
56
|
+
- Add `'alumni'` to `INSTALLED_APPS`
|
|
57
|
+
- Add below lines to your settings file:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
## Settings for AA-Alumni
|
|
61
|
+
# Tasks
|
|
62
|
+
CELERYBEAT_SCHEDULE['alumni_run_alumni_check_all'] = {
|
|
63
|
+
'task': 'alumni.tasks.run_alumni_check_all',
|
|
64
|
+
'schedule': crontab(minute="0", hour="0", day_of_week="4"),
|
|
65
|
+
'apply_offset': True,
|
|
66
|
+
}
|
|
67
|
+
CELERYBEAT_SCHEDULE['alumni_run_update_all_models'] = {
|
|
68
|
+
'task': 'alumni.tasks.update_all_models',
|
|
69
|
+
'schedule': crontab(minute="0", hour="0", day_of_week="3"),
|
|
70
|
+
'apply_offset': True,
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Step 4 - Update AA's State system
|
|
75
|
+
|
|
76
|
+
```shell
|
|
77
|
+
python myauth/manage.py alumni_state
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 5 - Maintain Alliance Auth
|
|
81
|
+
|
|
82
|
+
- Run migrations `python manage.py migrate`
|
|
83
|
+
- Gather your staticfiles `python manage.py collectstatic`
|
|
84
|
+
- Restart your project `supervisorctl restart myauth:`
|
|
85
|
+
|
|
86
|
+
### Step 6 - Configure Further
|
|
87
|
+
|
|
88
|
+
In the Admin interface, visit `alumni > config > add` or `<AUTH-URL>/admin/alumni/config/add/`
|
|
89
|
+
Select the Alliances and/or Corporations for which characters with historical membership are Alumni
|
|
90
|
+
|
|
91
|
+
## Settings
|
|
92
|
+
|
|
93
|
+
| Name | Description | Default |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
|`ALUMNI_CHARACTERCORPORATION_RATELIMIT`| Celery Rate Limit _per worker_, 10 tasks * 10 Workers = 100 tasks/min | '10/m' |
|
|
96
|
+
|
|
97
|
+
## Contributing
|
|
98
|
+
|
|
99
|
+
Make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at <https://developers.eveonline.com> before submitting any pull requests. All bug fixes or features must not include extra superfluous formatting changes.
|
|
100
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
alumni/__init__.py,sha256=oYGhAgysyvO0h-v434BQRXMOWqlEmjgjoamWPFb4Qys,293
|
|
2
|
+
alumni/admin.py,sha256=BiqnT1Gk_wJOJ1eeIFbtgQf1n2q5MLf8Wkc4CqSz5Vw,1271
|
|
3
|
+
alumni/app_settings.py,sha256=soF5EtRwxvWG2ARqzm7e7Qe5dpp-NCPZ-um781fENvs,383
|
|
4
|
+
alumni/apps.py,sha256=JNO5V_uldj3Y2OEJL0h4u86LoPZr5UOhzkcTFZ6VeNI,239
|
|
5
|
+
alumni/models.py,sha256=UPr_xGxiRwZrFB2nmhdMPXxCVCiN7RBKGeu-ULNjGd0,3036
|
|
6
|
+
alumni/providers.py,sha256=qUK2zX_DvGOwbgozn8dlgKXZ87e4Zpl4ZnYMEDLjjEY,1309
|
|
7
|
+
alumni/signals.py,sha256=WpeyRa8AGfcWAR9qvy3FgfdP2zNLBzqrh7xm1cH5Klg,835
|
|
8
|
+
alumni/tasks.py,sha256=r-y0M5ueWxlsdMgN-75MSU4b75zdLHXlX57xcL2Yw7Q,11991
|
|
9
|
+
alumni/locale/de/LC_MESSAGES/django.mo,sha256=hTZUh1Insgy-eZmz0-Owvgy9Mh6tNw3p2X9J6tD4TwE,1250
|
|
10
|
+
alumni/locale/de/LC_MESSAGES/django.po,sha256=aEAiaM_iPQFp3BCjD0-WEKRMrtLSIn2gp7INLJ5KbJY,1744
|
|
11
|
+
alumni/locale/en/LC_MESSAGES/django.mo,sha256=N1pb17IfLd0ASiKO8d68-B4ygSpDkhKOCs8YTzMXQo0,380
|
|
12
|
+
alumni/locale/en/LC_MESSAGES/django.po,sha256=dW_HNYdzscfIrGUoGA1-0PwV0LNn7-WdgXmdMU7pGm8,1250
|
|
13
|
+
alumni/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
alumni/management/commands/alumni_state.py,sha256=OyJKnkzk9eksk-vULSVBdGvMEhxZX_xa7eH5RXFjeJ8,759
|
|
15
|
+
alumni/migrations/0001_initial.py,sha256=C8R2k4y5KvpTLU_etBQ0J3jkGYd3HAgevAekUZvzxDw,3283
|
|
16
|
+
alumni/migrations/0002_auto_20211230_0147.py,sha256=p3sK1Ohdf6O20tDbMmuPJwC2vVFH-AR0kkXFy9H34_Y,626
|
|
17
|
+
alumni/migrations/0003_alter_alumnisetup_options_and_more.py,sha256=JyXQqo9vR-hRx7CHsNpXLxZEjEijSBLW3zX7Zmw3Y50,1012
|
|
18
|
+
alumni/migrations/0004_corporationupdatetimestamp_alter_alumnisetup_options_and_more.py,sha256=5jDhobyP_AfnBDnKlfAodriKbHrX4MyYMGf1A4oUk8w,2112
|
|
19
|
+
alumni/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
alumni/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
aa_alumni-1.0.0b1.dist-info/licenses/LICENSE,sha256=6TxpZBZY7OLGLrJXe6vREu--u7CjCfW19u4oXW_uZKs,1069
|
|
22
|
+
aa_alumni-1.0.0b1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
23
|
+
aa_alumni-1.0.0b1.dist-info/METADATA,sha256=oDf7ZDVPX1Hvo4mt7SaeDFxHpfE9W6K9SQ5v7urrVkg,3448
|
|
24
|
+
aa_alumni-1.0.0b1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Joel Falknau
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
alumni/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration with Alliance Auths State System
|
|
3
|
+
|
|
4
|
+
Creates and maintains an Alumni State for past members of an Alliance and/or Corporation
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0b1"
|
|
8
|
+
__title__ = "AAAlumni"
|
|
9
|
+
__url__ = "https://gitlab.com/tactical-supremacy/aa-alumni"
|
|
10
|
+
__esi_compatibility_date__ = "2025-11-06"
|
alumni/admin.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from solo.admin import SingletonModelAdmin
|
|
2
|
+
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
|
|
5
|
+
from .models import (
|
|
6
|
+
AlumniSetup, CharacterCorporationHistory, CharacterUpdateTimestamp,
|
|
7
|
+
CorporationAllianceHistory, CorporationUpdateTimestamp,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@admin.register(AlumniSetup)
|
|
12
|
+
class AlumniSetupAdmin(SingletonModelAdmin):
|
|
13
|
+
filter_horizontal = ["alumni_corporations", "alumni_alliances"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@admin.register(CorporationAllianceHistory)
|
|
17
|
+
class CorporationAllianceHistoryAdmin(admin.ModelAdmin):
|
|
18
|
+
search_fields = ['corporation_id', 'alliance_id']
|
|
19
|
+
list_display = ('corporation_id', 'alliance_id', 'record_id', 'start_date')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@admin.register(CharacterCorporationHistory)
|
|
23
|
+
class CharacterCorporationHistoryAdmin(admin.ModelAdmin):
|
|
24
|
+
search_fields = ['corporation_id', 'character']
|
|
25
|
+
list_display = ('corporation_id', 'character', 'record_id', 'start_date')
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@admin.register(CharacterUpdateTimestamp)
|
|
29
|
+
class CharacterUpdateTimestampAdmin(admin.ModelAdmin):
|
|
30
|
+
search_fields = ['character__character_name', 'character__character_id']
|
|
31
|
+
list_display = ('character', 'last_updated')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@admin.register(CorporationUpdateTimestamp)
|
|
35
|
+
class CorporationUpdateTimestampAdmin(admin.ModelAdmin):
|
|
36
|
+
search_fields = ['corporation_id']
|
|
37
|
+
list_display = ('corporation_id', 'last_updated')
|
alumni/app_settings.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
ALUMNI_STATE_NAME = getattr(settings, 'ALUMNI_STATE_NAME', "Alumni")
|
|
4
|
+
|
|
5
|
+
ALUMNI_TASK_PRIORITY = getattr(settings, 'ALUMNI_TASK_PRIORITY', 7)
|
|
6
|
+
|
|
7
|
+
ALUMNI_CHARACTERCORPORATION_RATELIMIT = getattr(settings, 'ALUMNI_CHARACTERCORPORATION_RATELIMIT', '10/m') # 10*10workers = 100/300 per min
|
|
8
|
+
|
|
9
|
+
ALUMNI_TASK_JITTER = getattr(settings, 'ALUMNI_TASK_PRIORITY', 600)
|
alumni/apps.py
ADDED
|
Binary file
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
|
+
#
|
|
6
|
+
# Translators:
|
|
7
|
+
# Peter Pfeufer, 2024
|
|
8
|
+
#
|
|
9
|
+
#, fuzzy
|
|
10
|
+
msgid ""
|
|
11
|
+
msgstr ""
|
|
12
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
13
|
+
"Report-Msgid-Bugs-To: \n"
|
|
14
|
+
"POT-Creation-Date: 2024-05-11 18:13+1000\n"
|
|
15
|
+
"PO-Revision-Date: 2024-05-11 10:42+0000\n"
|
|
16
|
+
"Last-Translator: Peter Pfeufer, 2024\n"
|
|
17
|
+
"Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n"
|
|
18
|
+
"MIME-Version: 1.0\n"
|
|
19
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
20
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
21
|
+
"Language: de\n"
|
|
22
|
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
23
|
+
|
|
24
|
+
#: alumni/models.py:14
|
|
25
|
+
msgid ""
|
|
26
|
+
"Characters with these Corps in their History will be given Alumni Status"
|
|
27
|
+
msgstr ""
|
|
28
|
+
"Charaktere mit diesen Corps in ihrer Vergangenheit erhalten den Alumni-"
|
|
29
|
+
"Status"
|
|
30
|
+
|
|
31
|
+
#: alumni/models.py:18
|
|
32
|
+
msgid ""
|
|
33
|
+
"Characters with these Alliances in their History will be given Alumni Status"
|
|
34
|
+
msgstr ""
|
|
35
|
+
"Charaktere mit diesen Allianzen in ihrer Vergangenheit erhalten den Alumni-"
|
|
36
|
+
"Status"
|
|
37
|
+
|
|
38
|
+
#: alumni/models.py:21 alumni/models.py:24 alumni/models.py:25
|
|
39
|
+
msgid "Alumni Config"
|
|
40
|
+
msgstr "Alumni-Konfiguration"
|
|
41
|
+
|
|
42
|
+
#: alumni/models.py:37 alumni/models.py:52
|
|
43
|
+
msgid "True if the corporation has been deleted"
|
|
44
|
+
msgstr "Wahr, wenn die Corporation gelöscht wurde"
|
|
45
|
+
|
|
46
|
+
#: alumni/models.py:39 alumni/models.py:54
|
|
47
|
+
msgid ""
|
|
48
|
+
"An incrementing ID that can be used to canonically establish order of "
|
|
49
|
+
"records in cases where dates may be ambiguous"
|
|
50
|
+
msgstr ""
|
|
51
|
+
"Eine aufsteigende ID, die zur kanonischen Festlegung der Reihenfolge von "
|
|
52
|
+
"Datensätzen in Fällen verwendet werden kann, in denen Daten möglicherweise "
|
|
53
|
+
"nicht eindeutig sind"
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# SOME DESCRIPTIVE TITLE.
|
|
2
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
3
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
5
|
+
#
|
|
6
|
+
#, fuzzy
|
|
7
|
+
msgid ""
|
|
8
|
+
msgstr ""
|
|
9
|
+
"Project-Id-Version: PACKAGE VERSION\n"
|
|
10
|
+
"Report-Msgid-Bugs-To: \n"
|
|
11
|
+
"POT-Creation-Date: 2024-08-29 14:59+1000\n"
|
|
12
|
+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
|
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
15
|
+
"Language: \n"
|
|
16
|
+
"MIME-Version: 1.0\n"
|
|
17
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
18
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
19
|
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
20
|
+
|
|
21
|
+
#: alumni/models.py:15
|
|
22
|
+
msgid ""
|
|
23
|
+
"Characters with these Corps in their History will be given Alumni Status"
|
|
24
|
+
msgstr ""
|
|
25
|
+
|
|
26
|
+
#: alumni/models.py:19
|
|
27
|
+
msgid ""
|
|
28
|
+
"Characters with these Alliances in their History will be given Alumni Status"
|
|
29
|
+
msgstr ""
|
|
30
|
+
|
|
31
|
+
#: alumni/models.py:22 alumni/models.py:25 alumni/models.py:26
|
|
32
|
+
msgid "Alumni Config"
|
|
33
|
+
msgstr ""
|
|
34
|
+
|
|
35
|
+
#: alumni/models.py:38 alumni/models.py:53
|
|
36
|
+
msgid "True if the corporation has been deleted"
|
|
37
|
+
msgstr ""
|
|
38
|
+
|
|
39
|
+
#: alumni/models.py:40 alumni/models.py:55
|
|
40
|
+
msgid ""
|
|
41
|
+
"An incrementing ID that can be used to canonically establish order of "
|
|
42
|
+
"records in cases where dates may be ambiguous"
|
|
43
|
+
msgstr ""
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
|
|
3
|
+
from allianceauth.authentication.models import State
|
|
4
|
+
|
|
5
|
+
from alumni import app_settings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Command(BaseCommand):
|
|
9
|
+
help = 'Setup/Reset/Fix the Alumni State for the Alumni Module'
|
|
10
|
+
|
|
11
|
+
def handle(self, *args, **options):
|
|
12
|
+
self.stdout.write("Creating/Reseting/Fixing the Alumni State for the Alumni Module")
|
|
13
|
+
alumni_name = app_settings.ALUMNI_STATE_NAME
|
|
14
|
+
priority = 1
|
|
15
|
+
|
|
16
|
+
created = State.objects.update_or_create(
|
|
17
|
+
name=alumni_name,
|
|
18
|
+
defaults={
|
|
19
|
+
"priority": priority, }
|
|
20
|
+
)
|
|
21
|
+
if created:
|
|
22
|
+
self.stdout.write("Success! Created Alumni State")
|
|
23
|
+
else:
|
|
24
|
+
self.stdout.write("Success! Updated Alumni State")
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Generated by Django 3.2.10 on 2021-12-29 11:41
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
('eveonline', '0015_factions'),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.CreateModel(
|
|
17
|
+
name='AlumniSetup',
|
|
18
|
+
fields=[
|
|
19
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
20
|
+
],
|
|
21
|
+
options={
|
|
22
|
+
'verbose_name_plural': 'Alumni Config',
|
|
23
|
+
},
|
|
24
|
+
),
|
|
25
|
+
migrations.CreateModel(
|
|
26
|
+
name='CharacterCorporationHistory',
|
|
27
|
+
fields=[
|
|
28
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
29
|
+
('corporation_id', models.PositiveIntegerField()),
|
|
30
|
+
('is_deleted', models.BooleanField(default=False, help_text='True if the corporation has been deleted')),
|
|
31
|
+
('record_id', models.IntegerField(help_text='An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous')),
|
|
32
|
+
('start_date', models.DateTimeField()),
|
|
33
|
+
],
|
|
34
|
+
),
|
|
35
|
+
migrations.CreateModel(
|
|
36
|
+
name='CorporationAllianceHistory',
|
|
37
|
+
fields=[
|
|
38
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
39
|
+
('corporation_id', models.PositiveIntegerField()),
|
|
40
|
+
('alliance_id', models.PositiveIntegerField(blank=True, null=True)),
|
|
41
|
+
('is_deleted', models.BooleanField(default=False, help_text='True if the corporation has been deleted')),
|
|
42
|
+
('record_id', models.IntegerField(help_text='An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous')),
|
|
43
|
+
('start_date', models.DateTimeField()),
|
|
44
|
+
],
|
|
45
|
+
),
|
|
46
|
+
migrations.AddConstraint(
|
|
47
|
+
model_name='corporationalliancehistory',
|
|
48
|
+
constraint=models.UniqueConstraint(fields=('corporation_id', 'record_id'), name='CorporationAllianceRecord'),
|
|
49
|
+
),
|
|
50
|
+
migrations.AddField(
|
|
51
|
+
model_name='charactercorporationhistory',
|
|
52
|
+
name='character',
|
|
53
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eveonline.evecharacter'),
|
|
54
|
+
),
|
|
55
|
+
migrations.AddField(
|
|
56
|
+
model_name='alumnisetup',
|
|
57
|
+
name='alumni_alliances',
|
|
58
|
+
field=models.ManyToManyField(blank=True, help_text='Characters with these Alliances in their History will be given Alumni Status', to='eveonline.EveAllianceInfo'),
|
|
59
|
+
),
|
|
60
|
+
migrations.AddField(
|
|
61
|
+
model_name='alumnisetup',
|
|
62
|
+
name='alumni_corporations',
|
|
63
|
+
field=models.ManyToManyField(blank=True, help_text='Characters with these Corps in their History will be given Alumni Status', to='eveonline.EveCorporationInfo'),
|
|
64
|
+
),
|
|
65
|
+
migrations.AddConstraint(
|
|
66
|
+
model_name='charactercorporationhistory',
|
|
67
|
+
constraint=models.UniqueConstraint(fields=('character', 'record_id'), name='CharacterCorporationRecord'),
|
|
68
|
+
),
|
|
69
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 3.2.10 on 2021-12-30 01:47
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('alumni', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='corporationalliancehistory',
|
|
15
|
+
name='alliance_id',
|
|
16
|
+
field=models.PositiveIntegerField(blank=True, db_index=True, null=True),
|
|
17
|
+
),
|
|
18
|
+
migrations.AlterField(
|
|
19
|
+
model_name='corporationalliancehistory',
|
|
20
|
+
name='corporation_id',
|
|
21
|
+
field=models.PositiveIntegerField(db_index=True),
|
|
22
|
+
),
|
|
23
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-05-11 08:13
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('alumni', '0002_auto_20211230_0147'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='alumnisetup',
|
|
15
|
+
options={'verbose_name': 'Alumni Config', 'verbose_name_plural': 'Alumni Config'},
|
|
16
|
+
),
|
|
17
|
+
migrations.AlterField(
|
|
18
|
+
model_name='charactercorporationhistory',
|
|
19
|
+
name='record_id',
|
|
20
|
+
field=models.PositiveIntegerField(help_text='An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous'),
|
|
21
|
+
),
|
|
22
|
+
migrations.AlterField(
|
|
23
|
+
model_name='corporationalliancehistory',
|
|
24
|
+
name='record_id',
|
|
25
|
+
field=models.PositiveIntegerField(help_text='An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous'),
|
|
26
|
+
),
|
|
27
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Generated by Django 4.2.26 on 2025-11-14 03:28
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
('eveonline', '0017_alliance_and_corp_names_are_not_unique'),
|
|
11
|
+
('alumni', '0003_alter_alumnisetup_options_and_more'),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name='CorporationUpdateTimestamp',
|
|
17
|
+
fields=[
|
|
18
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
19
|
+
('corporation_id', models.PositiveIntegerField(unique=True)),
|
|
20
|
+
('last_updated', models.DateTimeField(auto_now=True)),
|
|
21
|
+
],
|
|
22
|
+
options={
|
|
23
|
+
'verbose_name': 'Corporation Update Timestamp',
|
|
24
|
+
'verbose_name_plural': 'Corporation Update Timestamps',
|
|
25
|
+
},
|
|
26
|
+
),
|
|
27
|
+
migrations.AlterModelOptions(
|
|
28
|
+
name='alumnisetup',
|
|
29
|
+
options={'verbose_name': 'Alumni Config'},
|
|
30
|
+
),
|
|
31
|
+
migrations.AlterModelOptions(
|
|
32
|
+
name='charactercorporationhistory',
|
|
33
|
+
options={'verbose_name': 'Character/Corporation History', 'verbose_name_plural': 'Character/Corporation Histories'},
|
|
34
|
+
),
|
|
35
|
+
migrations.AlterModelOptions(
|
|
36
|
+
name='corporationalliancehistory',
|
|
37
|
+
options={'verbose_name': 'Corporation/Alliance History', 'verbose_name_plural': 'Corporation/Alliance Histories'},
|
|
38
|
+
),
|
|
39
|
+
migrations.CreateModel(
|
|
40
|
+
name='CharacterUpdateTimestamp',
|
|
41
|
+
fields=[
|
|
42
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
43
|
+
('last_updated', models.DateTimeField(auto_now=True)),
|
|
44
|
+
('character', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='eveonline.evecharacter')),
|
|
45
|
+
],
|
|
46
|
+
options={
|
|
47
|
+
'verbose_name': 'Character Update Timestamp',
|
|
48
|
+
'verbose_name_plural': 'Character Update Timestamps',
|
|
49
|
+
},
|
|
50
|
+
),
|
|
51
|
+
]
|
|
File without changes
|
alumni/models.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from solo.models import SingletonModel
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.utils.translation import gettext as _
|
|
5
|
+
|
|
6
|
+
from allianceauth.eveonline.models import (
|
|
7
|
+
EveAllianceInfo, EveCharacter, EveCorporationInfo,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AlumniSetup(SingletonModel):
|
|
12
|
+
alumni_corporations = models.ManyToManyField(
|
|
13
|
+
EveCorporationInfo,
|
|
14
|
+
blank=True,
|
|
15
|
+
help_text=_("Characters with these Corps in their History will be given Alumni Status"))
|
|
16
|
+
alumni_alliances = models.ManyToManyField(
|
|
17
|
+
EveAllianceInfo,
|
|
18
|
+
blank=True,
|
|
19
|
+
help_text=_("Characters with these Alliances in their History will be given Alumni Status"))
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
return _("Alumni Config")
|
|
23
|
+
|
|
24
|
+
class Meta:
|
|
25
|
+
verbose_name = _("Alumni Config")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CorporationUpdateTimestamp(models.Model):
|
|
29
|
+
corporation_id = models.PositiveIntegerField(unique=True)
|
|
30
|
+
last_updated = models.DateTimeField(auto_now=True)
|
|
31
|
+
|
|
32
|
+
class Meta:
|
|
33
|
+
verbose_name = _("Corporation Update Timestamp")
|
|
34
|
+
verbose_name_plural = _("Corporation Update Timestamps")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CharacterUpdateTimestamp(models.Model):
|
|
38
|
+
character = models.OneToOneField(EveCharacter, on_delete=models.CASCADE)
|
|
39
|
+
last_updated = models.DateTimeField(auto_now=True)
|
|
40
|
+
|
|
41
|
+
class Meta:
|
|
42
|
+
verbose_name = _("Character Update Timestamp")
|
|
43
|
+
verbose_name_plural = _("Character Update Timestamps")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CorporationAllianceHistory(models.Model):
|
|
47
|
+
corporation_id = models.PositiveIntegerField(db_index=True)
|
|
48
|
+
alliance_id = models.PositiveIntegerField(blank=True, null=True, db_index=True)
|
|
49
|
+
is_deleted = models.BooleanField(
|
|
50
|
+
default=False,
|
|
51
|
+
help_text=_("True if the corporation has been deleted"))
|
|
52
|
+
record_id = models.PositiveIntegerField(
|
|
53
|
+
help_text=_("An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous"))
|
|
54
|
+
start_date = models.DateTimeField()
|
|
55
|
+
|
|
56
|
+
class Meta:
|
|
57
|
+
verbose_name = _("Corporation/Alliance History")
|
|
58
|
+
verbose_name_plural = _("Corporation/Alliance Histories")
|
|
59
|
+
constraints = [
|
|
60
|
+
models.UniqueConstraint(fields=['corporation_id', 'record_id'], name="CorporationAllianceRecord"),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CharacterCorporationHistory(models.Model):
|
|
65
|
+
|
|
66
|
+
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
|
|
67
|
+
corporation_id = models.PositiveIntegerField()
|
|
68
|
+
is_deleted = models.BooleanField(
|
|
69
|
+
default=False,
|
|
70
|
+
help_text=_("True if the corporation has been deleted"))
|
|
71
|
+
record_id = models.PositiveIntegerField(
|
|
72
|
+
help_text=_("An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous"))
|
|
73
|
+
start_date = models.DateTimeField()
|
|
74
|
+
|
|
75
|
+
class Meta:
|
|
76
|
+
verbose_name = _("Character/Corporation History")
|
|
77
|
+
verbose_name_plural = _("Character/Corporation Histories")
|
|
78
|
+
constraints = [
|
|
79
|
+
models.UniqueConstraint(fields=['character', 'record_id'], name="CharacterCorporationRecord"),
|
|
80
|
+
]
|
alumni/providers.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from allianceauth.services.hooks import get_extension_logger
|
|
4
|
+
from esi.openapi_clients import ESIClientProvider
|
|
5
|
+
|
|
6
|
+
from . import __esi_compatibility_date__, __title__, __url__, __version__
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from esi.stubs import (
|
|
10
|
+
CharacterID, CharactersCharacterIdCorporationhistoryGetItem,
|
|
11
|
+
CorporationID, CorporationsCorporationIdAlliancehistoryGetItem,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
esi = ESIClientProvider(
|
|
15
|
+
compatibility_date=__esi_compatibility_date__,
|
|
16
|
+
ua_appname=__title__,
|
|
17
|
+
ua_version=__version__,
|
|
18
|
+
ua_url=__url__,
|
|
19
|
+
operations=[
|
|
20
|
+
"GetCorporationsCorporationIdAlliancehistory",
|
|
21
|
+
"GetCharactersCharacterIdCorporationhistory"]
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = get_extension_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_corporations_corporation_id_alliancehistory(corporation_id: "CorporationID") -> list["CorporationsCorporationIdAlliancehistoryGetItem"]:
|
|
28
|
+
result = esi.client.Corporation.GetCorporationsCorporationIdAlliancehistory(
|
|
29
|
+
corporation_id=corporation_id
|
|
30
|
+
).results()
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_characters_character_id_corporationhistory(character_id: "CharacterID") -> list["CharactersCharacterIdCorporationhistoryGetItem"]:
|
|
35
|
+
result = esi.client.Character.GetCharactersCharacterIdCorporationhistory(
|
|
36
|
+
character_id=character_id
|
|
37
|
+
).results()
|
|
38
|
+
return result
|
alumni/signals.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.db.models.signals import post_save
|
|
4
|
+
from django.dispatch import receiver
|
|
5
|
+
|
|
6
|
+
from allianceauth.eveonline.models import EveCharacter
|
|
7
|
+
|
|
8
|
+
from alumni.models import AlumniSetup
|
|
9
|
+
from alumni.tasks import alumni_check_character, run_alumni_check_all
|
|
10
|
+
|
|
11
|
+
from .app_settings import ALUMNI_TASK_PRIORITY
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@receiver(post_save, sender=AlumniSetup)
|
|
17
|
+
def alumni_was_updated(sender, instance: AlumniSetup, *args, **kwargs) -> None:
|
|
18
|
+
run_alumni_check_all.apply_async(priority=ALUMNI_TASK_PRIORITY) # Type ignore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@receiver(post_save, sender=EveCharacter)
|
|
22
|
+
def character_added(sender, instance: EveCharacter, created, *args, **kwargs) -> None:
|
|
23
|
+
if created is True:
|
|
24
|
+
alumni_check_character.apply_async(args=[instance.character_id], priority=ALUMNI_TASK_PRIORITY - 1) # Type ignore
|
alumni/tasks.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from math import ceil
|
|
4
|
+
from random import randint
|
|
5
|
+
|
|
6
|
+
from celery import shared_task
|
|
7
|
+
|
|
8
|
+
from allianceauth.authentication.models import State
|
|
9
|
+
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo
|
|
10
|
+
from allianceauth.services.hooks import get_extension_logger
|
|
11
|
+
from esi.decorators import esi_rate_limiter_bucketed
|
|
12
|
+
from esi.exceptions import (
|
|
13
|
+
ESIBucketLimitException, ESIErrorLimitException, HTTPNotModified,
|
|
14
|
+
)
|
|
15
|
+
from esi.rate_limiting import ESIRateLimitBucket
|
|
16
|
+
|
|
17
|
+
from .app_settings import (
|
|
18
|
+
ALUMNI_CHARACTERCORPORATION_RATELIMIT, ALUMNI_STATE_NAME,
|
|
19
|
+
ALUMNI_TASK_JITTER, ALUMNI_TASK_PRIORITY,
|
|
20
|
+
)
|
|
21
|
+
from .models import (
|
|
22
|
+
AlumniSetup, CharacterCorporationHistory, CharacterUpdateTimestamp,
|
|
23
|
+
CorporationAllianceHistory, CorporationUpdateTimestamp,
|
|
24
|
+
)
|
|
25
|
+
from .providers import (
|
|
26
|
+
get_characters_character_id_corporationhistory,
|
|
27
|
+
get_corporations_corporation_id_alliancehistory,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if typing.TYPE_CHECKING:
|
|
31
|
+
from esi.stubs import AllianceID, CharacterID, CorporationID
|
|
32
|
+
|
|
33
|
+
logger = get_extension_logger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def char_alliance_datecompare(alliance_id: "AllianceID", character_id: "CharacterID") -> bool:
|
|
37
|
+
"""Voodoo relating to checking start dates and _next_ start dates
|
|
38
|
+
|
|
39
|
+
Necessary to determine if a character was a member of a corp
|
|
40
|
+
WHILE it was in an alliance
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
alliance_id: int
|
|
45
|
+
Should match an existing EveAllianceInfo model
|
|
46
|
+
|
|
47
|
+
character_id: int
|
|
48
|
+
Should match an existing EveCharacter model
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
bool
|
|
53
|
+
Whether True"""
|
|
54
|
+
|
|
55
|
+
character = EveCharacter.objects.get(character_id=character_id)
|
|
56
|
+
char_corp_history = CharacterCorporationHistory.objects.filter(
|
|
57
|
+
character=character).order_by('record_id')
|
|
58
|
+
|
|
59
|
+
for index, char_corp_record in enumerate(char_corp_history):
|
|
60
|
+
# Corp Joins Alliance, Between Char Join/Leave Corp
|
|
61
|
+
try:
|
|
62
|
+
filter_end_date = char_corp_history[index + 1].start_date
|
|
63
|
+
except IndexError:
|
|
64
|
+
filter_end_date = datetime.now(timezone.utc)
|
|
65
|
+
|
|
66
|
+
if CorporationAllianceHistory.objects.filter(
|
|
67
|
+
corporation_id=char_corp_record.corporation_id,
|
|
68
|
+
alliance_id=alliance_id,
|
|
69
|
+
start_date__range=(
|
|
70
|
+
char_corp_record.start_date,
|
|
71
|
+
filter_end_date)).exists() is True:
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
corp_alliance_history = CorporationAllianceHistory.objects.filter(
|
|
75
|
+
corporation_id=char_corp_record.corporation_id).order_by('record_id')
|
|
76
|
+
|
|
77
|
+
for index_2, corp_alliance_record in enumerate(corp_alliance_history):
|
|
78
|
+
# Needs to be unfiltered alliance id because we need _next_ start date
|
|
79
|
+
# but check if the alliance id matches before we run any logic
|
|
80
|
+
try:
|
|
81
|
+
if corp_alliance_record.alliance_id == alliance_id:
|
|
82
|
+
# Char Joins Corp, Between Corp Join/Leave
|
|
83
|
+
if corp_alliance_record.start_date < char_corp_record.start_date < corp_alliance_history[index_2 + 1].start_date:
|
|
84
|
+
return True
|
|
85
|
+
# Char Leaves Corp, Between Corp Join/Leave
|
|
86
|
+
elif corp_alliance_record.start_date < char_corp_history[index + 1].start_date < corp_alliance_history[index_2 + 1].start_date:
|
|
87
|
+
return True
|
|
88
|
+
# Corp Leaves Alliance in between Char Join/Leave Corp
|
|
89
|
+
elif char_corp_record.start_date < corp_alliance_history[index_2 + 1].start_date < char_corp_history[index + 1].start_date:
|
|
90
|
+
return True
|
|
91
|
+
else:
|
|
92
|
+
pass
|
|
93
|
+
else:
|
|
94
|
+
pass
|
|
95
|
+
except Exception as e:
|
|
96
|
+
# Need to actually add some IndexError handling to above tasks, but lets log this gracefully so as not to cactus up the whole thing.
|
|
97
|
+
logger.exception(e)
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@shared_task
|
|
102
|
+
def alumni_check_character(character_id: "CharacterID") -> bool:
|
|
103
|
+
"""Check/Update a characters alumni status using the historical models
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
character_id: int
|
|
108
|
+
Should match an existing EveCharacter model
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
bool
|
|
113
|
+
Whether the user is an alumni or not **it is updated in this function as well**"""
|
|
114
|
+
|
|
115
|
+
alumni_setup = AlumniSetup.get_solo()
|
|
116
|
+
alumni_state = State.objects.get(name=ALUMNI_STATE_NAME)
|
|
117
|
+
character = EveCharacter.objects.get(character_id=character_id)
|
|
118
|
+
|
|
119
|
+
if character.corporation_id in alumni_setup.alumni_corporations.values_list('corporation_id', flat=True):
|
|
120
|
+
# Cheapo cop-out to end early
|
|
121
|
+
alumni_state.member_characters.add(character)
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
if character.alliance_id in alumni_setup.alumni_alliances.values_list('alliance_id', flat=True):
|
|
125
|
+
# Cheapo cop-out to end early
|
|
126
|
+
alumni_state.member_characters.add(character)
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
for char_corp in CharacterCorporationHistory.objects.filter(character=character):
|
|
130
|
+
if char_corp.corporation_id in alumni_setup.alumni_corporations.values_list('corporation_id', flat=True):
|
|
131
|
+
# Less Cheap, but ending here is still better than the next one.
|
|
132
|
+
alumni_state.member_characters.add(character)
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
for alliance in alumni_setup.alumni_alliances.all():
|
|
136
|
+
if char_alliance_datecompare(alliance_id=alliance.alliance_id, character_id=character_id):
|
|
137
|
+
alumni_state.member_characters.add(character)
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
# If we reach this point, we aren't an alumni
|
|
141
|
+
alumni_state.member_characters.remove(character)
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@shared_task
|
|
146
|
+
def run_alumni_check_all() -> None:
|
|
147
|
+
for character in EveCharacter.objects.all().values('character_id'):
|
|
148
|
+
alumni_check_character.apply_async(
|
|
149
|
+
args=[character['character_id']],
|
|
150
|
+
priority=ALUMNI_TASK_PRIORITY
|
|
151
|
+
) # pyright: ignore[reportFunctionMemberAccess, reportCallIssue]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@shared_task(bind=True, rate_limit=ALUMNI_CHARACTERCORPORATION_RATELIMIT)
|
|
155
|
+
def update_corporationalliancehistory(self, corporation_id: "CorporationID") -> None:
|
|
156
|
+
"""Update CorporationAllianceHistory models from ESI
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
corporation_id: int """
|
|
161
|
+
|
|
162
|
+
if corporation_id <= 98000000: # NPC Corps don't have CCH
|
|
163
|
+
CorporationUpdateTimestamp.objects.update_or_create(
|
|
164
|
+
corporation_id=corporation_id,
|
|
165
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
for cah in get_corporations_corporation_id_alliancehistory(corporation_id):
|
|
170
|
+
CorporationAllianceHistory.objects.update_or_create(
|
|
171
|
+
record_id=cah.record_id,
|
|
172
|
+
corporation_id=corporation_id,
|
|
173
|
+
alliance_id=cah.alliance_id,
|
|
174
|
+
defaults={
|
|
175
|
+
'is_deleted': True if cah.is_deleted == 'true' else False,
|
|
176
|
+
'start_date': cah.start_date # This can get adjusted by CCP i think
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
CorporationUpdateTimestamp.objects.update_or_create(
|
|
180
|
+
corporation_id=corporation_id,
|
|
181
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
182
|
+
)
|
|
183
|
+
except (ESIErrorLimitException, ESIBucketLimitException) as e:
|
|
184
|
+
raise self.retry(exc=e, countdown=e.reset + 1 if e.reset is not None else 61, max_retries=3)
|
|
185
|
+
except HTTPNotModified:
|
|
186
|
+
CorporationUpdateTimestamp.objects.update_or_create(
|
|
187
|
+
corporation_id=corporation_id,
|
|
188
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
189
|
+
)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.exception(e)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@shared_task(bind=True, rate_limit=ALUMNI_CHARACTERCORPORATION_RATELIMIT)
|
|
195
|
+
@esi_rate_limiter_bucketed(bucket=ESIRateLimitBucket(*ESIRateLimitBucket.CHARACTER_CORPORATION_HISTORY))
|
|
196
|
+
def update_charactercorporationhistory(self, character_id: "CharacterID") -> None:
|
|
197
|
+
"""Update CharacterCorporationHistory models from ESI
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
character_id: int
|
|
202
|
+
Should match an existing EveCharacter model"""
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
character = EveCharacter.objects.get(character_id=character_id)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.exception(e)
|
|
208
|
+
return
|
|
209
|
+
if character.character_id <= 90000000: # NPC Characters don't have CCH
|
|
210
|
+
CharacterUpdateTimestamp.objects.update_or_create(
|
|
211
|
+
character=character,
|
|
212
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
213
|
+
)
|
|
214
|
+
try:
|
|
215
|
+
for cch in get_characters_character_id_corporationhistory(character_id):
|
|
216
|
+
CharacterCorporationHistory.objects.update_or_create(
|
|
217
|
+
character=character,
|
|
218
|
+
corporation_id=cch.corporation_id,
|
|
219
|
+
record_id=cch.record_id,
|
|
220
|
+
defaults={
|
|
221
|
+
'is_deleted': True if cch.is_deleted == 'true' else False,
|
|
222
|
+
'start_date': cch.start_date
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
CharacterUpdateTimestamp.objects.update_or_create(
|
|
226
|
+
character=character,
|
|
227
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
228
|
+
)
|
|
229
|
+
except (ESIErrorLimitException, ESIBucketLimitException) as e:
|
|
230
|
+
raise self.retry(exc=e, countdown=e.reset + 1 if e.reset is not None else 61)
|
|
231
|
+
except HTTPNotModified:
|
|
232
|
+
CharacterUpdateTimestamp.objects.update_or_create(
|
|
233
|
+
character=character,
|
|
234
|
+
defaults={'last_updated': datetime.now(timezone.utc)}
|
|
235
|
+
)
|
|
236
|
+
except Exception as e:
|
|
237
|
+
logger.exception(e)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@shared_task
|
|
241
|
+
def update_models_subset(fraction: int = 14) -> None:
|
|
242
|
+
"""
|
|
243
|
+
Update a subset of the CharacterCorporation history models from ESI.
|
|
244
|
+
|
|
245
|
+
This task operates on 1/fraction of the oldest CCH and CAH records.
|
|
246
|
+
|
|
247
|
+
At 1/14th, once a day this will update all Alumni records in two weeks.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
# Force create all timestamps at zero if they don't exist
|
|
251
|
+
character_timestamps = [
|
|
252
|
+
CharacterUpdateTimestamp(
|
|
253
|
+
character_id=character.id,
|
|
254
|
+
last_updated=datetime.fromtimestamp(1, timezone.utc)) for character in EveCharacter.objects.all()]
|
|
255
|
+
CharacterUpdateTimestamp.objects.bulk_create(character_timestamps, ignore_conflicts=True, batch_size=500)
|
|
256
|
+
|
|
257
|
+
corporation_timestamps = [
|
|
258
|
+
CorporationUpdateTimestamp(
|
|
259
|
+
corporation_id=corp.corporation_id,
|
|
260
|
+
last_updated=datetime.fromtimestamp(1, timezone.utc)) for corp in EveCorporationInfo.objects.all()]
|
|
261
|
+
corporation_timestamps += [
|
|
262
|
+
CorporationUpdateTimestamp(
|
|
263
|
+
corporation_id=corp["corporation_id"],
|
|
264
|
+
last_updated=datetime.fromtimestamp(1, timezone.utc)) for corp in CharacterCorporationHistory.objects.values('corporation_id').distinct()]
|
|
265
|
+
CorporationUpdateTimestamp.objects.bulk_create(corporation_timestamps, ignore_conflicts=True, batch_size=500)
|
|
266
|
+
|
|
267
|
+
for character in CharacterUpdateTimestamp.objects.all(
|
|
268
|
+
).order_by('last_updated')[:ceil(CharacterUpdateTimestamp.objects.count() / fraction)].values('character__character_id'):
|
|
269
|
+
update_charactercorporationhistory.apply_async(
|
|
270
|
+
args=[character['character__character_id']],
|
|
271
|
+
priority=ALUMNI_TASK_PRIORITY,
|
|
272
|
+
countdown=randint(1, ALUMNI_TASK_JITTER)
|
|
273
|
+
) # pyright: ignore[reportFunctionMemberAccess, reportCallIssue]
|
|
274
|
+
|
|
275
|
+
for char_corp_record in CorporationUpdateTimestamp.objects.all(
|
|
276
|
+
).order_by('last_updated')[:ceil(CorporationUpdateTimestamp.objects.count() / fraction)].values('corporation_id'):
|
|
277
|
+
update_corporationalliancehistory.apply_async(
|
|
278
|
+
args=[char_corp_record['corporation_id']],
|
|
279
|
+
priority=ALUMNI_TASK_PRIORITY,
|
|
280
|
+
countdown=randint(1, ALUMNI_TASK_JITTER)
|
|
281
|
+
) # pyright: ignore[reportFunctionMemberAccess, reportCallIssue]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@shared_task()
|
|
285
|
+
def update_all_models() -> None:
|
|
286
|
+
"""Update All CharacterCorporation history models from ESI"""
|
|
287
|
+
|
|
288
|
+
update_models_subset.apply_async(priority=ALUMNI_TASK_PRIORITY) # pyright: ignore[reportFunctionMemberAccess, reportCallIssue]
|
alumni/tests/__init__.py
ADDED
|
File without changes
|