aa-planetary-operations 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. aa_planetary_operations-0.1.0.dist-info/METADATA +224 -0
  2. aa_planetary_operations-0.1.0.dist-info/RECORD +30 -0
  3. aa_planetary_operations-0.1.0.dist-info/WHEEL +5 -0
  4. aa_planetary_operations-0.1.0.dist-info/licenses/LICENSE +21 -0
  5. aa_planetary_operations-0.1.0.dist-info/licenses/NOTICE.md +7 -0
  6. aa_planetary_operations-0.1.0.dist-info/top_level.txt +1 -0
  7. planetary_operations/__init__.py +1 -0
  8. planetary_operations/apps.py +7 -0
  9. planetary_operations/auth_hooks.py +27 -0
  10. planetary_operations/catalog.py +16 -0
  11. planetary_operations/characters.py +79 -0
  12. planetary_operations/data/catalog.json +1 -0
  13. planetary_operations/esi.py +66 -0
  14. planetary_operations/forms.py +100 -0
  15. planetary_operations/management/__init__.py +0 -0
  16. planetary_operations/management/commands/__init__.py +0 -0
  17. planetary_operations/management/commands/po_recover_plans.py +23 -0
  18. planetary_operations/market.py +100 -0
  19. planetary_operations/migrations/0001_initial.py +83 -0
  20. planetary_operations/migrations/__init__.py +0 -0
  21. planetary_operations/models.py +40 -0
  22. planetary_operations/planner.py +482 -0
  23. planetary_operations/static/planetary_operations/app.css +1 -0
  24. planetary_operations/static/planetary_operations/progress.js +18 -0
  25. planetary_operations/tasks.py +70 -0
  26. planetary_operations/templates/planetary_operations/base.html +16 -0
  27. planetary_operations/templates/planetary_operations/detail.html +50 -0
  28. planetary_operations/templates/planetary_operations/index.html +31 -0
  29. planetary_operations/urls.py +15 -0
  30. planetary_operations/views.py +203 -0
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: aa-planetary-operations
3
+ Version: 0.1.0
4
+ Summary: Multi-character planetary industry planning for Alliance Auth v5
5
+ Author: jaybone26
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jaybone26/aa-planetary-operations
8
+ Project-URL: Documentation, https://github.com/jaybone26/aa-planetary-operations#readme
9
+ Project-URL: Repository, https://github.com/jaybone26/aa-planetary-operations
10
+ Project-URL: Issues, https://github.com/jaybone26/aa-planetary-operations/issues
11
+ Keywords: allianceauth,eve-online,planetary-industry
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ License-File: NOTICE.md
16
+ Requires-Dist: allianceauth<6,>=5.0
17
+ Requires-Dist: requests<3,>=2.32
18
+ Requires-Dist: ortools<10,>=9.10
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest>=8; extra == "test"
21
+ Requires-Dist: pytest-django>=4.8; extra == "test"
22
+ Requires-Dist: requests-mock>=1.12; extra == "test"
23
+ Requires-Dist: fakeredis>=2; extra == "test"
24
+ Requires-Dist: ruff; extra == "test"
25
+ Requires-Dist: build; extra == "test"
26
+ Dynamic: license-file
27
+
28
+ ![Version](https://img.shields.io/badge/version-0.1.0-blue)
29
+ ![Alliance Auth](https://img.shields.io/badge/Alliance_Auth-v5-blue)
30
+ ![Python](https://img.shields.io/badge/Python-3.11%2B-blue)
31
+ ![Django](https://img.shields.io/badge/Django-5.2-green)
32
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/LICENSE)
33
+ [![Verification](https://img.shields.io/badge/local_tests-36_passed-brightgreen)](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/VERIFICATION.md)
34
+ ![Status](https://img.shields.io/badge/status-staging-orange)
35
+
36
+ # AA Planetary Operations for Alliance Auth
37
+
38
+ Plan your planetary industry across your main and alts, build the production network, and see its Jita buy value per pull.
39
+
40
+ ---
41
+
42
+ ## Contents
43
+
44
+ - [Features](#features)
45
+ - [Upcoming](#upcoming)
46
+ - [Highlights](#highlights)
47
+ - [Installation](#installation)
48
+ - [Step 1 — Install the package](#step-1--install-the-package)
49
+ - [Step 2 — Configure Alliance Auth](#step-2--configure-alliance-auth)
50
+ - [Step 3 — Configure ESI access](#step-3--configure-esi-access)
51
+ - [Step 4 — Run migrations and collect static files](#step-4--run-migrations-and-collect-static-files)
52
+ - [Step 5 — Set up permissions](#step-5--set-up-permissions)
53
+ - [Step 6 — Authorize characters and build a plan](#step-6--authorize-characters-and-build-a-plan)
54
+ - [Settings](#settings)
55
+ - [Using the planner](#using-the-planner)
56
+ - [Translations](#translations)
57
+ - [Contributing](#contributing)
58
+ - [License and credits](#license-and-credits)
59
+
60
+ ## Features
61
+
62
+ - **Main and alt planning** — combine up to ten AA-authenticated characters in one operation.
63
+ - **Automatic PI skill reads** — use active skill levels and existing colonies to determine available capacity.
64
+ - **P0 through P4** — choose any of the 83 PI commodities as your finished product.
65
+ - **Import tiers** — extract from P0 or start with supplied P1, P2, or P3 materials.
66
+ - **Region selection** — use real planet types, resources, recipes, and stargate connections from CCP's static data.
67
+ - **One system first** — try single-system layouts before expanding into nearby systems.
68
+ - **Character assignments** — fit colonies and facilities within each character's skill limits.
69
+ - **Build sheets** — see each planet, command-center level, extractor, processor, and required material.
70
+ - **Logistics plans** — get import quantities, local routes, character handoffs, hauling volumes, and regional gate paths.
71
+ - **Extraction schedules** — set the pull duration, output target, assumed extraction yield, and extractor head count.
72
+ - **Jita buy valuation** — estimate gross ISK per pull, day, and week from Jita 4-4 buy orders and available order volume.
73
+ - **Saved operations** — keep private plans, adjust a copy, refresh prices, or export the build sheet.
74
+
75
+ ## Upcoming
76
+
77
+ Future possibilities beyond V1:
78
+
79
+ - Discord alerts.
80
+ - Alliance-wide collaborative production and suppliers.
81
+ - Advanced POCO tools.
82
+ - Historical production and market analytics.
83
+
84
+ ## Highlights
85
+
86
+ ### Characters and operation planner
87
+
88
+ Select your authenticated characters and configure the production chain in one place.
89
+
90
+ ![AA Planetary Operations — characters and planner](https://raw.githubusercontent.com/jaybone26/aa-planetary-operations/HEAD/docs/images/planner.png)
91
+
92
+ ### Production overview
93
+
94
+ Review estimated output, deployment size, and the Jita valuation status for a saved operation.
95
+
96
+ ![AA Planetary Operations — saved operation overview](https://raw.githubusercontent.com/jaybone26/aa-planetary-operations/HEAD/docs/images/operation.png)
97
+
98
+ *Screenshots show a local demonstration using sample characters. Market pricing is unavailable in the pictured preview; the live public ESI check is recorded in [Verification](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/VERIFICATION.md).*
99
+
100
+ ## Installation
101
+
102
+ > **Requirements:** Alliance Auth v5, Python 3.11+, and your existing Auth database, Redis/Valkey, and Celery worker.
103
+ >
104
+ > Version 0.1.0 is ready for staging evaluation. Install it directly into your Alliance Auth virtual environment. Complete a real SSO authorization and worker run on your server before rollout.
105
+
106
+ ### Step 1 — Install the package
107
+
108
+ Activate the virtual environment used by your Alliance Auth installation, then install the supplied wheel:
109
+
110
+ ```shell
111
+ python -m pip install /path/to/aa_planetary_operations-0.1.0-py3-none-any.whl
112
+ ```
113
+
114
+ Alternatively, install from the source directory:
115
+
116
+ ```shell
117
+ python -m pip install /path/to/aa-planetary-operations
118
+ ```
119
+
120
+ PyPI publication is pending. Maintainers can follow the [Publishing guide](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/docs/PUBLISHING.md) to configure the first release.
121
+
122
+ ### Step 2 — Configure Alliance Auth
123
+
124
+ Add the app in your Auth project's `settings/local.py`:
125
+
126
+ ```python
127
+ INSTALLED_APPS += [
128
+ "planetary_operations",
129
+ ]
130
+ ```
131
+
132
+ Set a User-Agent that identifies your deployment and administrator contact:
133
+
134
+ ```python
135
+ PLANETARY_OPERATIONS_USER_AGENT = "AA-Planetary-Operations/0.1.0 (your administrator contact)"
136
+ PLANETARY_OPERATIONS_SEARCH_SECONDS = 30
137
+ ```
138
+
139
+ The package includes its PI and universe catalog. A separate SDE app or initial data download is not required.
140
+
141
+ ### Step 3 — Configure ESI access
142
+
143
+ Enable the following scopes on the EVE developer application used by your Auth installation:
144
+
145
+ | Scope | Purpose |
146
+ | :--- | :--- |
147
+ | `esi-skills.read_skills.v1` | Read each character's active PI skill levels. |
148
+ | `esi-planets.manage_planets.v1` | Read existing colonies and occupied planet IDs. |
149
+
150
+ Keep your existing AA SSO callback URL. Users grant these scopes through **Authorize a character’s PI access**. The plugin only reads ESI, including the scope named “manage”; it makes no in-game changes.
151
+
152
+ ### Step 4 — Run migrations and collect static files
153
+
154
+ From your Auth project directory:
155
+
156
+ ```shell
157
+ python manage.py migrate
158
+ python manage.py collectstatic --noinput
159
+ python manage.py check
160
+ ```
161
+
162
+ Restart your existing Auth web processes and Celery workers through your normal service manager.
163
+
164
+ Plan calculations use the task `planetary_operations.tasks.calculate_plan` on the existing AA worker. No additional Celery Beat schedule is required.
165
+
166
+ ### Step 5 — Set up permissions
167
+
168
+ Grant this permission to the appropriate AA group or state:
169
+
170
+ | Permission | Description | Access |
171
+ | :--- | :--- | :--- |
172
+ | `planetary_operations.basic_access` | Can use Planetary Operations | Opens the app and allows users to create and manage their own plans. |
173
+
174
+ The sidebar entry appears for permitted users. Saved plans are private to their owner.
175
+
176
+ ### Step 6 — Authorize characters and build a plan
177
+
178
+ 1. Open **Planetary Operations** from the AA sidebar.
179
+ 2. Attach any missing alts to your AA account.
180
+ 3. Use **Authorize a character’s PI access** for each character.
181
+ 4. Select the characters, finished product, import tier, and region.
182
+ 5. Set the pull duration and output target. Enter an extraction estimate based on your in-game program.
183
+ 6. Select **Build & save plan**. The result page updates when the worker finishes.
184
+
185
+ ## Settings
186
+
187
+ Optional settings in `settings/local.py`:
188
+
189
+ | Setting | Description | Default |
190
+ | :--- | :--- | :--- |
191
+ | `PLANETARY_OPERATIONS_USER_AGENT` | Identifies your application to ESI. Set it to include your administrator contact. | `AA-Planetary-Operations/0.1.0` |
192
+ | `PLANETARY_OPERATIONS_SEARCH_SECONDS` | Solver search budget in seconds, limited to 5–120. | `30` |
193
+
194
+ ## Using the planner
195
+
196
+ Existing colonies reduce available slots by default. Enable **Plan a replacement for my existing colonies** if you intend to rebuild them in game. Saved plans are alternative layouts; they do not reserve colony slots against each other.
197
+
198
+ **Read estimates as steady-state targets.** The planner checks factory cycles and fitting budgets against your requested output. Future extraction yield, resource hotspots, exact link placement, startup buffers, sovereignty, and POCO access need an in-game check. Nearby-system results report a feasible network without claiming a global regional optimum.
199
+
200
+ **Jita estimates are gross value.** Imported materials, taxes, fees, and hauling costs are excluded. Unfilled buy-order volume is reported. Day/week figures extrapolate the current pull valuation; future liquidity can change.
201
+
202
+ For the full assumptions, operating instructions, catalog updates, and troubleshooting, see the [Operating guide](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/docs/OPERATING_GUIDE.md).
203
+
204
+ ## Translations
205
+
206
+ The V1 interface is currently in English. Translation support is not included in this release.
207
+
208
+ ## Contributing
209
+
210
+ Development uses a standard Python virtual environment. See the [Development guide](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/docs/DEVELOPMENT.md) for setup and verification commands.
211
+
212
+ - [Architecture](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/ARCHITECTURE.md) — modules, planning constraints, and ownership checks.
213
+ - [Verification](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/VERIFICATION.md) — completed tests, AA version checks, and live-data results.
214
+ - [Operating guide](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/docs/OPERATING_GUIDE.md) — behavior to preserve when changing the planner.
215
+
216
+ Include relevant tests with changes to production calculations, character permissions, or market handling.
217
+
218
+ ## License and credits
219
+
220
+ Source code is available under the [MIT License](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/LICENSE).
221
+
222
+ Built for [Alliance Auth](https://allianceauth.readthedocs.io/) using [Django-ESI](https://django-esi.readthedocs.io/) token management, CCP's [Static Data Export](https://developers.eveonline.com/docs/services/static-data/), and the [EVE ESI API](https://esi.evetech.net/ui/).
223
+
224
+ EVE Online and its game data belong to CCP. See [NOTICE.md](https://github.com/jaybone26/aa-planetary-operations/blob/HEAD/NOTICE.md).
@@ -0,0 +1,30 @@
1
+ aa_planetary_operations-0.1.0.dist-info/licenses/LICENSE,sha256=x9FiUns2NmZSPwSJEvAg0Pflcj_lcBRp3lkkUG6Q-zA,1093
2
+ aa_planetary_operations-0.1.0.dist-info/licenses/NOTICE.md,sha256=DXaCsPTWoMCavmEtDeiATpbmJHSEFKhxtnlgci_VqLM,638
3
+ planetary_operations/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
4
+ planetary_operations/apps.py,sha256=TJuIEaKafSZVFDg8M9H8BIGwQYztuHrdvjEb23_i29w,213
5
+ planetary_operations/auth_hooks.py,sha256=KyxQ6c7nY5n3LmUNW1GHoFlD_3JBsxo0jbn8fQYNczo,709
6
+ planetary_operations/catalog.py,sha256=KFvKiPqftuDf2mHTtBqGPD7iR8qI5QGo21t04_8pD0k,391
7
+ planetary_operations/characters.py,sha256=-c8ZHu0R9GKQ1OslNeisUlrRynhw8mBrcMRE87NXEWM,3132
8
+ planetary_operations/esi.py,sha256=nPPc0fiGFvVsc1zt9MWhUo9LN4jqRBgQdL8uMzUUvbQ,2132
9
+ planetary_operations/forms.py,sha256=qiZiF3m4rLAu8K9ktkafKYy6lbNrd4Nsn7A3gqg7bKA,4030
10
+ planetary_operations/market.py,sha256=qzQUr4waDGrAiiwlGgtuNnD5Kf4LgNzQE5RsXhPBuU4,3873
11
+ planetary_operations/models.py,sha256=m30SMOwqdGeKBUklio_G939pRqYkdxC57v1FONOH01o,1534
12
+ planetary_operations/planner.py,sha256=IhBZ7pZaH9r9_uJtOmmRUc1zQ2LbVT7xnMLl_RgDROo,20483
13
+ planetary_operations/tasks.py,sha256=u7_L6ryW_4yqUDNaHEe-iKLaQH07zBdd55okzmfkL9E,2681
14
+ planetary_operations/urls.py,sha256=exJIP84_uVpFT8G8ZVBdYoCQMGtScvtmDl46qsR5uwQ,604
15
+ planetary_operations/views.py,sha256=zpkVSf_qdE45tiD6Xgz5DkyiPM4uOJ-Bx2K7abceQrY,7249
16
+ planetary_operations/data/catalog.json,sha256=XRHGRHZP4foZ3ZSWJCghkmKbUiTN6FY4GCcYAv2fBAA,4823890
17
+ planetary_operations/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ planetary_operations/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ planetary_operations/management/commands/po_recover_plans.py,sha256=D3_yaBSrHz_J8bpzDST_sVdoxtwZq_CkTodDxdSI62g,852
20
+ planetary_operations/migrations/0001_initial.py,sha256=-0fDooAv0R8InZiNWo1-MRXVfHrrfNj3_VBNb6iSweI,3094
21
+ planetary_operations/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ planetary_operations/static/planetary_operations/app.css,sha256=4moa6CRH95qdQEDfJRobs1ZVGAgPYbLdPhhBfFHWIr4,1282
23
+ planetary_operations/static/planetary_operations/progress.js,sha256=S1fhj_v4B88PfhBPRFmTHkZyjwYH1f2bqOxB62d2Heg,750
24
+ planetary_operations/templates/planetary_operations/base.html,sha256=LyxQUT5cQ0i5jMUS9ZwJ1Aby1WVRrSDHQ0BaI-uJ0hs,659
25
+ planetary_operations/templates/planetary_operations/detail.html,sha256=P1LC-CWcwfdwDc6N4wLeuLOG_Ra4CNMNaYq0uO9Zs7U,6718
26
+ planetary_operations/templates/planetary_operations/index.html,sha256=KXGLJgrBwAR4boeC6E2mAxxB2Z19CQYPkwmg3gVX7vg,3055
27
+ aa_planetary_operations-0.1.0.dist-info/METADATA,sha256=or0DZOVBTF4la3gj_0BjZwAafN-rWDDFKWSY2CXOTg0,11090
28
+ aa_planetary_operations-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
+ aa_planetary_operations-0.1.0.dist-info/top_level.txt,sha256=lgt0xnd3Dcoys8jlcW5XJx805Duzrm3RilhWSMDDwfg,21
30
+ aa_planetary_operations-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AA Planetary Operations contributors
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.
@@ -0,0 +1,7 @@
1
+ # Game-data notice
2
+
3
+ EVE Online, the EVE logo, and EVE game data are the intellectual property of CCP hf. This community application is not affiliated with or endorsed by CCP or the Alliance Auth project.
4
+
5
+ The bundled catalog is derived from the official EVE Online Static Data Export. Access and use of EVE APIs and static data remain subject to CCP's applicable developer terms. No license to CCP's trademarks or game data is granted by this application's source-code license.
6
+
7
+ The dependency packages retain their own licenses. Alliance Auth is GPL-licensed; review its distribution requirements when distributing combined deployments.
@@ -0,0 +1 @@
1
+ planetary_operations
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class PlanetaryOperationsConfig(AppConfig):
5
+ name = "planetary_operations"
6
+ verbose_name = "Planetary Operations"
7
+ default_auto_field = "django.db.models.BigAutoField"
@@ -0,0 +1,27 @@
1
+ from allianceauth import hooks
2
+ from allianceauth.menu.hooks import MenuItemHook
3
+ from allianceauth.services.hooks import UrlHook
4
+
5
+ from . import urls
6
+
7
+
8
+ class PlanetaryMenu(MenuItemHook):
9
+ def __init__(self):
10
+ super().__init__(
11
+ "Planetary Operations", "fa-solid fa-globe", "planetary_operations:index", order=1500
12
+ )
13
+
14
+ def render(self, request):
15
+ if request.user.has_perm("planetary_operations.basic_access"):
16
+ return super().render(request)
17
+ return ""
18
+
19
+
20
+ @hooks.register("menu_item_hook")
21
+ def menu_item():
22
+ return PlanetaryMenu()
23
+
24
+
25
+ @hooks.register("url_hook")
26
+ def url_hook():
27
+ return UrlHook(urls, "planetary_operations", r"^planetary-operations/")
@@ -0,0 +1,16 @@
1
+ """Read-only, process-local static data. Never stores user or token data."""
2
+
3
+ import json
4
+ from functools import lru_cache
5
+ from importlib.resources import files
6
+
7
+
8
+ @lru_cache(maxsize=1)
9
+ def catalog():
10
+ return json.loads(
11
+ files("planetary_operations").joinpath("data/catalog.json").read_text("utf-8")
12
+ )
13
+
14
+
15
+ def commodity(type_id):
16
+ return catalog()["commodities"][str(type_id)]
@@ -0,0 +1,79 @@
1
+ """AA ownership and scoped-token checks precede every private ESI read."""
2
+
3
+ from allianceauth.authentication.models import CharacterOwnership
4
+ from esi.models import Token
5
+
6
+ from . import esi
7
+ from .models import CharacterSnapshot
8
+ from .planner import Character, PlanningError
9
+
10
+ SCOPES = ["esi-skills.read_skills.v1", "esi-planets.manage_planets.v1"]
11
+ PI_SKILLS = {
12
+ 2495: "interplanetary_consolidation",
13
+ 2505: "command_center_upgrades",
14
+ 2403: "planetology",
15
+ 2406: "advanced_planetology",
16
+ 13279: "remote_sensing",
17
+ }
18
+
19
+
20
+ def owned_characters(user):
21
+ return CharacterOwnership.objects.filter(user=user).select_related("character")
22
+
23
+
24
+ def refresh_character(user, character_id):
25
+ try:
26
+ ownership = owned_characters(user).get(character__character_id=character_id)
27
+ except CharacterOwnership.DoesNotExist as exc:
28
+ raise PlanningError("That character is not attached to your AA account.") from exc
29
+ token = (
30
+ Token.objects.filter(
31
+ user=user, character_id=character_id, character_owner_hash=ownership.owner_hash
32
+ )
33
+ .require_scopes(SCOPES)
34
+ .first()
35
+ )
36
+ if not token:
37
+ raise PlanningError(f"Authorize PI access for {ownership.character.character_name}.")
38
+ try:
39
+ access = token.valid_access_token()
40
+ except Exception as exc:
41
+ raise PlanningError(
42
+ f"Reauthorize PI access for {ownership.character.character_name}."
43
+ ) from exc
44
+ data, _ = esi.get(f"characters/{character_id}/skills/", access_token=access)
45
+ colonies, _ = esi.get(f"characters/{character_id}/planets/", access_token=access)
46
+ try:
47
+ # Active levels respect current account/skill restrictions.
48
+ skills = {
49
+ str(row["skill_id"]): row["active_skill_level"]
50
+ for row in data["skills"]
51
+ if row["skill_id"] in PI_SKILLS
52
+ }
53
+ if any(not isinstance(v, int) or not 0 <= v <= 5 for v in skills.values()):
54
+ raise ValueError("Invalid skill level")
55
+ planet_ids = [int(row["planet_id"]) for row in colonies]
56
+ except (KeyError, TypeError, ValueError) as exc:
57
+ raise esi.ESIUnavailable("ESI returned incomplete character data.") from exc
58
+ # Recheck ownership after network calls, including transfers between users.
59
+ if not CharacterOwnership.objects.filter(
60
+ pk=ownership.pk, user=user, owner_hash=ownership.owner_hash
61
+ ).exists():
62
+ raise PlanningError("Character ownership changed during the refresh.")
63
+ snapshot, _ = CharacterSnapshot.objects.update_or_create(
64
+ ownership=ownership,
65
+ defaults=dict(owner_hash=ownership.owner_hash, skills=skills, colonies=planet_ids),
66
+ )
67
+ return snapshot
68
+
69
+
70
+ def planning_character(snapshot, replace_existing=False):
71
+ character = snapshot.ownership.character
72
+ return Character(
73
+ id=character.character_id,
74
+ name=character.character_name,
75
+ ic=snapshot.skills.get("2495", 0),
76
+ ccu=snapshot.skills.get("2505", 0),
77
+ reserved=0 if replace_existing else len(snapshot.colonies),
78
+ occupied=() if replace_existing else tuple(snapshot.colonies),
79
+ )