Skip to content

Commit ae17fa0

Browse files
authored
Merge branch 'master' into issues/256-delete-org-blocked
2 parents 69a9d49 + c142d19 commit ae17fa0

6 files changed

Lines changed: 147 additions & 3 deletions

File tree

openwisp_firmware_upgrader/admin.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,10 +444,39 @@ def change_view(self, request, object_id, extra_context=None, **kwargs):
444444
args=["00000000-0000-0000-0000-000000000000"],
445445
)
446446
extra_context["django_locale"] = get_language()
447+
obj = self.get_object(request, object_id)
448+
# for custom breadcrumbs
449+
if obj and obj.batch_id:
450+
batch_opts = BatchUpgradeOperation._meta
451+
batch_admin_prefix = f"admin:{batch_opts.app_label}_{batch_opts.model_name}"
452+
extra_context["batch"] = obj.batch
453+
extra_context["batch_changelist_url"] = reverse(
454+
f"{batch_admin_prefix}_changelist"
455+
)
456+
extra_context["batch_change_url"] = reverse(
457+
f"{batch_admin_prefix}_change",
458+
args=[obj.batch_id],
459+
)
460+
batch_admin = self.admin_site._registry.get(BatchUpgradeOperation)
461+
extra_context["batch_has_view_permission"] = (
462+
batch_admin.has_view_permission(request) if batch_admin else False
463+
)
447464
return super().change_view(
448465
request, object_id, extra_context=extra_context, **kwargs
449466
)
450467

468+
def get_queryset(self, request):
469+
return super().get_queryset(request).select_related("batch")
470+
471+
def get_object(self, request, object_id, from_field=None):
472+
"""Avoids duplicating queries in change_view custom logic"""
473+
cache_attr = f"_cached_object_{object_id}_{from_field}"
474+
if not hasattr(request, cache_attr):
475+
setattr(
476+
request, cache_attr, super().get_object(request, object_id, from_field)
477+
)
478+
return getattr(request, cache_attr)
479+
451480
def get_fields(self, request, obj=None):
452481
fields = super().get_fields(request, obj).copy()
453482
if self._should_display_batch(obj, fields):

openwisp_firmware_upgrader/base/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ class Meta:
556556
verbose_name_plural = _("Mass upgrade operations")
557557

558558
def __str__(self):
559-
return f"Upgrade of {self.build} on {self.created}"
559+
return f"{self.build} ({timezone.localtime(self.created).strftime('%Y-%m-%d %H:%M:%S')})"
560560

561561
def clean(self):
562562
super().clean()
@@ -824,6 +824,9 @@ class AbstractUpgradeOperation(UpgradeOptionsMixin, TimeStampedEditableModel):
824824
null=True,
825825
)
826826

827+
def __str__(self):
828+
return f"{self.device} ({timezone.localtime(self.created).strftime('%Y-%m-%d %H:%M:%S')})"
829+
827830
class Meta:
828831
abstract = True
829832

openwisp_firmware_upgrader/templates/admin/firmware_upgrader/upgrade_operation_change_form.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
{% extends "admin/change_form.html" %}
22
{% load i18n admin_urls static %}
33

4+
{% block breadcrumbs %}
5+
{% if batch %}
6+
<div class="breadcrumbs">
7+
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
8+
&rsaquo;
9+
<a href="{% url 'admin:app_list' app_label=opts.app_label %}">
10+
{{ opts.app_config.verbose_name }}
11+
</a>
12+
&rsaquo;
13+
{% if batch_has_view_permission %}
14+
<a href="{{ batch_changelist_url }}">{% trans 'Mass upgrade operations' %}</a>
15+
{% else %}
16+
{% trans 'Mass upgrade operations' %}
17+
{% endif %}
18+
&rsaquo;
19+
{% if batch_has_view_permission %}
20+
<a href="{{ batch_change_url }}">{{ batch }}</a>
21+
{% else %}
22+
{{ batch }}
23+
{% endif %}
24+
&rsaquo; {{ original }}
25+
</div>
26+
{% else %}
27+
{{ block.super }}
28+
{% endif %}
29+
{% endblock %}
30+
431
{% block extrahead %}
532
{{ block.super }}
633
<link rel="stylesheet" type="text/css" href="{% static "firmware-upgrader/css/upgrade-progress.css" %}">

openwisp_firmware_upgrader/tests/test_admin.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,74 @@ def test_massive_upgrade_operation_page(self, *args):
863863
self.assertContains(response, "Failure rate")
864864
self.assertContains(response, "Abortion rate")
865865

