aa-fleetfinder 2.6.0__py3-none-any.whl → 2.7.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.

Potentially problematic release.


This version of aa-fleetfinder might be problematic. Click here for more details.

Files changed (40) hide show
  1. {aa_fleetfinder-2.6.0.dist-info → aa_fleetfinder-2.7.0.dist-info}/METADATA +1 -1
  2. {aa_fleetfinder-2.6.0.dist-info → aa_fleetfinder-2.7.0.dist-info}/RECORD +40 -33
  3. fleetfinder/__init__.py +1 -1
  4. fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +94 -17
  5. fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
  6. fleetfinder/locale/de/LC_MESSAGES/django.po +108 -20
  7. fleetfinder/locale/django.pot +96 -18
  8. fleetfinder/locale/es/LC_MESSAGES/django.po +108 -18
  9. fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +98 -17
  10. fleetfinder/locale/it_IT/LC_MESSAGES/django.po +91 -17
  11. fleetfinder/locale/ja/LC_MESSAGES/django.po +95 -17
  12. fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +108 -18
  13. fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +91 -17
  14. fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +93 -17
  15. fleetfinder/locale/ru/LC_MESSAGES/django.mo +0 -0
  16. fleetfinder/locale/ru/LC_MESSAGES/django.po +112 -22
  17. fleetfinder/locale/sk/LC_MESSAGES/django.po +91 -17
  18. fleetfinder/locale/uk/LC_MESSAGES/django.mo +0 -0
  19. fleetfinder/locale/uk/LC_MESSAGES/django.po +112 -22
  20. fleetfinder/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  21. fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +112 -22
  22. fleetfinder/static/fleetfinder/css/fleetfinder.css +21 -0
  23. fleetfinder/static/fleetfinder/css/fleetfinder.min.css +1 -1
  24. fleetfinder/static/fleetfinder/css/fleetfinder.min.css.map +1 -1
  25. fleetfinder/static/fleetfinder/js/fleetfinder.js +23 -0
  26. fleetfinder/static/fleetfinder/js/fleetfinder.min.js +2 -0
  27. fleetfinder/static/fleetfinder/js/fleetfinder.min.js.map +1 -0
  28. fleetfinder/tasks.py +137 -150
  29. fleetfinder/templates/fleetfinder/bundles/js/fleetfinder-js.html +3 -0
  30. fleetfinder/templates/fleetfinder/dashboard.html +38 -63
  31. fleetfinder/templates/fleetfinder/fleet-details.html +124 -33
  32. fleetfinder/templates/fleetfinder/join-fleet.html +11 -1
  33. fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html +46 -0
  34. fleetfinder/templates/fleetfinder/partials/body/form-fleet-details.html +8 -8
  35. fleetfinder/tests/test_tasks.py +140 -0
  36. fleetfinder/tests/test_views.py +473 -0
  37. fleetfinder/urls.py +5 -0
  38. fleetfinder/views.py +316 -103
  39. {aa_fleetfinder-2.6.0.dist-info → aa_fleetfinder-2.7.0.dist-info}/WHEEL +0 -0
  40. {aa_fleetfinder-2.6.0.dist-info → aa_fleetfinder-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,21 +4,21 @@
4
4
  # Dehao Wu <wudehao2000@163.com>, 2024.
5
5
  # Faer Yili <yilifaer@gmail.com>, 2024.
6
6
  # SAM_FPS <sam_fps@163.com>, 2024.
7
- # Peter Pfeufer <info@ppfeufer.de>, 2024.
7
+ # Peter Pfeufer <info@ppfeufer.de>, 2024, 2025.
8
8
  msgid ""
9
9
  msgstr ""
10
10
  "Project-Id-Version: AA Fleet Finder 2.2.1\n"
11
11
  "Report-Msgid-Bugs-To: https://github.com/ppfeufer/aa-fleetfinder/issues\n"
12
- "POT-Creation-Date: 2025-08-04 22:06+0200\n"
13
- "PO-Revision-Date: 2024-06-06 14:27+0000\n"
12
+ "POT-Creation-Date: 2025-08-21 15:11+0200\n"
13
+ "PO-Revision-Date: 2025-08-21 13:16+0000\n"
14
14
  "Last-Translator: Peter Pfeufer <info@ppfeufer.de>\n"
15
- "Language-Team: Chinese (Simplified) <https://weblate.ppfeufer.de/projects/alliance-auth-apps/aa-fleetfinder/zh_Hans/>\n"
15
+ "Language-Team: Chinese (Simplified Han script) <https://weblate.ppfeufer.de/projects/alliance-auth-apps/aa-fleetfinder/zh_Hans/>\n"
16
16
  "Language: zh_Hans\n"
17
17
  "MIME-Version: 1.0\n"
18
18
  "Content-Type: text/plain; charset=UTF-8\n"
19
19
  "Content-Transfer-Encoding: 8bit\n"
20
20
  "Plural-Forms: nplurals=1; plural=0;\n"
21
- "X-Generator: Weblate 5.5.5\n"
21
+ "X-Generator: Weblate 5.13\n"
22
22
 
23
23
  #: fleetfinder/__init__.py:9 fleetfinder/models.py:24
24
24
  #: fleetfinder/templates/fleetfinder/base.html:6
@@ -70,7 +70,6 @@ msgid "Creation date and time"
70
70
  msgstr "创建日期和时间"
71
71
 
72
72
  #: fleetfinder/models.py:62
73
- #: fleetfinder/templates/fleetfinder/partials/body/form-fleet-details.html:37
74
73
  msgid "Fleet MOTD"
75
74
  msgstr "舰队置顶信息"
76
75
 
@@ -117,14 +116,6 @@ msgstr "创建于"
117
116
  msgid "Join"
118
117
  msgstr "加入"
119
118
 
120
- #: fleetfinder/templates/fleetfinder/dashboard.html:25
121
- msgid "Details"
122
- msgstr "细节"
123
-
124
- #: fleetfinder/templates/fleetfinder/dashboard.html:26
125
- msgid "Edit"
126
- msgstr "编辑"
127
-
128
119
  #: fleetfinder/templates/fleetfinder/edit-fleet.html:6
129
120
  #: fleetfinder/templates/fleetfinder/edit-fleet.html:14
130
121
  msgid "Edit fleet"
@@ -159,7 +150,26 @@ msgstr "名字"
159
150
  msgid "System"
160
151
  msgstr "星系"
161
152
 
162
- #: fleetfinder/templates/fleetfinder/join-fleet.html:6 fleetfinder/views.py:90
153
+ #: fleetfinder/templates/fleetfinder/fleet-details.html:53
154
+ msgid "Action"
155
+ msgstr "行动"
156
+
157
+ #: fleetfinder/templates/fleetfinder/fleet-details.html:79
158
+ #, fuzzy
159
+ #| msgid "Fleet members"
160
+ msgid "Fleet boss"
161
+ msgstr "舰队成员"
162
+
163
+ #: fleetfinder/templates/fleetfinder/fleet-details.html:80
164
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:10
165
+ msgid "Kick member from fleet"
166
+ msgstr ""
167
+
168
+ #: fleetfinder/templates/fleetfinder/fleet-details.html:81
169
+ msgid "An unknown error occurred."
170
+ msgstr ""
171
+
172
+ #: fleetfinder/templates/fleetfinder/join-fleet.html:6 fleetfinder/views.py:182
163
173
  msgid "Join fleet"
164
174
  msgstr "加入舰队"
165
175
 
@@ -167,14 +177,50 @@ msgstr "加入舰队"
167
177
  msgid "Fleet invitation"
168
178
  msgstr "舰队邀请"
169
179
 
170
- #: fleetfinder/templates/fleetfinder/join-fleet.html:25
171
- msgid "Select the characters to invite"
180
+ #: fleetfinder/templates/fleetfinder/join-fleet.html:22
181
+ msgid "Please ensure you don't have any CSPA charges set on your characters, as this will prevent fleet invites from being sent."
182
+ msgstr ""
183
+
184
+ #: fleetfinder/templates/fleetfinder/join-fleet.html:24
185
+ #, fuzzy
186
+ #| msgid "Select the characters to invite"
187
+ msgid "You can select multiple characters to invite them all at once."
172
188
  msgstr "选择要邀请的角色"
173
189
 
190
+ #: fleetfinder/templates/fleetfinder/join-fleet.html:28
191
+ msgid "The selected characters will receive a fleet invite in-game, which they can accept to join the fleet."
192
+ msgstr ""
193
+
174
194
  #: fleetfinder/templates/fleetfinder/join-fleet.html:35
195
+ #, fuzzy
196
+ #| msgid "Select the characters to invite"
197
+ msgid "Please select the characters you want to invite:"
198
+ msgstr "选择要邀请的角色"
199
+
200
+ #: fleetfinder/templates/fleetfinder/join-fleet.html:45
175
201
  msgid "Send fleet invites"
176
202
  msgstr "发送舰队邀请"
177
203
 
204
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:13
205
+ msgid "Close"
206
+ msgstr ""
207
+
208
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:18
209
+ msgid "Are you sure you want to kick this member from the fleet?"
210
+ msgstr ""
211
+
212
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:27
213
+ msgid "Error"
214
+ msgstr ""
215
+
216
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:36
217
+ msgid "Cancel"
218
+ msgstr "取消"
219
+
220
+ #: fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html:41
221
+ msgid "Confirm"
222
+ msgstr "确认"
223
+
178
224
  #: fleetfinder/templates/fleetfinder/partials/body/form-fleet-details.html:15
179
225
  msgid "Select groups"
180
226
  msgstr "选择群组"
@@ -199,18 +245,62 @@ msgstr "加入我们的翻译团队吧!"
199
245
  msgid "Available fleets"
200
246
  msgstr "可用的舰队"
201
247
 
202
- #: fleetfinder/views.py:103
248
+ #: fleetfinder/views.py:193
203
249
  msgid "View fleet details"
204
250
  msgstr "查看船队详情"
205
251
 
206
- #: fleetfinder/views.py:112
252
+ #: fleetfinder/views.py:199
207
253
  msgid "Edit fleet advert"
208
254
  msgstr "编辑舰队宣传信息"
209
255
 
210
- #: fleetfinder/views.py:276
211
- #, python-brace-format
212
- msgid "<h4>Error!</h4><p>ESI returned the following error: {esi_error_message}</p>"
256
+ #: fleetfinder/views.py:244
257
+ #, fuzzy, python-brace-format
258
+ #| msgid "<h4>Error!</h4><p>ESI returned the following error: {esi_error_message}</p>"
259
+ msgid "<h4>Error!</h4><p>There was an error creating the fleet: {error_detail}</p>"
260
+ msgstr "<h4>错误!</h4><p>ESI返回如下错误:{esi_error_message}</p>"
261
+
262
+ #: fleetfinder/views.py:284
263
+ msgid "<h4>Error!</h4><p>Fleet does not exist or is no longer available.</p>"
264
+ msgstr ""
265
+
266
+ #: fleetfinder/views.py:438
267
+ #, fuzzy, python-brace-format
268
+ #| msgid "<h4>Error!</h4><p>ESI returned the following error: {esi_error_message}</p>"
269
+ msgid "<h4>Error!</h4><p>ESI returned the following error: {esi_error}</p>"
213
270
  msgstr "<h4>错误!</h4><p>ESI返回如下错误:{esi_error_message}</p>"
214
271
 
272
+ #: fleetfinder/views.py:446
273
+ #, python-brace-format
274
+ msgid "<h4>Error!</h4><p>There was an error creating the fleet: {ex}</p>"
275
+ msgstr ""
276
+
277
+ #: fleetfinder/views.py:517
278
+ msgid "Method not allowed"
279
+ msgstr ""
280
+
281
+ #: fleetfinder/views.py:527
282
+ msgid "Member ID required"
283
+ msgstr ""
284
+
285
+ #: fleetfinder/views.py:546
286
+ #, fuzzy
287
+ #| msgid "Fleet invitation"
288
+ msgid "Fleet not found"
289
+ msgstr "舰队邀请"
290
+
291
+ #: fleetfinder/views.py:550
292
+ msgid "Invalid request data"
293
+ msgstr ""
294
+
295
+ #: fleetfinder/views.py:554
296
+ msgid "Member not found in fleet"
297
+ msgstr ""
298
+
299
+ #~ msgid "Details"
300
+ #~ msgstr "细节"
301
+
302
+ #~ msgid "Edit"
303
+ #~ msgstr "编辑"
304
+
215
305
  #~ msgid "Toggle navigation"
216
306
  #~ msgstr "切换导航"
@@ -7,4 +7,25 @@
7
7
  .aa-fleetfinder .table-vertical-middle td {
8
8
  vertical-align: middle;
9
9
  }
10
+
11
+ /* Slim Select
12
+ --------------------------------------------------------------------------------- */
13
+
14
+ .aa-fleetfinder .ss-main {
15
+ appearance: none;
16
+ background-clip: padding-box;
17
+ border: var(--bs-border-width) solid var(--bs-border-color);
18
+ border-radius: var(--bs-border-radius);
19
+ color: var(--bs-body-color);
20
+ font-size: 1rem;
21
+ font-weight: 400;
22
+ line-height: 1.5;
23
+ min-height: calc(1.5em + .75rem + 2px);
24
+ padding: .375rem .75rem;
25
+ transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
26
+ width: 100%;
27
+ }
28
+
29
+ /* END Slim Select
30
+ --------------------------------------------------------------------------------- */
10
31
  }
