aa-fleetfinder 2.5.1__py3-none-any.whl → 2.6.1__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.
- {aa_fleetfinder-2.5.1.dist-info → aa_fleetfinder-2.6.1.dist-info}/METADATA +2 -2
- {aa_fleetfinder-2.5.1.dist-info → aa_fleetfinder-2.6.1.dist-info}/RECORD +27 -25
- fleetfinder/__init__.py +1 -1
- fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/de/LC_MESSAGES/django.po +23 -10
- fleetfinder/locale/django.pot +21 -8
- fleetfinder/locale/es/LC_MESSAGES/django.po +23 -8
- fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/it_IT/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/ja/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +23 -8
- fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/ru/LC_MESSAGES/django.po +23 -8
- fleetfinder/locale/sk/LC_MESSAGES/django.po +20 -7
- fleetfinder/locale/uk/LC_MESSAGES/django.po +23 -8
- fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +23 -8
- fleetfinder/tasks.py +121 -150
- fleetfinder/templates/fleetfinder/dashboard.html +60 -47
- fleetfinder/templates/fleetfinder/fleet-details.html +7 -1
- fleetfinder/templates/fleetfinder/partials/body/form-fleet-details.html +8 -8
- fleetfinder/tests/test_tasks.py +140 -0
- fleetfinder/tests/test_views.py +416 -0
- fleetfinder/views.py +176 -62
- {aa_fleetfinder-2.5.1.dist-info → aa_fleetfinder-2.6.1.dist-info}/WHEEL +0 -0
- {aa_fleetfinder-2.5.1.dist-info → aa_fleetfinder-2.6.1.dist-info}/licenses/LICENSE +0 -0
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_
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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 "{
|
|
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
209
|
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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(
|
|
231
|
+
def send_fleet_invitation(fleet_id: int, character_ids: list) -> None:
|
|
289
232
|
"""
|
|
290
|
-
Send
|
|
291
|
-
|
|
292
|
-
|
|
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,118 @@ 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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
276
|
+
if not fleets.exists():
|
|
277
|
+
logger.info("No registered fleets found. Nothing to do...")
|
|
328
278
|
|
|
329
|
-
|
|
279
|
+
return
|
|
330
280
|
|
|
331
|
-
|
|
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
|
-
)
|
|
281
|
+
logger.info(f"Processing {fleets.count()} registered fleets...")
|
|
337
282
|
|
|
338
|
-
|
|
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.")
|
|
339
286
|
|
|
340
|
-
|
|
341
|
-
|
|
287
|
+
return
|
|
288
|
+
|
|
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(fleet_id: int) -> FleetViewAggregate | None:
|
|
346
295
|
"""
|
|
347
|
-
|
|
296
|
+
Get the composition of a fleet by its ID
|
|
297
|
+
This task retrieves the composition of a fleet using its ESI ID.
|
|
348
298
|
|
|
349
|
-
:param fleet_id:
|
|
350
|
-
:
|
|
299
|
+
:param fleet_id: The ESI ID of the fleet to retrieve
|
|
300
|
+
:type fleet_id: int
|
|
301
|
+
:return: FleetViewAggregate containing fleet members and aggregate data
|
|
302
|
+
:rtype: FleetViewAggregate | None
|
|
351
303
|
"""
|
|
352
304
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
305
|
+
try:
|
|
306
|
+
fleet = Fleet.objects.get(fleet_id=fleet_id)
|
|
307
|
+
except Fleet.DoesNotExist:
|
|
308
|
+
logger.error(f"Fleet with ID {fleet_id} not found")
|
|
309
|
+
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
logger.info(
|
|
313
|
+
f'Getting fleet composition for fleet "{fleet.name}" '
|
|
314
|
+
f"of {fleet.fleet_commander.character_name} (ESI ID: {fleet_id})"
|
|
357
315
|
)
|
|
358
|
-
fleet_infos = esi.client.Fleets.get_fleets_fleet_id_members(
|
|
359
|
-
fleet_id=fleet_id, token=token.valid_access_token()
|
|
360
|
-
).result()
|
|
361
316
|
|
|
362
|
-
|
|
363
|
-
systems = {}
|
|
364
|
-
ship_type = {}
|
|
317
|
+
required_scopes = ["esi-fleets.read_fleet.v1"]
|
|
365
318
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
319
|
+
try:
|
|
320
|
+
token = Token.get_token(
|
|
321
|
+
character_id=fleet.fleet_commander.character_id, scopes=required_scopes
|
|
322
|
+
)
|
|
370
323
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
ids.extend(list(ship_type.keys()))
|
|
324
|
+
fleet_infos = esi.client.Fleets.get_fleets_fleet_id_members(
|
|
325
|
+
fleet_id=fleet_id, token=token.valid_access_token()
|
|
326
|
+
).result()
|
|
375
327
|
|
|
376
|
-
|
|
328
|
+
# Get all unique IDs and fetch names in one call
|
|
329
|
+
all_ids = {
|
|
330
|
+
item_id
|
|
331
|
+
for member in fleet_infos
|
|
332
|
+
for item_id in [
|
|
333
|
+
member["character_id"],
|
|
334
|
+
member["solar_system_id"],
|
|
335
|
+
member["ship_type_id"],
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
ids_to_name = esi.client.Universe.post_universe_names(
|
|
340
|
+
ids=list(all_ids)
|
|
341
|
+
).result()
|
|
377
342
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
343
|
+
# Create a lookup dictionary for names
|
|
344
|
+
name_lookup = {item["id"]: item["name"] for item in ids_to_name}
|
|
345
|
+
|
|
346
|
+
# Add names to fleet members
|
|
347
|
+
for member in fleet_infos:
|
|
348
|
+
member.update(
|
|
349
|
+
{
|
|
350
|
+
"character_name": name_lookup[member["character_id"]],
|
|
351
|
+
"solar_system_name": name_lookup[member["solar_system_id"]],
|
|
352
|
+
"ship_type_name": name_lookup[member["ship_type_id"]],
|
|
353
|
+
}
|
|
354
|
+
)
|
|
381
355
|
|
|
382
|
-
|
|
383
|
-
member["solar_system_id"]
|
|
384
|
-
)
|
|
385
|
-
member["solar_system_name"] = ids_to_name[index_solar_system]["name"]
|
|
356
|
+
aggregate = _get_fleet_aggregate(fleet_infos=fleet_infos)
|
|
386
357
|
|
|
387
|
-
|
|
388
|
-
member["ship_type_name"] = ids_to_name[index_ship_type]["name"]
|
|
358
|
+
return FleetViewAggregate(fleet=fleet_infos, aggregate=aggregate)
|
|
389
359
|
|
|
390
|
-
|
|
360
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
361
|
+
logger.error(f"Failed to get fleet composition for fleet {fleet_id}: {e}")
|
|
391
362
|
|
|
392
|
-
|
|
363
|
+
return None
|
|
@@ -51,52 +51,57 @@
|
|
|
51
51
|
const DATETIME_FORMAT = 'YYYY-MM-DD, HH:mm';
|
|
52
52
|
const table_fleet_overview = $('#table_available-fleets');
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
63
|
-
columns: [
|
|
64
|
-
{
|
|
65
|
-
data: 'fleet_commander',
|
|
66
|
-
render: {
|
|
67
|
-
_: 'html',
|
|
68
|
-
sort: 'sort'
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
{data: 'fleet_name'},
|
|
72
|
-
{
|
|
73
|
-
data: 'created_at',
|
|
74
|
-
},
|
|
75
|
-
{data: 'join'},
|
|
76
|
-
{% if perms.fleetfinder.manage_fleets %}
|
|
77
|
-
{data: 'details'},
|
|
78
|
-
{data: 'edit'},
|
|
79
|
-
{% endif %}
|
|
80
|
-
],
|
|
81
|
-
columnDefs: [
|
|
82
|
-
{
|
|
83
|
-
targets: 2,
|
|
84
|
-
render: DataTable.render.date(DATETIME_FORMAT)
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
orderable: false,
|
|
88
|
-
targets: [3]
|
|
89
|
-
},
|
|
90
|
-
{% if perms.fleetfinder.manage_fleets %}
|
|
91
|
-
{
|
|
92
|
-
orderable: false,
|
|
93
|
-
targets: [4, 5]
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the datatable for fleet overview
|
|
56
|
+
*/
|
|
57
|
+
fetchGet({url: '{% url "fleetfinder:ajax_dashboard" %}'})
|
|
58
|
+
.then((data) => {
|
|
59
|
+
table_fleet_overview.DataTable({
|
|
60
|
+
language: {
|
|
61
|
+
url: '{{ DT_LANG_PATH }}'
|
|
94
62
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
63
|
+
data: data,
|
|
64
|
+
columns: [
|
|
65
|
+
{
|
|
66
|
+
data: 'fleet_commander',
|
|
67
|
+
render: {
|
|
68
|
+
_: 'html',
|
|
69
|
+
sort: 'sort'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{data: 'fleet_name'},
|
|
73
|
+
{
|
|
74
|
+
data: 'created_at',
|
|
75
|
+
},
|
|
76
|
+
{data: 'join'},
|
|
77
|
+
{% if perms.fleetfinder.manage_fleets %}
|
|
78
|
+
{data: 'details'},
|
|
79
|
+
{data: 'edit'},
|
|
80
|
+
{% endif %}
|
|
81
|
+
],
|
|
82
|
+
columnDefs: [
|
|
83
|
+
{
|
|
84
|
+
targets: 2,
|
|
85
|
+
render: DataTable.render.date(DATETIME_FORMAT)
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
orderable: false,
|
|
89
|
+
targets: [3]
|
|
90
|
+
},
|
|
91
|
+
{% if perms.fleetfinder.manage_fleets %}
|
|
92
|
+
{
|
|
93
|
+
orderable: false,
|
|
94
|
+
targets: [4, 5]
|
|
95
|
+
},
|
|
96
|
+
{% endif %}
|
|
97
|
+
],
|
|
98
|
+
order: [[0, 'asc']],
|
|
99
|
+
paging: false
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
.catch((error) => {
|
|
103
|
+
console.error('Error fetching fleet data:', error);
|
|
104
|
+
});
|
|
100
105
|
|
|
101
106
|
/**
|
|
102
107
|
* Refresh the datatable information every 30 seconds
|
|
@@ -108,7 +113,7 @@
|
|
|
108
113
|
* reload datatable "fleetfinderFleetsTable"
|
|
109
114
|
*/
|
|
110
115
|
const reloadDataTable = () => {
|
|
111
|
-
|
|
116
|
+
const dt = Date.now() - expectedReloadDatatable; // The drift (positive for overshooting)
|
|
112
117
|
|
|
113
118
|
if (dt > intervalReloadDatatable) {
|
|
114
119
|
// Something awful happened. Maybe the browser (tab) was inactive?
|
|
@@ -120,7 +125,15 @@
|
|
|
120
125
|
);
|
|
121
126
|
}
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
fetchGet({url: '{% url "fleetfinder:ajax_dashboard" %}'})
|
|
129
|
+
.then((newData) => {
|
|
130
|
+
const dataTable = table_fleet_overview.DataTable();
|
|
131
|
+
|
|
132
|
+
dataTable.clear().rows.add(newData).draw();
|
|
133
|
+
})
|
|
134
|
+
.catch((error) => {
|
|
135
|
+
console.error('Error fetching updated data:', error);
|
|
136
|
+
});
|
|
124
137
|
|
|
125
138
|
expectedReloadDatatable += intervalReloadDatatable;
|
|
126
139
|
|
|
@@ -78,7 +78,10 @@
|
|
|
78
78
|
const tabe_fleet_composition = $('#table_fleet_composition');
|
|
79
79
|
|
|
80
80
|
const populateDatatables = () => {
|
|
81
|
-
|
|
81
|
+
fetchGet({
|
|
82
|
+
url: '{% url "fleetfinder:ajax_fleet_details" fleet_id %}'
|
|
83
|
+
})
|
|
84
|
+
.then((data) => {
|
|
82
85
|
table_fleet_members.DataTable({
|
|
83
86
|
language: {
|
|
84
87
|
url: '{{ DT_LANG_PATH }}'
|
|
@@ -106,6 +109,9 @@
|
|
|
106
109
|
],
|
|
107
110
|
order: [[1, 'desc']]
|
|
108
111
|
});
|
|
112
|
+
})
|
|
113
|
+
.catch((error) => {
|
|
114
|
+
console.error('Error fetching fleet details:', error);
|
|
109
115
|
});
|
|
110
116
|
};
|
|
111
117
|
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
</p>
|
|
34
34
|
</div>
|
|
35
35
|
|
|
36
|
-
<div class="mb-3"
|
|
37
|
-
<label for="motd" class="control-label">{% translate "Fleet MOTD" %}</label
|
|
38
|
-
|
|
39
|
-
<div
|
|
40
|
-
<textarea class="form-control" name="motd" id="motd" rows="10">{% if motd %}{{ motd }}{% endif %}</textarea
|
|
41
|
-
</div
|
|
42
|
-
</div
|
|
36
|
+
{# <div class="mb-3">#}
|
|
37
|
+
{# <label for="motd" class="control-label">{% translate "Fleet MOTD" %}</label>#}
|
|
38
|
+
{##}
|
|
39
|
+
{# <div>#}
|
|
40
|
+
{# <textarea class="form-control" name="motd" id="motd" rows="10">{% if motd %}{{ motd }}{% endif %}</textarea>#}
|
|
41
|
+
{# </div>#}
|
|
42
|
+
{# </div>#}
|
|
43
43
|
|
|
44
44
|
<div class="mb-3">
|
|
45
|
-
<input type="checkbox" style="margin-right: 5px;" class="form-check-input" name="free_move" id="free_move" {% if is_free_move %}checked{% endif %}>
|
|
45
|
+
<input type="checkbox" style="margin-right: 5px;" class="form-check-input" name="free_move" id="free_move" {% if fleet.is_free_move %}checked{% endif %}>
|
|
46
46
|
<label for="free_move" class="form-check-label">{% translate "Enable free move" %}</label>
|
|
47
47
|
</div>
|
|
48
48
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the fleetfinder.tasks module.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Standard Library
|
|
6
|
+
from unittest import TestCase
|
|
7
|
+
from unittest.mock import Mock, patch
|
|
8
|
+
|
|
9
|
+
# AA Fleet Finder
|
|
10
|
+
from fleetfinder.tasks import (
|
|
11
|
+
_get_fleet_aggregate,
|
|
12
|
+
check_fleet_adverts,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestGetFleetAggregate(TestCase):
|
|
17
|
+
"""
|
|
18
|
+
Tests for the _get_fleet_aggregate function.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def test_returns_correct_counts_for_valid_fleet_infos(self):
|
|
22
|
+
"""
|
|
23
|
+
Test that _get_fleet_aggregate returns correct counts for valid fleet_infos.
|
|
24
|
+
|
|
25
|
+
:return:
|
|
26
|
+
:rtype:
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
fleet_infos = [
|
|
30
|
+
{"ship_type_name": "Cruiser"},
|
|
31
|
+
{"ship_type_name": "Cruiser"},
|
|
32
|
+
{"ship_type_name": "Battleship"},
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
result = _get_fleet_aggregate(fleet_infos)
|
|
36
|
+
|
|
37
|
+
assert result == {"Cruiser": 2, "Battleship": 1}
|
|
38
|
+
|
|
39
|
+
def test_returns_empty_dict_for_empty_fleet_infos(self):
|
|
40
|
+
"""
|
|
41
|
+
Test that _get_fleet_aggregate returns an empty dictionary for empty fleet_infos.
|
|
42
|
+
|
|
43
|
+
:return:
|
|
44
|
+
:rtype:
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
fleet_infos = []
|
|
48
|
+
|
|
49
|
+
result = _get_fleet_aggregate(fleet_infos)
|
|
50
|
+
|
|
51
|
+
assert result == {}
|
|
52
|
+
|
|
53
|
+
def test_returns_only_valid_ship_type_names(self):
|
|
54
|
+
"""
|
|
55
|
+
Test that _get_fleet_aggregate returns only valid ship type names.
|
|
56
|
+
|
|
57
|
+
:return:
|
|
58
|
+
:rtype:
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
fleet_infos = [
|
|
62
|
+
{"ship_type_name": "Cruiser"},
|
|
63
|
+
{},
|
|
64
|
+
{"ship_type_name": None},
|
|
65
|
+
{"ship_type_name": "Battleship"},
|
|
66
|
+
{"other_key": "Frigate"},
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
result = _get_fleet_aggregate(fleet_infos)
|
|
70
|
+
|
|
71
|
+
assert result == {"Cruiser": 1, "Battleship": 1}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestCheckFleetAdvert(TestCase):
|
|
75
|
+
"""
|
|
76
|
+
Tests for the check_fleet_adverts function.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
@patch("fleetfinder.models.Fleet.objects.all")
|
|
80
|
+
@patch("fleetfinder.tasks.fetch_esi_status")
|
|
81
|
+
def test_processes_registered_fleets_when_available(
|
|
82
|
+
self, mock_fetch_esi_status, mock_fleet_objects
|
|
83
|
+
):
|
|
84
|
+
"""
|
|
85
|
+
Test that check_fleet_adverts processes registered fleets when ESI is available.
|
|
86
|
+
|
|
87
|
+
:param mock_fetch_esi_status:
|
|
88
|
+
:type mock_fetch_esi_status:
|
|
89
|
+
:param mock_fleet_objects:
|
|
90
|
+
:type mock_fleet_objects:
|
|
91
|
+
:return:
|
|
92
|
+
:rtype:
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
mock_fetch_esi_status.return_value.is_ok = True
|
|
96
|
+
mock_fleet_objects.return_value.exists.return_value = True
|
|
97
|
+
mock_fleet_objects.return_value.count.return_value = 2
|
|
98
|
+
mock_fleet_objects.return_value.__iter__.return_value = iter([Mock(), Mock()])
|
|
99
|
+
|
|
100
|
+
check_fleet_adverts()
|
|
101
|
+
|
|
102
|
+
mock_fleet_objects.return_value.__iter__.assert_called_once()
|
|
103
|
+
mock_fetch_esi_status.assert_called_once()
|
|
104
|
+
|
|
105
|
+
@patch("fleetfinder.models.Fleet.objects.all")
|
|
106
|
+
@patch("fleetfinder.tasks.fetch_esi_status")
|
|
107
|
+
def test_logs_no_registered_fleets_when_none_exist(
|
|
108
|
+
self, mock_fetch_esi_status, mock_fleet_objects
|
|
109
|
+
):
|
|
110
|
+
"""
|
|
111
|
+
Test that check_fleet_adverts logs a message when no registered fleets exist.
|
|
112
|
+
|
|
113
|
+
:param mock_fetch_esi_status:
|
|
114
|
+
:type mock_fetch_esi_status:
|
|
115
|
+
:param mock_fleet_objects:
|
|
116
|
+
:type mock_fleet_objects:
|
|
117
|
+
:return:
|
|
118
|
+
:rtype:
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
mock_fetch_esi_status.return_value.is_ok = True
|
|
122
|
+
mock_fleet_objects.return_value.exists.return_value = False
|
|
123
|
+
|
|
124
|
+
check_fleet_adverts()
|
|
125
|
+
|
|
126
|
+
mock_fleet_objects.return_value.exists.assert_called_once()
|
|
127
|
+
mock_fetch_esi_status.assert_not_called()
|
|
128
|
+
|
|
129
|
+
@patch("fleetfinder.models.Fleet.objects.all")
|
|
130
|
+
@patch("fleetfinder.tasks.fetch_esi_status")
|
|
131
|
+
def test_aborts_processing_when_esi_is_unavailable(
|
|
132
|
+
self, mock_fetch_esi_status, mock_fleet_objects
|
|
133
|
+
):
|
|
134
|
+
mock_fetch_esi_status.return_value.is_ok = False
|
|
135
|
+
mock_fleet_objects.return_value.exists.return_value = True
|
|
136
|
+
|
|
137
|
+
check_fleet_adverts()
|
|
138
|
+
|
|
139
|
+
mock_fetch_esi_status.assert_called_once()
|
|
140
|
+
mock_fleet_objects.return_value.__iter__.assert_not_called()
|