aa-fleetfinder 2.7.0__py3-none-any.whl → 2.7.2__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.7.0.dist-info → aa_fleetfinder-2.7.2.dist-info}/METADATA +1 -1
- {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.2.dist-info}/RECORD +38 -32
- fleetfinder/__init__.py +1 -1
- fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/de/LC_MESSAGES/django.po +35 -33
- fleetfinder/locale/django.pot +31 -21
- fleetfinder/locale/es/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +69 -55
- fleetfinder/locale/it_IT/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/ja/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/ru/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/sk/LC_MESSAGES/django.po +30 -20
- fleetfinder/locale/uk/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/uk/LC_MESSAGES/django.po +52 -52
- fleetfinder/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +33 -23
- fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.js +86 -0
- fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js +2 -0
- fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js.map +1 -0
- fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.js +154 -0
- fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js +2 -0
- fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js.map +1 -0
- fleetfinder/static/fleetfinder/js/fleetfinder.min.js +1 -1
- fleetfinder/static/fleetfinder/js/fleetfinder.min.js.map +1 -1
- fleetfinder/tasks.py +13 -5
- fleetfinder/templates/fleetfinder/base.html +11 -0
- fleetfinder/templates/fleetfinder/bundles/js/fleetfinder-js.html +6 -0
- fleetfinder/templates/fleetfinder/dashboard.html +8 -83
- fleetfinder/templates/fleetfinder/fleet-details.html +22 -150
- fleetfinder/tests/test_views.py +63 -0
- fleetfinder/views.py +32 -3
- {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.2.dist-info}/WHEEL +0 -0
- {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.2.dist-info}/licenses/LICENSE +0 -0
fleetfinder/tasks.py
CHANGED
|
@@ -306,10 +306,10 @@ def get_fleet_composition( # pylint: disable=too-many-locals
|
|
|
306
306
|
|
|
307
307
|
try:
|
|
308
308
|
fleet = Fleet.objects.get(fleet_id=fleet_id)
|
|
309
|
-
except Fleet.DoesNotExist:
|
|
309
|
+
except Fleet.DoesNotExist as exc:
|
|
310
310
|
logger.error(f"Fleet with ID {fleet_id} not found")
|
|
311
311
|
|
|
312
|
-
|
|
312
|
+
raise Fleet.DoesNotExist(f"Fleet with ID {fleet_id} not found.") from exc
|
|
313
313
|
|
|
314
314
|
logger.info(
|
|
315
315
|
f'Getting fleet composition for fleet "{fleet.name}" '
|
|
@@ -342,8 +342,14 @@ def get_fleet_composition( # pylint: disable=too-many-locals
|
|
|
342
342
|
f"Found {len(all_ids)} unique IDs to fetch names for in fleet {fleet_id}"
|
|
343
343
|
)
|
|
344
344
|
|
|
345
|
-
# Process IDs in chunks to avoid ESI limits
|
|
346
|
-
|
|
345
|
+
# Process IDs in chunks of 1000 to avoid ESI limits.
|
|
346
|
+
# ESI has a limit of 1000 IDs per request, so we will chunk the requests,
|
|
347
|
+
# even though there is a theoretical limit of 768 unique IDs per fleet,
|
|
348
|
+
# so we never should hit the ESI limit.
|
|
349
|
+
# But to be on the safe side, we will chunk the requests in case CCP decides
|
|
350
|
+
# to change the fleet limit in the future, we will use a chunk size of 1000,
|
|
351
|
+
# which is the maximum allowed by ESI for the `post_universe_names` endpoint.
|
|
352
|
+
chunk_size = 1000
|
|
347
353
|
ids_to_name = []
|
|
348
354
|
all_ids_list = list(all_ids)
|
|
349
355
|
|
|
@@ -376,4 +382,6 @@ def get_fleet_composition( # pylint: disable=too-many-locals
|
|
|
376
382
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
377
383
|
logger.error(f"Failed to get fleet composition for fleet {fleet_id}: {e}")
|
|
378
384
|
|
|
379
|
-
|
|
385
|
+
raise RuntimeError(
|
|
386
|
+
f"Failed to get fleet composition for fleet {fleet_id} : {str(e)}"
|
|
387
|
+
) from e
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{% extends "allianceauth/base-bs5.html" %}
|
|
2
2
|
|
|
3
3
|
{% load i18n %}
|
|
4
|
+
{% load aa_i18n %}
|
|
4
5
|
|
|
5
6
|
{% block page_title %}
|
|
6
7
|
{% translate "Fleet Finder" %}
|
|
@@ -22,6 +23,16 @@
|
|
|
22
23
|
{% block content %}
|
|
23
24
|
<div class="aa-fleetfinder">
|
|
24
25
|
<div class="aa-fleetfinder-body">
|
|
26
|
+
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
const aaFleetFinderSettings = {
|
|
30
|
+
dataTables: {
|
|
31
|
+
languageUrl: "{{ DT_LANG_PATH }}",
|
|
32
|
+
datetimeFormat: 'YYYY-MM-DD, HH:mm',
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
25
36
|
{% block aa_fleetfinder_body %}{% endblock %}
|
|
26
37
|
</div>
|
|
27
38
|
|
|
@@ -38,91 +38,16 @@
|
|
|
38
38
|
{% block extra_javascript %}
|
|
39
39
|
{% include "bundles/datatables-js-bs5.html" %}
|
|
40
40
|
{% include "bundles/moment-js.html" with locale=True %}
|
|
41
|
-
{% include "fleetfinder/bundles/js/fleetfinder-js.html" %}
|
|
42
|
-
|
|
43
|
-
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
|
|
44
41
|
|
|
45
42
|
<script>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const DATETIME_FORMAT = 'YYYY-MM-DD, HH:mm';
|
|
51
|
-
const table_fleet_overview = $('#table_available-fleets');
|
|
52
|
-
let dataTable = null;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Initialize or update the datatable.
|
|
56
|
-
* If the table already exists, it will be updated with new data.
|
|
57
|
-
* If it does not exist, a new DataTable will be created.
|
|
58
|
-
*
|
|
59
|
-
* @param {Object} data - The fleet data to populate the table.
|
|
60
|
-
*/
|
|
61
|
-
const initializeOrUpdateTable = (data) => {
|
|
62
|
-
if (dataTable) {
|
|
63
|
-
// Update existing table
|
|
64
|
-
dataTable.clear().rows.add(data).draw();
|
|
65
|
-
} else {
|
|
66
|
-
// Initialize new table
|
|
67
|
-
dataTable = table_fleet_overview.DataTable({
|
|
68
|
-
language: {
|
|
69
|
-
url: '{{ DT_LANG_PATH }}'
|
|
70
|
-
},
|
|
71
|
-
data: data,
|
|
72
|
-
columns: [
|
|
73
|
-
{
|
|
74
|
-
data: 'fleet_commander',
|
|
75
|
-
render: {
|
|
76
|
-
_: 'html',
|
|
77
|
-
sort: 'sort'
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
data: 'fleet_name'
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
data: 'created_at',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
data: 'actions',
|
|
88
|
-
className: 'text-end',
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
columnDefs: [
|
|
92
|
-
{
|
|
93
|
-
targets: 2,
|
|
94
|
-
render: DataTable.render.date(DATETIME_FORMAT)
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
orderable: false,
|
|
98
|
-
targets: [3]
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
order: [[0, 'asc']],
|
|
102
|
-
paging: false
|
|
103
|
-
});
|
|
43
|
+
const aaFleetFinderSettingsOverride = {
|
|
44
|
+
dataTables: {
|
|
45
|
+
url: {
|
|
46
|
+
dashboard: '{% url "fleetfinder:ajax_dashboard" %}'
|
|
104
47
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
fleetfinderBootstrapTooltip({selector: '#table_available-fleets'});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Fetch and update fleet data
|
|
112
|
-
*/
|
|
113
|
-
const fetchFleetData = () => {
|
|
114
|
-
fetchGet({url: '{% url "fleetfinder:ajax_dashboard" %}'})
|
|
115
|
-
.then(initializeOrUpdateTable)
|
|
116
|
-
.catch((error) => {
|
|
117
|
-
console.error('Error fetching fleet data:', error);
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// Initial load
|
|
122
|
-
fetchFleetData();
|
|
123
|
-
|
|
124
|
-
// Refresh every 30 seconds
|
|
125
|
-
setInterval(fetchFleetData, 30000);
|
|
126
|
-
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
127
50
|
</script>
|
|
51
|
+
|
|
52
|
+
{% include "fleetfinder/bundles/js/fleetfinder-js.html" with view="dashboard" %}
|
|
128
53
|
{% endblock %}
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
{% endblock %}
|
|
9
9
|
|
|
10
10
|
{% block aa_fleetfinder_body %}
|
|
11
|
+
{% include "framework/header/page-header.html" with title=fleet.name subtitle=fleet.fleet_commander.character_name %}
|
|
12
|
+
|
|
13
|
+
<div class="alert alert-warning d-none" id="fleetfinder-fleet-details-warning"></div>
|
|
14
|
+
|
|
11
15
|
<div class="row">
|
|
12
16
|
<div class="col-lg-6 col-lg-push-6">
|
|
13
17
|
<div class="card card-primary">
|
|
@@ -19,7 +23,7 @@
|
|
|
19
23
|
|
|
20
24
|
<div class="card-body">
|
|
21
25
|
<div class="table-responsive">
|
|
22
|
-
<table class="table table-striped table-hover table-vertical-middle w-100" id="
|
|
26
|
+
<table class="table table-striped table-hover table-vertical-middle w-100" id="table-fleet-composition">
|
|
23
27
|
<thead>
|
|
24
28
|
<tr>
|
|
25
29
|
<th>{% translate "Ship class" %}</th>
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
|
|
45
49
|
<div class="card-body">
|
|
46
50
|
<div class="table-responsive">
|
|
47
|
-
<table class="table table-striped table-hover table-vertical-middle w-100" id="
|
|
51
|
+
<table class="table table-striped table-hover table-vertical-middle w-100" id="table-fleet-members">
|
|
48
52
|
<thead>
|
|
49
53
|
<tr>
|
|
50
54
|
<th>{% translate "Name" %}</th>
|
|
@@ -72,159 +76,27 @@
|
|
|
72
76
|
|
|
73
77
|
{% block extra_javascript %}
|
|
74
78
|
{% include "bundles/datatables-js-bs5.html" %}
|
|
75
|
-
{% include "fleetfinder/bundles/js/fleetfinder-js.html" %}
|
|
76
|
-
|
|
77
|
-
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
|
|
78
79
|
|
|
79
80
|
{% translate "Fleet boss" as l10nFleetBoss %}
|
|
80
81
|
{% translate "Kick member from fleet" as l10nKickMemberFromFleet %}
|
|
81
82
|
{% translate "An unknown error occurred." as l10nUnknownError %}
|
|
82
83
|
|
|
83
84
|
<script>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
destroy: true
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const populateDatatables = () => {
|
|
102
|
-
fetchGet({
|
|
103
|
-
url: url.fleetDetails,
|
|
104
|
-
})
|
|
105
|
-
.then((data) => {
|
|
106
|
-
table_fleet_members.DataTable({
|
|
107
|
-
...dataTableConfig,
|
|
108
|
-
data: data.fleet_member,
|
|
109
|
-
columns: [
|
|
110
|
-
{
|
|
111
|
-
render: (data, type, row) => {
|
|
112
|
-
const fwIcon = '<i class="fa-solid fa-star"></i>';
|
|
113
|
-
|
|
114
|
-
return row.is_fleet_boss
|
|
115
|
-
? `${row.character_name} <sup data-bs-tooltip="aa-fleetfinder" title="{{ l10nFleetBoss|escapejs }}"><small>${fwIcon}</small></sup>`
|
|
116
|
-
: row.character_name;
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{data: 'ship_type_name'},
|
|
120
|
-
{data: 'solar_system_name'},
|
|
121
|
-
{
|
|
122
|
-
render: (data, type, row) => {
|
|
123
|
-
const fwIcon = '<i class="fa-solid fa-user-minus"></i>';
|
|
124
|
-
const dataAttributes = Object.entries({
|
|
125
|
-
'data-character-id': row.character_id,
|
|
126
|
-
'data-character-name': row.character_name,
|
|
127
|
-
'data-bs-toggle': 'modal',
|
|
128
|
-
'data-bs-target': '#kick-fleet-member',
|
|
129
|
-
'data-bs-tooltip': 'aa-fleetfinder'
|
|
130
|
-
}).map(([key, value]) => {
|
|
131
|
-
return `${key}="${value}"`;
|
|
132
|
-
}).join(' ');
|
|
133
|
-
|
|
134
|
-
return row.is_fleet_boss
|
|
135
|
-
? ''
|
|
136
|
-
: `<button type="button" class="btn btn-sm btn-danger" ${dataAttributes} title="{{ l10nKickMemberFromFleet|escapejs }}">${fwIcon}</button>`;
|
|
137
|
-
},
|
|
138
|
-
orderable: false,
|
|
139
|
-
searchable: false,
|
|
140
|
-
width: '50px',
|
|
141
|
-
className: 'text-end'
|
|
142
|
-
}
|
|
143
|
-
],
|
|
144
|
-
createdRow: (row, data, rowIndex) => {
|
|
145
|
-
$(row).attr('data-row-id', rowIndex);
|
|
146
|
-
$(row).attr('data-character-id', data.character_id);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
table_fleet_composition.DataTable({
|
|
151
|
-
...dataTableConfig,
|
|
152
|
-
data: data.fleet_composition,
|
|
153
|
-
columns: [
|
|
154
|
-
{data: 'ship_type_name'},
|
|
155
|
-
{data: 'number', className: 'text-right', width: '100px'}
|
|
156
|
-
],
|
|
157
|
-
order: [[1, 'desc']]
|
|
158
|
-
});
|
|
159
|
-
})
|
|
160
|
-
.then(() => {
|
|
161
|
-
// Initialize tooltips for the kick buttons
|
|
162
|
-
fleetfinderBootstrapTooltip({selector: '#table_fleet_members'});
|
|
163
|
-
})
|
|
164
|
-
.catch((error) => {
|
|
165
|
-
console.error('Error fetching fleet details:', error);
|
|
166
|
-
});
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
populateDatatables();
|
|
170
|
-
|
|
171
|
-
setInterval(populateDatatables, 30000);
|
|
172
|
-
|
|
173
|
-
/* Modals
|
|
174
|
-
------------------------------------------------------------------------- */
|
|
175
|
-
// Handle the kick fleet member modal
|
|
176
|
-
$('#kick-fleet-member')
|
|
177
|
-
.on('show.bs.modal', (event) => {
|
|
178
|
-
const button = $(event.relatedTarget);
|
|
179
|
-
const characterId = button.data('character-id');
|
|
180
|
-
const characterName = button.data('character-name');
|
|
181
|
-
const link = url.kickFleetMember;
|
|
182
|
-
const csrfToken = '{{ csrf_token }}';
|
|
183
|
-
|
|
184
|
-
// Populate the modal content
|
|
185
|
-
$('#kick-fleet-member-character-name').text(characterName);
|
|
186
|
-
|
|
187
|
-
$('#modal-button-confirm-kick-fleet-member')
|
|
188
|
-
// Remove any previous click handlers to avoid multiple bindings
|
|
189
|
-
.off('click.kickMember')
|
|
190
|
-
// Bind the click event for the confirmation button
|
|
191
|
-
.on('click.kickMember', () => {
|
|
192
|
-
fetchPost({
|
|
193
|
-
url: link,
|
|
194
|
-
csrfToken: csrfToken,
|
|
195
|
-
payload: {
|
|
196
|
-
memberId: characterId
|
|
197
|
-
},
|
|
198
|
-
responseIsJson: true
|
|
199
|
-
})
|
|
200
|
-
.then((data) => {
|
|
201
|
-
if (data.success) {
|
|
202
|
-
populateDatatables();
|
|
203
|
-
|
|
204
|
-
$('#kick-fleet-member').modal('hide');
|
|
205
|
-
} else {
|
|
206
|
-
$('#kick-fleet-member .modal-kick-fleet-member-error')
|
|
207
|
-
.removeClass('d-none')
|
|
208
|
-
.find('.modal-kick-fleet-member-error-message')
|
|
209
|
-
.text(data.error || '{{ l10nUnknownError|escapejs }}');
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
.catch((error) => {
|
|
213
|
-
console.error('Error kicking fleet member:', error);
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
})
|
|
217
|
-
.on('hide.bs.modal', () => {
|
|
218
|
-
// Reset modal content
|
|
219
|
-
$('#kick-fleet-member-character-name').empty();
|
|
220
|
-
$('#kick-fleet-member .modal-kick-fleet-member-error')
|
|
221
|
-
.addClass('d-none')
|
|
222
|
-
.find('.modal-kick-fleet-member-error-message')
|
|
223
|
-
.empty();
|
|
224
|
-
|
|
225
|
-
// Clean up event handler
|
|
226
|
-
$('#modal-button-confirm-kick-fleet-member').off('click.kickMember');
|
|
227
|
-
});
|
|
228
|
-
});
|
|
85
|
+
const aaFleetFinderSettingsOverride = {
|
|
86
|
+
dataTables: {
|
|
87
|
+
url: {
|
|
88
|
+
fleetDetails: '{% url "fleetfinder:ajax_fleet_details" fleet.fleet_id %}',
|
|
89
|
+
kickFleetMember: '{% url "fleetfinder:ajax_fleet_kick_member" fleet.fleet_id %}'
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
l10n: {
|
|
93
|
+
fleetBoss: '{{ l10nFleetBoss|escapejs }}',
|
|
94
|
+
kickMemberFromFleet: '{{ l10nKickMemberFromFleet|escapejs }}',
|
|
95
|
+
unknownError: '{{ l10nUnknownError|escapejs }}'
|
|
96
|
+
},
|
|
97
|
+
csrfToken: '{{ csrf_token }}'
|
|
98
|
+
};
|
|
229
99
|
</script>
|
|
100
|
+
|
|
101
|
+
{% include "fleetfinder/bundles/js/fleetfinder-js.html" with view="fleet-details" %}
|
|
230
102
|
{% endblock %}
|
fleetfinder/tests/test_views.py
CHANGED
|
@@ -402,6 +402,21 @@ class TestFleetDetailsView(FleetfinderTestViews):
|
|
|
402
402
|
|
|
403
403
|
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
|
404
404
|
|
|
405
|
+
def test_fleet_redirects_to_dashboard_if_not_found(self):
|
|
406
|
+
"""
|
|
407
|
+
Test that the fleet_details view redirects to the dashboard if the fleet does not exist.
|
|
408
|
+
|
|
409
|
+
:return:
|
|
410
|
+
:rtype:
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
self.client.force_login(self.user_with_manage_perms)
|
|
414
|
+
|
|
415
|
+
response = self.client.get(reverse("fleetfinder:fleet_details", args=[123]))
|
|
416
|
+
|
|
417
|
+
self.assertEqual(response.status_code, HTTPStatus.FOUND)
|
|
418
|
+
self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
|
|
419
|
+
|
|
405
420
|
|
|
406
421
|
class TestAjaxFleetDetailsView(FleetfinderTestViews):
|
|
407
422
|
"""
|
|
@@ -471,3 +486,51 @@ class TestAjaxFleetDetailsView(FleetfinderTestViews):
|
|
|
471
486
|
|
|
472
487
|
self.assertEqual(response.status_code, HTTPStatus.OK)
|
|
473
488
|
self.assertEqual(json.loads(response.content), expected_fleet_composition)
|
|
489
|
+
|
|
490
|
+
@patch("fleetfinder.views.get_fleet_composition")
|
|
491
|
+
def test_returns_error_when_fleet_does_not_exist(self, mock_get_fleet_composition):
|
|
492
|
+
"""
|
|
493
|
+
Test that the ajax_fleet_details view returns an error when the fleet does not exist.
|
|
494
|
+
|
|
495
|
+
:param mock_get_fleet_composition:
|
|
496
|
+
:type mock_get_fleet_composition:
|
|
497
|
+
:return:
|
|
498
|
+
:rtype:
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
mock_get_fleet_composition.side_effect = Fleet.DoesNotExist
|
|
502
|
+
|
|
503
|
+
self.client.force_login(user=self.user_with_manage_perms)
|
|
504
|
+
|
|
505
|
+
url = reverse("fleetfinder:ajax_fleet_details", args=[123])
|
|
506
|
+
response = self.client.get(url)
|
|
507
|
+
|
|
508
|
+
self.assertEqual(response.status_code, HTTPStatus.OK)
|
|
509
|
+
self.assertJSONEqual(
|
|
510
|
+
response.content,
|
|
511
|
+
{"error": "Fleet with ID 123 does not exist."},
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
@patch("fleetfinder.views.get_fleet_composition")
|
|
515
|
+
def test_returns_error_when_runtime_error_occurs(self, mock_get_fleet_composition):
|
|
516
|
+
"""
|
|
517
|
+
Test that the ajax_fleet_details view returns an error when a runtime error occurs.
|
|
518
|
+
|
|
519
|
+
:param mock_get_fleet_composition:
|
|
520
|
+
:type mock_get_fleet_composition:
|
|
521
|
+
:return:
|
|
522
|
+
:rtype:
|
|
523
|
+
"""
|
|
524
|
+
|
|
525
|
+
mock_get_fleet_composition.side_effect = RuntimeError("Unexpected error")
|
|
526
|
+
|
|
527
|
+
self.client.force_login(user=self.user_with_manage_perms)
|
|
528
|
+
|
|
529
|
+
url = reverse("fleetfinder:ajax_fleet_details", args=[123])
|
|
530
|
+
response = self.client.get(url)
|
|
531
|
+
|
|
532
|
+
self.assertEqual(response.status_code, HTTPStatus.OK)
|
|
533
|
+
self.assertJSONEqual(
|
|
534
|
+
response.content,
|
|
535
|
+
{"error": "Error retrieving fleet composition: Unexpected error"},
|
|
536
|
+
)
|
fleetfinder/views.py
CHANGED
|
@@ -462,9 +462,25 @@ def fleet_details(request, fleet_id):
|
|
|
462
462
|
:return:
|
|
463
463
|
"""
|
|
464
464
|
|
|
465
|
-
|
|
465
|
+
try:
|
|
466
|
+
fleet = Fleet.objects.get(fleet_id=fleet_id)
|
|
467
|
+
except Fleet.DoesNotExist:
|
|
468
|
+
logger.debug(f"Fleet with ID {fleet_id} does not exist.")
|
|
466
469
|
|
|
467
|
-
|
|
470
|
+
messages.error(
|
|
471
|
+
request,
|
|
472
|
+
mark_safe(
|
|
473
|
+
_(
|
|
474
|
+
"<h4>Error!</h4><p>Fleet does not exist or is no longer available.</p>"
|
|
475
|
+
)
|
|
476
|
+
),
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return redirect("fleetfinder:dashboard")
|
|
480
|
+
|
|
481
|
+
context = {"fleet": fleet}
|
|
482
|
+
|
|
483
|
+
logger.info(msg=f"Fleet {fleet.fleet_id} details view called by {request.user}")
|
|
468
484
|
|
|
469
485
|
return render(
|
|
470
486
|
request=request,
|
|
@@ -485,7 +501,20 @@ def ajax_fleet_details(
|
|
|
485
501
|
:param fleet_id:
|
|
486
502
|
"""
|
|
487
503
|
|
|
488
|
-
|
|
504
|
+
try:
|
|
505
|
+
fleet = get_fleet_composition(fleet_id)
|
|
506
|
+
except Fleet.DoesNotExist:
|
|
507
|
+
logger.debug(f"Fleet with ID {fleet_id} does not exist.")
|
|
508
|
+
|
|
509
|
+
return JsonResponse(
|
|
510
|
+
data={"error": _(f"Fleet with ID {fleet_id} does not exist.")}, safe=False
|
|
511
|
+
)
|
|
512
|
+
except RuntimeError as ex:
|
|
513
|
+
logger.debug(f"Error retrieving fleet composition: {ex}", exc_info=True)
|
|
514
|
+
|
|
515
|
+
return JsonResponse(
|
|
516
|
+
data={"error": _(f"Error retrieving fleet composition: {ex}")}, safe=False
|
|
517
|
+
)
|
|
489
518
|
|
|
490
519
|
data = {
|
|
491
520
|
"fleet_member": list(fleet.fleet),
|
|
File without changes
|
|
File without changes
|