@@ -1,2 +1,2 @@
1
- @media all{.aa-fleetfinder .eve-character-portrait,.aa-fleetfinder .eve-type-icon{margin-right:.5rem}.aa-fleetfinder .table-vertical-middle td{vertical-align:middle}}
1
+ @media all{.aa-fleetfinder .eve-character-portrait,.aa-fleetfinder .eve-type-icon{margin-right:.5rem}.aa-fleetfinder .table-vertical-middle td{vertical-align:middle}.aa-fleetfinder .ss-main{appearance:none;background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);color:var(--bs-body-color);font-size:1rem;font-weight:400;line-height:1.5;min-height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:100%}}
2
2
  /*# sourceMappingURL=fleetfinder.min.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["fleetfinder.css"],"names":[],"mappings":"AAAA,A,WACI,uC,CACA,8B,CACI,kB,CAGJ,yC,CACI,uB","file":"fleetfinder.css","sourcesContent":["@media all {\n .aa-fleetfinder .eve-character-portrait,\n .aa-fleetfinder .eve-type-icon {\n margin-right: 0.5rem;\n }\n\n .aa-fleetfinder .table-vertical-middle td {\n vertical-align: middle;\n }\n}\n"]}
1
+ {"version":3,"sources":["fleetfinder.css"],"names":[],"mappings":"AAAA,A,WACI,uC,CACA,8B,CACI,kB,CAGJ,yC,CACI,qB,CAMJ,wB,CACI,e,CACA,2B,CACA,0D,CACA,qC,CACA,0B,CACA,c,CACA,e,CACA,e,CACA,qC,CACA,sB,CACA,oE,CACA,Y","file":"fleetfinder.css","sourcesContent":["@media all {\n .aa-fleetfinder .eve-character-portrait,\n .aa-fleetfinder .eve-type-icon {\n margin-right: 0.5rem;\n }\n\n .aa-fleetfinder .table-vertical-middle td {\n vertical-align: middle;\n }\n\n /* Slim Select\n --------------------------------------------------------------------------------- */\n\n .aa-fleetfinder .ss-main {\n appearance: none;\n background-clip: padding-box;\n border: var(--bs-border-width) solid var(--bs-border-color);\n border-radius: var(--bs-border-radius);\n color: var(--bs-body-color);\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n min-height: calc(1.5em + .75rem + 2px);\n padding: .375rem .75rem;\n transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;\n width: 100%;\n }\n\n /* END Slim Select\n --------------------------------------------------------------------------------- */\n}\n"]}
@@ -0,0 +1,23 @@
1
+ /* global bootstrap */
2
+
3
+ /* jshint -W097 */
4
+ 'use strict';
5
+
6
+ /**
7
+ * Bootstrap tooltip
8
+ *
9
+ * @param {string} [selector=body] Selector for the tooltip elements, defaults to 'body'
10
+ * to apply to all elements with the data-bs-tooltip attribute.
11
+ * Example: 'body', '.my-tooltip-class', '#my-tooltip-id'
12
+ * If you want to apply it to a specific element, use that element's selector.
13
+ * If you want to apply it to all elements with the data-bs-tooltip attribute,
14
+ * use 'body' or leave it empty.
15
+ * @param {string} [namespace=aa-fleetfinderl] Namespace for the tooltip
16
+ * @returns {void}
17
+ */
18
+ const fleetfinderBootstrapTooltip = ({selector = 'body', namespace = 'aa-fleetfinder'}) => { // eslint-disable-line no-unused-vars
19
+ document.querySelectorAll(`${selector} [data-bs-tooltip="${namespace}"]`)
20
+ .forEach((tooltipTriggerEl) => {
21
+ return new bootstrap.Tooltip(tooltipTriggerEl);
22
+ });
23
+ };
@@ -0,0 +1,2 @@
1
+ const fleetfinderBootstrapTooltip=({selector:o="body",namespace:e="aa-fleetfinder"})=>{document.querySelectorAll(`${o} [data-bs-tooltip="${e}"]`).forEach(o=>new bootstrap.Tooltip(o))};
2
+ //# sourceMappingURL=fleetfinder.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["fleetfinder.js"],"names":["fleetfinderBootstrapTooltip","selector","namespace","document","querySelectorAll","forEach","bootstrap","Tooltip","tooltipTriggerEl"],"mappings":"AAiBA,MAAMA,4BAA8B,CAAA,CAAEC,SAAAA,EAAW,OAAQC,UAAAA,EAAY,gBAAiB,KAClFC,SAASC,oBAAoBH,uBAA8BC,KAAa,EACnEG,QAAQ,GACE,IAAIC,UAAUC,QAAQC,CAAgB,CAChD,CACT"}
fleetfinder/tasks.py CHANGED
@@ -14,7 +14,6 @@ from celery import shared_task
14
14
  from django.utils import timezone
