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

Files changed (35) hide show
  1. {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.1.dist-info}/METADATA +1 -1
  2. {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.1.dist-info}/RECORD +35 -29
  3. fleetfinder/__init__.py +1 -1
  4. fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +30 -20
  5. fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
  6. fleetfinder/locale/de/LC_MESSAGES/django.po +35 -33
  7. fleetfinder/locale/django.pot +31 -21
  8. fleetfinder/locale/es/LC_MESSAGES/django.po +30 -20
  9. fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +30 -20
  10. fleetfinder/locale/it_IT/LC_MESSAGES/django.po +30 -20
  11. fleetfinder/locale/ja/LC_MESSAGES/django.po +30 -20
  12. fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +30 -20
  13. fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +30 -20
  14. fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +30 -20
  15. fleetfinder/locale/ru/LC_MESSAGES/django.po +30 -20
  16. fleetfinder/locale/sk/LC_MESSAGES/django.po +30 -20
  17. fleetfinder/locale/uk/LC_MESSAGES/django.po +30 -20
  18. fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +30 -20
  19. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.js +86 -0
  20. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js +2 -0
  21. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js.map +1 -0
  22. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.js +154 -0
  23. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js +2 -0
  24. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js.map +1 -0
  25. fleetfinder/static/fleetfinder/js/fleetfinder.min.js +1 -1
  26. fleetfinder/static/fleetfinder/js/fleetfinder.min.js.map +1 -1
  27. fleetfinder/tasks.py +13 -5
  28. fleetfinder/templates/fleetfinder/base.html +11 -0
  29. fleetfinder/templates/fleetfinder/bundles/js/fleetfinder-js.html +6 -0
  30. fleetfinder/templates/fleetfinder/dashboard.html +8 -83
  31. fleetfinder/templates/fleetfinder/fleet-details.html +22 -150
  32. fleetfinder/tests/test_views.py +63 -0
  33. fleetfinder/views.py +32 -3
  34. {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.1.dist-info}/WHEEL +0 -0
  35. {aa_fleetfinder-2.7.0.dist-info → aa_fleetfinder-2.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -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="table_fleet_composition">
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="table_fleet_members">
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
- $(document).ready(() => {
85
- /* DataTables
86
- ------------------------------------------------------------------------- */
87
- const table_fleet_members = $('#table_fleet_members');
88
- const table_fleet_composition = $('#table_fleet_composition');
89
- const url = {
90
- kickFleetMember: '{% url "fleetfinder:ajax_fleet_kick_member" fleet_id %}',
91
- fleetDetails: '{% url "fleetfinder:ajax_fleet_details" fleet_id %}'
92
- };
93
- const dataTableConfig = {
94
- language: {
95
- url: '{{ DT_LANG_PATH }}'
96
- },
97
- paging: false,
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 %}
@@ -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
- context = {"fleet_id": fleet_id}
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
- logger.info(msg=f"Fleet {fleet_id} details view called by {request.user}")
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
- fleet = get_fleet_composition(fleet_id)
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),