866+
def test_upgrade_operation_change_breadcrumb_with_batch(self, *args):
867+
self.test_upgrade_all()
868+
uo = UpgradeOperation.objects.first()
869+
url = reverse(f"admin:{self.app_label}_upgradeoperation_change", args=[uo.pk])
870+
response = self.client.get(url)
871+
self.assertEqual(response.status_code, 200)
872+
batch_changelist_url = reverse(
873+
f"admin:{self.app_label}_batchupgradeoperation_changelist"
874+
)
875+
batch_change_url = reverse(
876+
f"admin:{self.app_label}_batchupgradeoperation_change", args=[uo.batch.pk]
877+
)
878+
self.assertTrue(response.context["batch_has_view_permission"])
879+
self.assertEqual(response.context["batch"], uo.batch)
880+
self.assertContains(response, batch_changelist_url)
881+
self.assertContains(response, batch_change_url)
882+
self.assertContains(response, str(uo.batch))
883+
generic_upgrade_changelist_url = reverse(
884+
f"admin:{self.app_label}_upgradeoperation_changelist"
885+
)
886+
self.assertNotContains(response, f'href="{generic_upgrade_changelist_url}"')
887+
888+
def test_upgrade_operation_change_breadcrumb_without_batch(self, *args):
889+
self._login()
890+
device_fw = self._create_device_firmware()
891+
device_fw.save(upgrade=True)
892+
uo = device_fw.device.upgradeoperation_set.first()
893+
self.assertIsNone(uo.batch_id)
894+
url = reverse(f"admin:{self.app_label}_upgradeoperation_change", args=[uo.pk])
895+
response = self.client.get(url)
896+
self.assertEqual(response.status_code, 200)
897+
self.assertIsNone(response.context.get("batch"))
898+
generic_upgrade_changelist_url = reverse(
899+
f"admin:{self.app_label}_upgradeoperation_changelist"
900+
)
901+
self.assertContains(response, f'href="{generic_upgrade_changelist_url}"')
902+
903+
def test_upgrade_operation_change_breadcrumb_with_batch_no_permission(self, *args):
904+
self.test_upgrade_all()
905+
uo = UpgradeOperation.objects.first()
906+
url = reverse(f"admin:{self.app_label}_upgradeoperation_change", args=[uo.pk])
907+
with mock.patch(
908+
"openwisp_firmware_upgrader.admin.BatchUpgradeOperationAdmin.has_view_permission",
909+
return_value=False,
910+
):
911+
response = self.client.get(url)
912+
self.assertEqual(response.status_code, 200)
913+
batch_changelist_url = reverse(
914+
f"admin:{self.app_label}_batchupgradeoperation_changelist"
915+
)
916+
batch_change_url = reverse(
917+
f"admin:{self.app_label}_batchupgradeoperation_change", args=[uo.batch.pk]
918+
)
919+
self.assertFalse(response.context["batch_has_view_permission"])
920+
self.assertEqual(response.context["batch"], uo.batch)
921+
breadcrumbs = (
922+
response.content.decode()
923+
.split('<div class="breadcrumbs">', 1)[1]
924+
.split("</div>", 1)[0]
925+
)
926+
self.assertNotIn(f'href="{batch_changelist_url}"', breadcrumbs)
927+
self.assertNotIn(f'href="{batch_change_url}"', breadcrumbs)
928+
generic_upgrade_changelist_url = reverse(
929+
f"admin:{self.app_label}_upgradeoperation_changelist"
930+
)
931+
self.assertNotIn(f'href="{generic_upgrade_changelist_url}"', breadcrumbs)
932+
self.assertIn(str(uo.batch), breadcrumbs)
933+
866934
def test_recent_upgrades(self, *args):
867935
self._login()
868936
env = self._create_upgrade_env()

openwisp_firmware_upgrader/tests/test_models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from celery.exceptions import Retry
99
from django.core.exceptions import ValidationError
1010
from django.test import TestCase, TransactionTestCase
11+
from django.utils import timezone
1112

1213
from openwisp_utils.tests import capture_any_output
1314

@@ -536,6 +537,21 @@ def test_remove_file_root_directory(self, mock_storage, mock_logger):
536537
"Deleted firmware file: %s", "firmware.bin"
537538
)
538539

540+
def test_batch_upgrade_operation_str(self):
541+
build = self._create_build()
542+
batch = BatchUpgradeOperation.objects.create(build=build)
543+
expected = f"{build} ({timezone.localtime(batch.created).strftime('%Y-%m-%d %H:%M:%S')})"
544+
self.assertEqual(str(batch), expected)
545+
546+
def test_upgrade_operation_str(self):
547+
with mock.patch(
548+
f"{self.app_label}.models.UpgradeOperation.upgrade", return_value=None
549+
):
550+
self._create_device_firmware(upgrade=True)
551+
uo = UpgradeOperation.objects.first()
552+
expected = f"{uo.device} ({timezone.localtime(uo.created).strftime('%Y-%m-%d %H:%M:%S')})"
553+
self.assertEqual(str(uo), expected)
554+
539555

540556
class TestModelsTransaction(TestUpgraderMixin, TransactionTestCase):
541557
_mock_updrade = "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade"

openwisp_firmware_upgrader/tests/test_selenium.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,10 @@ def test_restoring_deleted_device(self, *args):
114114
By.XPATH, '//*[@id="device_form"]/div/div[1]/input[1]'
115115
).click()
116116
try:
117-
WebDriverWait(self.web_driver, 5).until(
118-
EC.url_to_be(f"{self.live_server_url}/admin/config/device/")
117+
device_changelist_url = self.live_server_url + reverse(
118+
f"admin:{self.config_app_label}_device_changelist"
119119
)
120+
WebDriverWait(self.web_driver, 5).until(EC.url_to_be(device_changelist_url))
120121
except TimeoutException:
121122
self.fail("Deleted device was not restored")
122123

0 commit comments

Comments
 (0)