15
15
 
16
16
  # Alliance Auth
17
- from allianceauth.eveonline.models import EveCharacter
18
17
  from allianceauth.services.hooks import get_extension_logger
19
18
  from allianceauth.services.tasks import QueueOnce
20
19
  from esi.models import Token
@@ -148,10 +147,13 @@ def _get_fleet_aggregate(fleet_infos):
148
147
  for member in fleet_infos:
149
148
  type_ = member.get("ship_type_name")
150
149
 
151
- if type_ in counts:
152
- counts[type_] += 1
153
- else:
154
- counts[type_] = 1
150
+ if type_ and isinstance(type_, str) and type_.strip():
151
+ type_ = type_.strip() # Normalize ship type name
152
+
153
+ if type_ in counts:
154
+ counts[type_] += 1
155
+ else:
156
+ counts[type_] = 1
155
157
 
156
158
  return counts
157
159
 
@@ -187,110 +189,56 @@ def _check_for_esi_fleet(fleet: Fleet):
187
189
  return False
188
190
 
189
191
 
190
- def _process_fleet(fleet: Fleet):
192
+ def _process_fleet(fleet: Fleet) -> None:
191
193
  """
192
194
  Processing a fleet
193
195
 
194
- :param fleet:
195
- :type fleet:
196
- :return:
197
- :rtype:
196
+ :param fleet: Fleet object to process
197
+ :type fleet: Fleet
198
+ :return: None
199
+ :rtype: None
198
200
  """
