diff --git a/src/meshapi/admin/models/install.py b/src/meshapi/admin/models/install.py index 8c7778bc..765ab2c7 100644 --- a/src/meshapi/admin/models/install.py +++ b/src/meshapi/admin/models/install.py @@ -12,7 +12,7 @@ from simple_history.admin import SimpleHistoryAdmin from meshapi.models import Install -from meshapi.widgets import ExternalHyperlinkWidget +from meshapi.widgets import ExternalHyperlinkWidget, InstallStatusWidget from ..ranked_search import RankedSearchMixin @@ -40,8 +40,13 @@ class Meta: lambda ticket_number: f"{OSTICKET_URL}/scp/tickets.php?number={ticket_number}", title="View in OSTicket", ), + "status": InstallStatusWidget(), } + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.fields["status"].widget.form_instance = self + @admin.register(Install) class InstallAdmin(RankedSearchMixin, ImportExportMixin, ExportActionMixin, SimpleHistoryAdmin): diff --git a/src/meshapi/templates/widgets/install_status.html b/src/meshapi/templates/widgets/install_status.html new file mode 100644 index 00000000..09c42afe --- /dev/null +++ b/src/meshapi/templates/widgets/install_status.html @@ -0,0 +1,15 @@ +{% load i18n static %} + diff --git a/src/meshapi/tests/test_admin_change_view.py b/src/meshapi/tests/test_admin_change_view.py index 3beb836a..ba8d19bb 100644 --- a/src/meshapi/tests/test_admin_change_view.py +++ b/src/meshapi/tests/test_admin_change_view.py @@ -642,6 +642,25 @@ def test_change_building_address(self): self.assertEqual(building.primary_node, self.node1) self.assertEqual(list(building.nodes.all()), [self.node1]) + def test_install_hyperlinks_to_nn_assigned_node(self): + old_install = Install(**sample_install) + old_install.install_number = 741 + old_install.member = self.member + old_install.building = self.building_1 + old_install.status = Install.InstallStatus.NN_REASSIGNED + old_install.save() + + new_node = Node(**sample_node) + new_node.network_number = old_install.install_number + new_node.save() + + change_url = f"/admin/meshapi/install/{old_install.id}/change/" + response = self._call(change_url, 200) + form_soup = bs4.BeautifulSoup(response.content.decode()).find(id="install_form") + link_tag = form_soup.find("select", {"id": "id_status"}).parent.find("a") + self.assertEqual(link_tag.attrs["href"], f"/admin/meshapi/node/{new_node.id}") + self.assertEqual(link_tag.attrs["title"], "Show the node that got this install's number") + class TestAdminAddView(TestCase): def setUp(self): diff --git a/src/meshapi/widgets.py b/src/meshapi/widgets.py index 4d4bd54e..8fdde2a7 100644 --- a/src/meshapi/widgets.py +++ b/src/meshapi/widgets.py @@ -9,6 +9,8 @@ from django_jsonform.widgets import JSONFormWidget from flags.state import flag_enabled +from meshapi.models import Install, Node + class PanoramaViewer(JSONFormWidget): pano_template_name = "widgets/panorama_viewer.html" @@ -93,6 +95,22 @@ class UISPHyperlinkWidget(widgets.TextInput): template_name = "widgets/uisp_link.html" +class InstallStatusWidget(widgets.Select): + template_name = "widgets/install_status.html" + form_instance: "InstallAdminForm" + + def get_context(self, name: str, value: str, attrs: Optional[dict] = None) -> dict: + context = super().get_context(name, value, attrs) + if self.form_instance: + install = self.form_instance.instance + if install and install.status == Install.InstallStatus.NN_REASSIGNED: + recycled_as_node = Node.objects.filter(network_number=install.install_number).first() + if recycled_as_node: + context["reassigned_node_id"] = str(recycled_as_node.id) + + return context + + class AutoPopulateLocationWidget(forms.Widget): template_name = "widgets/auto_populate_location.html" @@ -111,3 +129,7 @@ def get_context(self, name: str, value: str, attrs: Optional[dict] = None) -> di context["auto_populate_source"] = self.source context["auto_populate_url"] = self.source return context + + +# Down here to resolve circular imports +from meshapi.admin.models.install import InstallAdminForm # noqa: E402