Render SoftwareProduct manufacturer consistently (#59)

Closes #58

Signed-off-by: wkoot <3715211+wkoot@users.noreply.github.com>
This commit is contained in:
wkoot
2025-02-17 23:01:51 +01:00
committed by GitHub
parent 306d853a16
commit 410b8f6617
12 changed files with 78 additions and 53 deletions
+5 -4
View File
@@ -49,6 +49,7 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
"display",
"url",
"name",
"description",
"manufacturer",
"description",
"tags",
@@ -60,8 +61,7 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
brief_fields = ("id", "display", "url", "name", "description")
def get_display(self, obj):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{obj.manufacturer} - {obj}"
return f"{obj}"
class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
@@ -87,7 +87,7 @@ class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
"created",
"last_updated",
)
brief_fields = ("id", "display", "url", "name")
brief_fields = ("id", "display", "url")
def get_display(self, obj):
return f"{obj}"
@@ -104,6 +104,7 @@ class SoftwareProductVersionSerializer(NetBoxModelSerializer):
"display",
"url",
"name",
"description",
"release_date",
"documentation_url",
"end_of_support",
@@ -118,7 +119,7 @@ class SoftwareProductVersionSerializer(NetBoxModelSerializer):
"created",
"last_updated",
)
brief_fields = ("id", "display", "url", "name")
brief_fields = ("id", "display", "url", "name", "description")
def get_display(self, obj):
return f"{obj}"
+3
View File
@@ -42,6 +42,7 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProductVersion instances."""
name = CharFilter(lookup_expr="icontains")
description = CharFilter(lookup_expr="icontains")
filename = CharFilter(lookup_expr="icontains")
release_type = MultipleChoiceFilter(choices=SoftwareReleaseTypes.choices)
@@ -66,6 +67,7 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
return queryset
qs_filter = (
Q(name__icontains=value)
| Q(description__icontains=value)
| Q(software_product__name__icontains=value)
| Q(software_product__manufacturer__name__icontains=value)
| Q(comments__icontains=value)
@@ -130,6 +132,7 @@ class SoftwareLicenseFilterSet(NetBoxModelFilterSet):
return queryset
qs_filter = (
Q(name__icontains=value)
| Q(description__icontains=value)
| Q(software_product__name__icontains=value)
| Q(version__name__icontains=value)
| Q(installation__device__name__icontains=value)
+13 -4
View File
@@ -74,7 +74,14 @@ class SoftwareLicenseFilterForm(NetBoxModelFilterSetForm):
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet(
"name", "description", "type", "stored_location", "support", "software_product", "version", "installation"
"name",
"description",
"type",
"stored_location",
"support",
"software_product_id",
"version_id",
"installation_id",
),
)
selector_fields = ("q", "filter_id", "name")
@@ -87,18 +94,20 @@ class SoftwareLicenseFilterForm(NetBoxModelFilterSetForm):
stored_location = CharField(required=False)
support = ChoiceField(required=False, choices=BOOLEAN_WITH_BLANK_CHOICES)
software_product = DynamicModelMultipleChoiceField(
software_product_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
)
version = DynamicModelMultipleChoiceField(
version_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProductVersion.objects.all(),
required=False,
label="Version",
)
installation = DynamicModelMultipleChoiceField(
installation_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProductInstallation.objects.all(),
required=False,
label="Installation",
)
+3 -2
View File
@@ -37,7 +37,7 @@ class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProduct
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("name", "description", "manufacturer"),
FieldSet("name", "description", "manufacturer_id"),
)
selector_fields = ("q", "filter_id", "name")
@@ -45,9 +45,10 @@ class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
name = CharField(required=False)
description = CharField(required=False)
manufacturer = DynamicModelMultipleChoiceField(
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label="Manufacturer",
)
@@ -69,33 +69,36 @@ class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductInstallation
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("device", "virtualmachine", "cluster", "software_product", "version"),
FieldSet("device_id", "virtualmachine_id", "cluster_id", "software_product_id", "version_id"),
)
selector_fields = ("filter_id", "q")
tag = TagFilterField(model)
device = DynamicModelMultipleChoiceField(
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
label="Device",
)
virtualmachine = DynamicModelMultipleChoiceField(
virtualmachine_id = DynamicModelMultipleChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
label="Virtual Machine",
)
cluster = DynamicModelMultipleChoiceField(
cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
label="Cluster",
)
software_product = DynamicModelMultipleChoiceField(
software_product_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
)
version = DynamicModelMultipleChoiceField(
version_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProductVersion.objects.all(),
required=False,
label="Version",
)
+7 -3
View File
@@ -33,6 +33,7 @@ class SoftwareProductVersionForm(NetBoxModelForm):
model = SoftwareProductVersion
fields = (
"name",
"description",
"software_product",
"release_date",
"documentation_url",
@@ -50,22 +51,24 @@ class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductVersion
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("name", "filename", "release_type", "manufacturer", "software_product"),
FieldSet("name", "description", "filename", "release_type", "manufacturer_id", "software_product_id"),
)
selector_fields = ("q", "filter_id", "name")
tag = TagFilterField(model)
name = CharField(required=False)
description = CharField(required=False)
filename = CharField(required=False)
release_type = MultipleChoiceField(required=False, choices=SoftwareReleaseTypes.choices)
manufacturer = DynamicModelMultipleChoiceField(
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
label="Manufacturer",
)
software_product = DynamicModelMultipleChoiceField(
software_product_id = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
@@ -77,6 +80,7 @@ class SoftwareProductVersionBulkImportForm(NetBoxModelImportForm):
model = SoftwareProductVersion
fields = (
"name",
"description",
"software_product",
"release_date",
"end_of_support",
@@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-02-17 21:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("netbox_slm", "0008_software_release_types_and_more"),
]
operations = [
migrations.AddField(
model_name="softwareproductversion",
name="description",
field=models.CharField(blank=True, max_length=255, null=True),
),
]
+3 -2
View File
@@ -33,7 +33,7 @@ class SoftwareProduct(NetBoxModel):
def get_installation_count(self):
count = SoftwareProductInstallation.objects.filter(software_product_id=self.pk).count()
query_string = urlencode(dict(software_product=self.pk))
query_string = urlencode(dict(software_product_id=self.pk))
search_target = reverse("plugins:netbox_slm:softwareproductinstallation_list")
# Can be composed directly with reverse(query=) in Django 5.2, see https://code.djangoproject.com/ticket/25582
return format_html(f"<a href='{search_target}?{query_string}'>{count}</a>") if count else "0"
@@ -50,6 +50,7 @@ class SoftwareProductVersion(NetBoxModel):
name = models.CharField(max_length=64)
comments = models.TextField(blank=True)
description = models.CharField(max_length=255, null=True, blank=True)
release_date = models.DateField(null=True, blank=True)
documentation_url = LaxURLField(max_length=1024, null=True, blank=True)
end_of_support = models.DateField(null=True, blank=True)
@@ -78,7 +79,7 @@ class SoftwareProductVersion(NetBoxModel):
def get_installation_count(self):
count = SoftwareProductInstallation.objects.filter(version_id=self.pk).count()
query_string = urlencode(dict(version=self.pk))
query_string = urlencode(dict(version_id=self.pk))
search_target = reverse("plugins:netbox_slm:softwareproductinstallation_list")
# Can be composed directly with reverse(query=) in Django 5.2, see https://code.djangoproject.com/ticket/25582
return format_html(f"<a href='{search_target}?{query_string}'>{count}</a>") if count else "0"
+10 -30
View File
@@ -20,8 +20,8 @@ class SoftwareProductTable(NetBoxTable):
fields = (
"pk",
"name",
"manufacturer",
"description",
"manufacturer",
"installations",
"tags",
)
@@ -29,16 +29,9 @@ class SoftwareProductTable(NetBoxTable):
"pk",
"name",
"manufacturer",
"description",
"installations",
"tags",
)
sequence = (
"manufacturer",
"name",
"description",
"installations",
)
def order_installations(self, queryset, is_descending):
queryset = queryset.annotate(count=Count("softwareproductinstallation__id")).order_by(
@@ -63,6 +56,7 @@ class SoftwareProductVersionTable(NetBoxTable):
fields = (
"pk",
"name",
"description",
"software_product",
"manufacturer",
"release_date",
@@ -74,18 +68,12 @@ class SoftwareProductVersionTable(NetBoxTable):
default_columns = (
"pk",
"name",
"software_product",
"manufacturer",
"software_product",
"release_date",
"installations",
"tags",
)
sequence = (
"manufacturer",
"software_product",
"name",
"installations",
)
def order_installations(self, queryset, is_descending):
queryset = queryset.annotate(count=Count("softwareproductinstallation__id")).order_by(
@@ -98,13 +86,9 @@ class SoftwareProductInstallationTable(NetBoxTable):
"""Table for displaying SoftwareProductInstallation objects."""
pk = ToggleColumn()
name = tables.LinkColumn()
device = tables.Column(accessor="device", linkify=True)
virtualmachine = tables.Column(accessor="virtualmachine", linkify=True)
cluster = tables.Column(accessor="cluster", linkify=True)
platform = tables.Column(accessor="platform", linkify=True)
type = tables.Column(accessor="render_type")
manufacturer = tables.Column(accessor="software_product__manufacturer", linkify=True)
software_product = tables.Column(accessor="software_product", linkify=True)
version = tables.Column(accessor="version", linkify=True)
@@ -114,9 +98,9 @@ class SoftwareProductInstallationTable(NetBoxTable):
model = SoftwareProductInstallation
fields = (
"pk",
"name",
"platform",
"type",
"manufacturer",
"software_product",
"version",
"tags",
@@ -125,6 +109,7 @@ class SoftwareProductInstallationTable(NetBoxTable):
"pk",
"platform",
"type",
"manufacturer",
"software_product",
"version",
"tags",
@@ -144,10 +129,6 @@ class SoftwareProductInstallationTable(NetBoxTable):
queryset_union = device_annotate.union(vm_annotate).union(cluster_annotate)
return queryset_union.order_by(f"{'-' if is_descending else ''}render_type"), True
def render_software_product(self, value, **kwargs):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{kwargs['record'].software_product.manufacturer.name} - {value}"
class SoftwareLicenseTable(NetBoxTable):
"""Table for displaying SoftwareLicense objects."""
@@ -158,6 +139,7 @@ class SoftwareLicenseTable(NetBoxTable):
type = tables.Column()
stored_location = tables.Column(accessor="stored_location_txt", linkify=lambda record: record.stored_location_url)
manufacturer = tables.Column(accessor="software_product__manufacturer", linkify=True)
software_product = tables.Column(accessor="software_product", linkify=True)
version = tables.Column(accessor="version", linkify=True)
installation = tables.Column(accessor="installation", linkify=True)
@@ -174,6 +156,7 @@ class SoftwareLicenseTable(NetBoxTable):
"stored_location",
"start_date",
"expiration_date",
"manufacturer",
"software_product",
"version",
"installation",
@@ -184,15 +167,12 @@ class SoftwareLicenseTable(NetBoxTable):
default_columns = (
"pk",
"name",
"expiration_date",
"manufacturer",
"software_product",
"installation",
"expiration_date",
"tags",
)
def render_software_product(self, value, **kwargs):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{kwargs['record'].software_product.manufacturer.name} - {value}"
def render_installation(self, **kwargs):
return f"{kwargs['record'].installation.platform}"
@@ -16,6 +16,10 @@
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Description</th>
<td>{{ object.description }}</td>
</tr>
<tr>
<th scope="row">Manufacturer</th>
<td>{{ object.software_product.manufacturer|linkify }}</td>
+2 -2
View File
@@ -50,11 +50,11 @@ class ModelTestCase(TestCase):
def test_get_installation_count(self):
self.assertEqual(
f"<a href='/plugins/slm/installations/?software_product={self.software_product.pk}'>1</a>",
f"<a href='/plugins/slm/installations/?software_product_id={self.software_product.pk}'>1</a>",
self.software_product.get_installation_count(),
)
self.assertEqual(
f"<a href='/plugins/slm/installations/?version={self.software_product_version.pk}'>1</a>",
f"<a href='/plugins/slm/installations/?version_id={self.software_product_version.pk}'>1</a>",
self.software_product_version.get_installation_count(),
)