199
201
 
200
- fleet_id = fleet.fleet_id
201
- fleet_name = fleet.name
202
- fleet_commander = fleet.fleet_commander
203
-
204
202
  logger.info(
205
- f'Processing information for fleet "{fleet_name}" '
206
- f"of {fleet_commander} (ESI ID: {fleet_id})"
203
+ f'Processing information for fleet "{fleet.name}" '
204
+ f"of {fleet.fleet_commander} (ESI ID: {fleet.fleet_id})"
207
205
  )
208
206
 
209
207
  # Check if there is a fleet
210
208
  esi_fleet = _check_for_esi_fleet(fleet=fleet)
211
- if esi_fleet and fleet.fleet_id == esi_fleet["fleet"]["fleet_id"]:
212
- try:
213
- fleet_from_esi = esi.client.Fleets.get_characters_character_id_fleet(
214
- character_id=fleet.fleet_commander.character_id,
215
- token=esi_fleet["token"].valid_access_token(),
216
- ).result()
217
- except HTTPNotFound:
218
- _esi_fleet_error_handling(
219
- fleet=fleet, error_key=Fleet.EsiError.NOT_IN_FLEET
220
- )
221
- except Exception: # pylint: disable=broad-exception-caught
222
- _esi_fleet_error_handling(fleet=fleet, error_key=Fleet.EsiError.NO_FLEET)
223
-
224
- # We have a valid fleet result from ESI
225
- else:
226
- if fleet_id == fleet_from_esi["fleet_id"]:
227
- # Check if we deal with the fleet boss here
228
- try:
229
- _ = esi.client.Fleets.get_fleets_fleet_id_members(
230
- fleet_id=fleet_from_esi["fleet_id"],
231
- token=esi_fleet["token"].valid_access_token(),
232
- ).result()
233
- except Exception: # pylint: disable=broad-exception-caught
234
- _esi_fleet_error_handling(
235
- fleet=fleet, error_key=Fleet.EsiError.NOT_FLEETBOSS
236
- )
237
- else:
238
- _esi_fleet_error_handling(
239
- fleet=fleet, error_key=Fleet.EsiError.FC_CHANGED_FLEET
240
- )
241
-
242
-
243
- @shared_task
244
- def open_fleet(character_id, motd, free_move, name, groups):
245
- """
246
- Open a fleet
247
-
248
- :param character_id:
249
- :param motd:
250
- :param free_move:
251
- :param name:
252
- :param groups:
253
- :return:
254
- """
255
-
256
- required_scopes = ["esi-fleets.read_fleet.v1", "esi-fleets.write_fleet.v1"]
257
- token = Token.get_token(character_id=character_id, scopes=required_scopes)
258
-
259
- fleet_result = esi.client.Fleets.get_characters_character_id_fleet(
260
- character_id=token.character_id, token=token.valid_access_token()
261
- ).result()
262
- fleet_id = fleet_result.pop("fleet_id")
263
- fleet_role = fleet_result.pop("role")
264
209
 
265
- if fleet_id is None or fleet_role is None or fleet_role != "fleet_commander":
210
+ if not esi_fleet:
266
211
  return
267
212
 
268
- fleet_commander = EveCharacter.objects.get(character_id=token.character_id)
269
-
270
- fleet = Fleet(
271
- fleet_id=fleet_id,
272
- created_at=timezone.now(),
273
- motd=motd,
274
- is_free_move=free_move,
275
- fleet_commander=fleet_commander,
276
- name=name,
277
- )
278
- fleet.save()
279
- fleet.groups.set(groups)
213
+ # Fleet IDs don't match, FC changed fleets
214
+ if fleet.fleet_id != esi_fleet["fleet"]["fleet_id"]:
215
+ _esi_fleet_error_handling(
216
+ fleet=fleet, error_key=Fleet.EsiError.FC_CHANGED_FLEET
217
+ )
218
+ return
280
219
 
281
- esi_fleet = {"is_free_move": free_move, "motd": motd}
282
- esi.client.Fleets.put_fleets_fleet_id(
283
- fleet_id=fleet_id, token=token.valid_access_token(), new_settings=esi_fleet
284
- ).result()
220
+ # Check if we deal with the fleet boss here
221
+ try:
222
+ _ = esi.client.Fleets.get_fleets_fleet_id_members(
223
+ fleet_id=fleet.fleet_id,
224
+ token=esi_fleet["token"].valid_access_token(),
225
+ ).result()
226
+ except Exception: # pylint: disable=broad-exception-caught
227
+ _esi_fleet_error_handling(fleet=fleet, error_key=Fleet.EsiError.NOT_FLEETBOSS)
285
228
 
286
229
 
287
230
  @shared_task
288
- def send_fleet_invitation(character_ids, fleet_id):
231
+ def send_fleet_invitation(fleet_id: int, character_ids: list) -> None:
289
232
  """
290
- Send a fleet invitation through the eve client
291
-
292
- :param character_ids:
293
- :param fleet_id:
233
+ Send fleet invitations to characters through ESI
234
+ This task sends fleet invitations to a list of character IDs using the ESI API.
235
+
236
+ :param fleet_id: The ID of the fleet to which invitations are sent
237
+ :type fleet_id: int
238
+ :param character_ids: List of character IDs to invite to the fleet
239
+ :type character_ids: list[int]
240
+ :return: None
241
+ :rtype: None
294
242
  """
295
243
 
296
244
  required_scopes = ["esi-fleets.write_fleet.v1"]
@@ -298,95 +246,134 @@ def send_fleet_invitation(character_ids, fleet_id):
298
246
  fleet_commander_token = Token.get_token(
299
247
  character_id=fleet.fleet_commander.character_id, scopes=required_scopes
300
248
  )
301
- _processes = []
302
249
 
303
250
  with ThreadPoolExecutor(max_workers=50) as ex:
304
- for _character_id in character_ids:
305
- _processes.append(
306
- ex.submit(
307
- _send_invitation,
308
- character_id=_character_id,
309
- fleet_commander_token=fleet_commander_token,
310
- fleet_id=fleet_id,
311
- )
251
+ futures = [
252
+ ex.submit(
253
+ _send_invitation,
254
+ character_id=character_id,
255
+ fleet_commander_token=fleet_commander_token,
256
+ fleet_id=fleet_id,
312
257
  )
258
+ for character_id in character_ids
259
+ ]
313
260
 
314
- for item in as_completed(_processes):
315
- _ = item.result()
261
+ for future in as_completed(futures):
262
+ future.result() # This will raise any exceptions that occurred
316
263
 
317
264
 
318
265
  @shared_task(**{**TASK_DEFAULT_KWARGS}, **{"base": QueueOnce})
319
- def check_fleet_adverts():
266
+ def check_fleet_adverts() -> None:
320
267
  """
321
- Scheduled task :: Check for fleets adverts
268
+ Check all registered fleets and process them
269
+
270
+ :return: None
271
+ :rtype: None
322
272
  """
323
273
 
324
274
  fleets = Fleet.objects.all()
325
- fleet_count = fleets.count()
326
275
 
327
- processing_text = "Processing..." if fleet_count > 0 else "Nothing to do..."
276
+ if not fleets.exists():
277
+ logger.info("No registered fleets found. Nothing to do...")
278
+
279
+ return
328
280
 
329
- logger.info(msg=f"{fleet_count} registered fleets found. {processing_text}")
281
+ logger.info(f"Processing {fleets.count()} registered fleets...")
330
282
 
331
- if fleet_count > 0:
332
- # Abort if ESI seems to be offline or above the error limit
333
- if not fetch_esi_status().is_ok:
334
- logger.warning(
335
- msg="ESI doesn't seem to be available at this time. Aborting."
336
- )
283
+ # Abort if ESI seems to be offline or above the error limit
284
+ if not fetch_esi_status().is_ok:
285
+ logger.warning("ESI doesn't seem to be available at this time. Aborting.")
337
286
 
338
- return
287
+ return
339
288
 
340
- for fleet in fleets:
341
- _process_fleet(fleet=fleet)
289
+ for fleet in fleets:
290
+ _process_fleet(fleet=fleet)
342
291
 
343
292
 
344
293
  @shared_task
345
- def get_fleet_composition(fleet_id):
294
+ def get_fleet_composition( # pylint: disable=too-many-locals
295
+ fleet_id: int,
296
+ ) -> FleetViewAggregate | None:
346
297
  """
347
- Getting the fleet composition
298
+ Get the composition of a fleet by its ID
299
+ This task retrieves the composition of a fleet using its ESI ID.
348
300
 
349
- :param fleet_id:
350
- :return:
301
+ :param fleet_id: The ESI ID of the fleet to retrieve
302
+ :type fleet_id: int
303
+ :return: FleetViewAggregate containing fleet members and aggregate data
304
+ :rtype: FleetViewAggregate | None
351
305
  """
352
306
 
353
- required_scopes = ["esi-fleets.read_fleet.v1", "esi-fleets.write_fleet.v1"]
354
- fleet = Fleet.objects.get(fleet_id=fleet_id)
355
- token = Token.get_token(
356
- character_id=fleet.fleet_commander.character_id, scopes=required_scopes
357
- )
358
- fleet_infos = esi.client.Fleets.get_fleets_fleet_id_members(
359
- fleet_id=fleet_id, token=token.valid_access_token()
360
- ).result()
307
+ try:
308
+ fleet = Fleet.objects.get(fleet_id=fleet_id)
309
+ except Fleet.DoesNotExist:
310
+ logger.error(f"Fleet with ID {fleet_id} not found")
361
311
 
362
- characters = {}
363
- systems = {}
364
- ship_type = {}
312
+ return None
365
313
 
366
- for member in fleet_infos:
367
- characters[member["character_id"]] = ""
368
- systems[member["solar_system_id"]] = ""
369
- ship_type[member["ship_type_id"]] = ""
314
+ logger.info(
315
+ f'Getting fleet composition for fleet "{fleet.name}" '
316
+ f"of {fleet.fleet_commander.character_name} (ESI ID: {fleet_id})"
317
+ )
370
318
 
371
- ids = []
372
- ids.extend(list(characters.keys()))
373
- ids.extend(list(systems.keys()))
374
- ids.extend(list(ship_type.keys()))
319
+ required_scopes = ["esi-fleets.read_fleet.v1"]
375
320
 
376
- ids_to_name = esi.client.Universe.post_universe_names(ids=ids).result()
321
+ try:
322
+ token = Token.get_token(
323
+ character_id=fleet.fleet_commander.character_id, scopes=required_scopes
324
+ )
377
325
 
378
- for member in fleet_infos:
379
- index_character = [x["id"] for x in ids_to_name].index(member["character_id"])
380
- member["character_name"] = ids_to_name[index_character]["name"]
326
+ fleet_infos = esi.client.Fleets.get_fleets_fleet_id_members(
327
+ fleet_id=fleet_id, token=token.valid_access_token()
328
+ ).result()
381
329
 
382
- index_solar_system = [x["id"] for x in ids_to_name].index(
383
- member["solar_system_id"]
330
+ # Get all unique IDs and fetch names in one call
331
+ all_ids = {
332
+ item_id
333
+ for member in fleet_infos
334
+ for item_id in [
335
+ member["character_id"],
336
+ member["solar_system_id"],
337
+ member["ship_type_id"],
338
+ ]
339
+ }
340
+
341
+ logger.debug(
342
+ f"Found {len(all_ids)} unique IDs to fetch names for in fleet {fleet_id}"
384
343
  )
385
- member["solar_system_name"] = ids_to_name[index_solar_system]["name"]
386
344
 
387
- index_ship_type = [x["id"] for x in ids_to_name].index(member["ship_type_id"])
388
- member["ship_type_name"] = ids_to_name[index_ship_type]["name"]
345
+ # Process IDs in chunks to avoid ESI limits
346
+ chunk_size = 500
347
+ ids_to_name = []
348
+ all_ids_list = list(all_ids)
349
+
350
+ for i in range(0, len(all_ids_list), chunk_size):
351
+ chunk = all_ids_list[i : i + chunk_size]
352
+ chunk_result = esi.client.Universe.post_universe_names(ids=chunk).result()
353
+
354
+ ids_to_name.extend(chunk_result)
355
+
356
+ # Create a lookup dictionary for names
357
+ name_lookup = {item["id"]: item["name"] for item in ids_to_name}
358
+
359
+ # Add additional information to each fleet member
360
+ for member in fleet_infos:
361
+ is_fleet_boss = member["character_id"] == fleet.fleet_commander.character_id
362
+
363
+ member.update(
364
+ {
365
+ "character_name": name_lookup[member["character_id"]],
366
+ "solar_system_name": name_lookup[member["solar_system_id"]],
367
+ "ship_type_name": name_lookup[member["ship_type_id"]],
368
+ "is_fleet_boss": is_fleet_boss,
369
+ }
370
+ )
371
+
372
+ aggregate = _get_fleet_aggregate(fleet_infos=fleet_infos)
373
+
374
+ return FleetViewAggregate(fleet=fleet_infos, aggregate=aggregate)
389
375
 
390
- aggregate = _get_fleet_aggregate(fleet_infos=fleet_infos)
376
+ except Exception as e: # pylint: disable=broad-exception-caught
377
+ logger.error(f"Failed to get fleet composition for fleet {fleet_id}: {e}")
391
378
 
392
- return FleetViewAggregate(fleet=fleet_infos, aggregate=aggregate)
379
+ return None
@@ -0,0 +1,3 @@
1
+ {% load sri %}
2
+
3
+ {% sri_static "fleetfinder/js/fleetfinder.min.js" %}