#22205 - Add EOL to DeviceType, ModuleType
This commit is contained in:
parent
58e8068958
commit
3f077df77f
|
|
@ -61,6 +61,10 @@ The default direction in which airflow circulates within the device chassis. Thi
|
|||
|
||||
The numeric weight of the device, including a unit designation (e.g. 10 kilograms or 20 pounds).
|
||||
|
||||
### End of Life
|
||||
|
||||
The date after which this device type is no longer supported by its manufacturer. This can be used to identify devices approaching or past their support horizon to aid in hardware lifecycle planning.
|
||||
|
||||
### Front & Rear Images
|
||||
|
||||
Users can upload illustrations of the device's front and rear panels. If present, these will be used to render the device in [rack](./rack.md) elevation diagrams.
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ The numeric weight of the module, including a unit designation (e.g. 3 kilograms
|
|||
|
||||
The direction in which air circulates through the device chassis for cooling.
|
||||
|
||||
### End of Life
|
||||
|
||||
The date after which this module type is no longer supported by its manufacturer. This can be used to identify modules approaching or past their support horizon to aid in hardware lifecycle planning.
|
||||
|
||||
### Profile
|
||||
|
||||
The assigned [profile](./moduletypeprofile.md) for the type of module. Profiles can be used to classify module types by function (e.g. power supply, hard disk, etc.), and they support the addition of user-configurable attributes on module types. The assignment of a module type to a profile is optional.
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
|
|||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number',
|
||||
'u_height', 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
|
||||
'weight_unit', 'front_image', 'rear_image', 'description', 'owner', 'comments', 'tags', 'custom_fields',
|
||||
'weight_unit', 'end_of_life', 'front_image', 'rear_image', 'description', 'owner',
|
||||
'comments', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', 'device_count', 'console_port_template_count',
|
||||
'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count',
|
||||
'interface_template_count', 'front_port_template_count', 'rear_port_template_count',
|
||||
|
|
@ -115,7 +116,8 @@ class ModuleTypeSerializer(PrimaryModelSerializer):
|
|||
model = ModuleType
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'profile', 'manufacturer', 'model', 'part_number', 'airflow',
|
||||
'weight', 'weight_unit', 'description', 'attributes', 'owner', 'comments', 'tags', 'custom_fields',
|
||||
'weight', 'weight_unit', 'end_of_life', 'description', 'attributes', 'owner', 'comments',
|
||||
'tags', 'custom_fields',
|
||||
'created', 'last_updated', 'module_count', 'console_port_template_count',
|
||||
'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count',
|
||||
'interface_template_count', 'front_port_template_count', 'rear_port_template_count',
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ class DeviceTypeFilterSet(PrimaryModelFilterSet):
|
|||
model = DeviceType
|
||||
fields = (
|
||||
'id', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth',
|
||||
'subdevice_role', 'airflow', 'weight', 'weight_unit', 'description',
|
||||
'subdevice_role', 'airflow', 'weight', 'weight_unit', 'end_of_life', 'description',
|
||||
|
||||
# Counters
|
||||
'console_port_template_count',
|
||||
|
|
@ -870,7 +870,7 @@ class ModuleTypeFilterSet(AttributeFiltersMixin, PrimaryModelFilterSet):
|
|||
class Meta:
|
||||
model = ModuleType
|
||||
fields = (
|
||||
'id', 'model', 'part_number', 'airflow', 'weight', 'weight_unit', 'description',
|
||||
'id', 'model', 'part_number', 'airflow', 'weight', 'weight_unit', 'end_of_life', 'description',
|
||||
|
||||
# Counters
|
||||
'console_port_template_count',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from utilities.forms.fields import (
|
|||
PositiveBigIntegerField,
|
||||
)
|
||||
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
|
||||
from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
|
||||
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, NumberWithOptions
|
||||
from virtualization.models import Cluster
|
||||
from wireless.choices import WirelessRoleChoices
|
||||
from wireless.models import WirelessLAN, WirelessLANGroup
|
||||
|
|
@ -550,6 +550,11 @@ class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
|
|||
required=False,
|
||||
initial=''
|
||||
)
|
||||
end_of_life = forms.DateField(
|
||||
label=_('End of life'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
|
||||
model = DeviceType
|
||||
fieldsets = (
|
||||
|
|
@ -558,8 +563,9 @@ class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
|
|||
'airflow', 'description', name=_('Device Type')
|
||||
),
|
||||
FieldSet('weight', 'weight_unit', name=_('Weight')),
|
||||
FieldSet('end_of_life', name=_('Lifecycle')),
|
||||
)
|
||||
nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')
|
||||
nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'end_of_life', 'description', 'comments')
|
||||
|
||||
|
||||
class ModuleTypeProfileBulkEditForm(PrimaryModelBulkEditForm):
|
||||
|
|
@ -606,6 +612,11 @@ class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
|
|||
required=False,
|
||||
initial=''
|
||||
)
|
||||
end_of_life = forms.DateField(
|
||||
label=_('End of life'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
|
||||
model = ModuleType
|
||||
fieldsets = (
|
||||
|
|
@ -615,8 +626,9 @@ class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
|
|||
InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')),
|
||||
name=_('Chassis')
|
||||
),
|
||||
FieldSet('end_of_life', name=_('Lifecycle')),
|
||||
)
|
||||
nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'description', 'comments')
|
||||
nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'end_of_life', 'description', 'comments')
|
||||
|
||||
|
||||
class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm):
|
||||
|
|
|
|||
|
|
@ -457,8 +457,8 @@ class DeviceTypeImportForm(PrimaryModelImportForm):
|
|||
model = DeviceType
|
||||
fields = [
|
||||
'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization',
|
||||
'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'owner', 'comments',
|
||||
'tags',
|
||||
'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'end_of_life',
|
||||
'owner', 'comments', 'tags',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -509,8 +509,8 @@ class ModuleTypeImportForm(PrimaryModelImportForm):
|
|||
class Meta:
|
||||
model = ModuleType
|
||||
fields = [
|
||||
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'profile',
|
||||
'attribute_data', 'owner', 'comments', 'tags',
|
||||
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'end_of_life',
|
||||
'profile', 'attribute_data', 'owner', 'comments', 'tags',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from users.models import User
|
|||
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
|
||||
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, PositiveBigIntegerField, TagFilterField
|
||||
from utilities.forms.rendering import FieldSet
|
||||
from utilities.forms.widgets import NumberWithOptions
|
||||
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
||||
from virtualization.models import Cluster, ClusterGroup, VirtualMachine
|
||||
from vpn.models import L2VPN
|
||||
from wireless.choices import *
|
||||
|
|
@ -569,6 +569,7 @@ class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
|
|||
'pass_through_ports', 'device_bays', 'module_bays', 'inventory_items', name=_('Components')
|
||||
),
|
||||
FieldSet('weight', 'weight_unit', name=_('Weight')),
|
||||
FieldSet('end_of_life__gte', 'end_of_life__lte', name=_('Lifecycle')),
|
||||
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
|
||||
)
|
||||
selector_fields = ('filter_id', 'q', 'manufacturer_id')
|
||||
|
|
@ -688,6 +689,16 @@ class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
|
|||
choices=add_blank_choice(WeightUnitChoices),
|
||||
required=False
|
||||
)
|
||||
end_of_life__gte = forms.DateField(
|
||||
label=_('End of life after'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
end_of_life__lte = forms.DateField(
|
||||
label=_('End of life before'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
|
||||
|
||||
class ModuleTypeProfileFilterForm(PrimaryModelFilterSetForm):
|
||||
|
|
@ -713,6 +724,7 @@ class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
|
|||
'pass_through_ports', 'module_bays', name=_('Components')
|
||||
),
|
||||
FieldSet('weight', 'weight_unit', name=_('Weight')),
|
||||
FieldSet('end_of_life__gte', 'end_of_life__lte', name=_('Lifecycle')),
|
||||
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
|
||||
)
|
||||
selector_fields = ('filter_id', 'q', 'manufacturer_id')
|
||||
|
|
@ -800,6 +812,16 @@ class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
|
|||
choices=add_blank_choice(WeightUnitChoices),
|
||||
required=False
|
||||
)
|
||||
end_of_life__gte = forms.DateField(
|
||||
label=_('End of life after'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
end_of_life__lte = forms.DateField(
|
||||
label=_('End of life before'),
|
||||
required=False,
|
||||
widget=DatePicker()
|
||||
)
|
||||
|
||||
|
||||
class DeviceRoleFilterForm(NestedGroupModelFilterSetForm):
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from utilities.forms.widgets import (
|
|||
APISelect,
|
||||
ClearableFileInput,
|
||||
ClearableSelect,
|
||||
DatePicker,
|
||||
HTMXSelect,
|
||||
NumberWithOptions,
|
||||
SelectWithPK,
|
||||
|
|
@ -510,6 +511,7 @@ class DeviceTypeForm(PrimaryModelForm):
|
|||
'u_height', 'exclude_from_utilization', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow',
|
||||
'weight', 'weight_unit', name=_('Chassis')
|
||||
),
|
||||
FieldSet('end_of_life', name=_('Lifecycle')),
|
||||
FieldSet('front_image', 'rear_image', name=_('Images')),
|
||||
)
|
||||
|
||||
|
|
@ -517,8 +519,8 @@ class DeviceTypeForm(PrimaryModelForm):
|
|||
model = DeviceType
|
||||
fields = [
|
||||
'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization',
|
||||
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
|
||||
'description', 'owner', 'comments', 'tags',
|
||||
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'end_of_life', 'front_image',
|
||||
'rear_image', 'description', 'owner', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'front_image': ClearableFileInput(attrs={
|
||||
|
|
@ -527,6 +529,7 @@ class DeviceTypeForm(PrimaryModelForm):
|
|||
'rear_image': ClearableFileInput(attrs={
|
||||
'accept': DEVICETYPE_IMAGE_FORMATS
|
||||
}),
|
||||
'end_of_life': DatePicker(),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -575,6 +578,7 @@ class ModuleTypeForm(PrimaryModelForm):
|
|||
return [
|
||||
FieldSet('manufacturer', 'model', 'part_number', 'description', 'tags', name=_('Module Type')),
|
||||
FieldSet('airflow', 'weight', 'weight_unit', name=_('Hardware')),
|
||||
FieldSet('end_of_life', name=_('Lifecycle')),
|
||||
FieldSet('profile', *self.attr_fields, name=_('Profile & Attributes'), html_id='profile-attributes')
|
||||
]
|
||||
|
||||
|
|
@ -582,8 +586,11 @@ class ModuleTypeForm(PrimaryModelForm):
|
|||
model = ModuleType
|
||||
fields = [
|
||||
'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
|
||||
'owner', 'comments', 'tags',
|
||||
'end_of_life', 'owner', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'end_of_life': DatePicker(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ import strawberry
|
|||
import strawberry_django
|
||||
from django.db.models import Q
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, ComparisonFilterLookup, FilterLookup, StrFilterLookup
|
||||
from strawberry_django import (
|
||||
BaseFilterLookup,
|
||||
ComparisonFilterLookup,
|
||||
DateFilterLookup,
|
||||
FilterLookup,
|
||||
StrFilterLookup,
|
||||
)
|
||||
|
||||
from dcim import models
|
||||
from dcim.constants import *
|
||||
|
|
@ -391,6 +397,7 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod
|
|||
airflow: BaseFilterLookup[Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
end_of_life: DateFilterLookup | None = strawberry_django.filter_field()
|
||||
front_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
|
@ -781,6 +788,7 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod
|
|||
airflow: BaseFilterLookup[Annotated['ModuleAirflowEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
end_of_life: DateFilterLookup | None = strawberry_django.filter_field()
|
||||
consoleporttemplates: Annotated['ConsolePortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field(name='console_port_templates')
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("dcim", "0241_consolidate_unique_constraints"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="devicetype",
|
||||
name="end_of_life",
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="moduletype",
|
||||
name="end_of_life",
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -136,6 +136,12 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
blank=True,
|
||||
null=True
|
||||
)
|
||||
end_of_life = models.DateField(
|
||||
verbose_name=_('end of life'),
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_('The date after which this device type is no longer supported by the manufacturer')
|
||||
)
|
||||
front_image = models.ImageField(
|
||||
upload_to='devicetype-images',
|
||||
blank=True
|
||||
|
|
@ -193,7 +199,7 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
|
||||
clone_fields = (
|
||||
'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
|
||||
'weight_unit',
|
||||
'weight_unit', 'end_of_life',
|
||||
)
|
||||
prerequisite_models = (
|
||||
'dcim.Manufacturer',
|
||||
|
|
@ -245,6 +251,7 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
'airflow': self.airflow,
|
||||
'weight': float(self.weight) if self.weight is not None else None,
|
||||
'weight_unit': self.weight_unit,
|
||||
'end_of_life': self.end_of_life.isoformat() if self.end_of_life else None,
|
||||
'comments': self.comments,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
blank=True,
|
||||
null=True
|
||||
)
|
||||
end_of_life = models.DateField(
|
||||
verbose_name=_('end of life'),
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_('The date after which this module type is no longer supported by the manufacturer')
|
||||
)
|
||||
attribute_data = models.JSONField(
|
||||
blank=True,
|
||||
null=True,
|
||||
|
|
@ -135,7 +141,7 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
to_field='module_type'
|
||||
)
|
||||
|
||||
clone_fields = ('profile', 'manufacturer', 'weight', 'weight_unit', 'airflow')
|
||||
clone_fields = ('profile', 'manufacturer', 'weight', 'weight_unit', 'airflow', 'end_of_life')
|
||||
prerequisite_models = (
|
||||
'dcim.Manufacturer',
|
||||
)
|
||||
|
|
@ -199,6 +205,7 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
|
|||
'weight': float(self.weight) if self.weight is not None else None,
|
||||
'weight_unit': self.weight_unit,
|
||||
'airflow': self.airflow,
|
||||
'end_of_life': self.end_of_life.isoformat() if self.end_of_life else None,
|
||||
'attribute_data': self.attribute_data,
|
||||
'comments': self.comments,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class DeviceTypeTable(PrimaryModelTable):
|
|||
model = models.DeviceType
|
||||
fields = (
|
||||
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height',
|
||||
'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
|
||||
'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'end_of_life',
|
||||
'description', 'comments', 'device_count', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ class ModuleTypeTable(PrimaryModelTable):
|
|||
class Meta(PrimaryModelTable.Meta):
|
||||
model = ModuleType
|
||||
fields = (
|
||||
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description',
|
||||
'attributes', 'module_count', 'comments', 'tags', 'created', 'last_updated',
|
||||
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'end_of_life',
|
||||
'description', 'attributes', 'module_count', 'comments', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'model', 'profile', 'manufacturer', 'part_number', 'module_count',
|
||||
|
|
|
|||
|
|
@ -941,6 +941,7 @@ class DeviceTypeTestCase(APIViewTestCases.APIViewTestCase):
|
|||
brief_fields = ['description', 'device_count', 'display', 'id', 'manufacturer', 'model', 'slug', 'url']
|
||||
bulk_update_data = {
|
||||
'part_number': 'ABC123',
|
||||
'end_of_life': '2030-01-01',
|
||||
}
|
||||
user_permissions = ('dcim.view_manufacturer', )
|
||||
|
||||
|
|
@ -972,6 +973,7 @@ class DeviceTypeTestCase(APIViewTestCases.APIViewTestCase):
|
|||
'model': 'Device Type 5',
|
||||
'slug': 'device-type-5',
|
||||
'u_height': 0.5,
|
||||
'end_of_life': '2035-06-30',
|
||||
},
|
||||
{
|
||||
'manufacturer': manufacturers[1].pk,
|
||||
|
|
@ -987,6 +989,7 @@ class ModuleTypeTestCase(APIViewTestCases.APIViewTestCase):
|
|||
brief_fields = ['description', 'display', 'id', 'manufacturer', 'model', 'module_count', 'profile', 'url']
|
||||
bulk_update_data = {
|
||||
'part_number': 'ABC123',
|
||||
'end_of_life': '2030-01-01',
|
||||
}
|
||||
user_permissions = ('dcim.view_manufacturer', )
|
||||
|
||||
|
|
@ -1014,6 +1017,7 @@ class ModuleTypeTestCase(APIViewTestCases.APIViewTestCase):
|
|||
{
|
||||
'manufacturer': manufacturers[1].pk,
|
||||
'model': 'Module Type 5',
|
||||
'end_of_life': '2035-06-30',
|
||||
},
|
||||
{
|
||||
'manufacturer': manufacturers[1].pk,
|
||||
|
|
|
|||
|
|
@ -1376,6 +1376,7 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
rear_image='rear.png',
|
||||
weight=10,
|
||||
weight_unit=WeightUnitChoices.UNIT_POUND,
|
||||
end_of_life='2030-01-01',
|
||||
description='foobar1'
|
||||
),
|
||||
DeviceType(
|
||||
|
|
@ -1390,6 +1391,7 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
airflow=DeviceAirflowChoices.AIRFLOW_FRONT_TO_REAR,
|
||||
weight=20,
|
||||
weight_unit=WeightUnitChoices.UNIT_POUND,
|
||||
end_of_life='2035-06-30',
|
||||
description='foobar2'
|
||||
),
|
||||
DeviceType(
|
||||
|
|
@ -1471,6 +1473,14 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
params = {'part_number': ['Part Number 1', 'Part Number 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_end_of_life(self):
|
||||
params = {'end_of_life': ['2030-01-01', '2035-06-30']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'end_of_life__gte': ['2031-01-01']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
params = {'end_of_life__lte': ['2031-01-01']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_description(self):
|
||||
params = {'description': ['foobar1', 'foobar2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
|
@ -1630,6 +1640,7 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
weight_unit=WeightUnitChoices.UNIT_POUND,
|
||||
description='foobar1',
|
||||
airflow=ModuleAirflowChoices.FRONT_TO_REAR,
|
||||
end_of_life='2030-01-01',
|
||||
profile=module_type_profiles[0],
|
||||
attribute_data={
|
||||
'string': 'string1',
|
||||
|
|
@ -1646,6 +1657,7 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
weight_unit=WeightUnitChoices.UNIT_POUND,
|
||||
description='foobar2',
|
||||
airflow=ModuleAirflowChoices.REAR_TO_FRONT,
|
||||
end_of_life='2035-06-30',
|
||||
profile=module_type_profiles[1],
|
||||
attribute_data={
|
||||
'string': 'string2',
|
||||
|
|
@ -1728,6 +1740,14 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||
params = {'description': ['foobar1', 'foobar2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_end_of_life(self):
|
||||
params = {'end_of_life': ['2030-01-01', '2035-06-30']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'end_of_life__gte': ['2031-01-01']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
params = {'end_of_life__lte': ['2031-01-01']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_manufacturer(self):
|
||||
manufacturers = Manufacturer.objects.all()[:2]
|
||||
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import csv
|
||||
import datetime
|
||||
import json
|
||||
from decimal import Decimal
|
||||
from io import StringIO
|
||||
|
|
@ -719,6 +720,7 @@ class DeviceTypeTestCase(
|
|||
'u_height': 2,
|
||||
'is_full_depth': True,
|
||||
'subdevice_role': None,
|
||||
'end_of_life': datetime.date(2035, 6, 30),
|
||||
'comments': 'Some comments',
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
|
@ -728,6 +730,7 @@ class DeviceTypeTestCase(
|
|||
'default_platform': platforms[1].pk,
|
||||
'u_height': 3,
|
||||
'is_full_depth': False,
|
||||
'end_of_life': datetime.date(2030, 1, 1),
|
||||
}
|
||||
|
||||
def test_devicetype_consoleports(self):
|
||||
|
|
@ -1269,6 +1272,7 @@ class ModuleTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||
'manufacturer': manufacturers[1].pk,
|
||||
'model': 'Device Type X',
|
||||
'part_number': '123ABC',
|
||||
'end_of_life': datetime.date(2035, 6, 30),
|
||||
'comments': 'Some comments',
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
|
@ -1276,11 +1280,12 @@ class ModuleTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||
cls.bulk_edit_data = {
|
||||
'manufacturer': manufacturers[1].pk,
|
||||
'part_number': '456DEF',
|
||||
'end_of_life': datetime.date(2030, 1, 1),
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"manufacturer,model,part_number,comments,profile",
|
||||
f"Manufacturer 1,fan0,generic-fan,,{fan_module_type_profile.name}"
|
||||
"manufacturer,model,part_number,end_of_life,comments,profile",
|
||||
f"Manufacturer 1,fan0,generic-fan,2035-06-30,,{fan_module_type_profile.name}"
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ class DeviceTypePanel(panels.ObjectAttributesPanel):
|
|||
weight = attrs.WeightAttr('weight')
|
||||
subdevice_role = attrs.ChoiceAttr('subdevice_role', label=_('Parent/child'))
|
||||
airflow = attrs.ChoiceAttr('airflow')
|
||||
end_of_life = attrs.DateTimeAttr('end_of_life', spec='date')
|
||||
front_image = attrs.ImageAttr('front_image')
|
||||
rear_image = attrs.ImageAttr('rear_image')
|
||||
|
||||
|
|
@ -185,6 +186,7 @@ class ModuleTypePanel(panels.ObjectAttributesPanel):
|
|||
description = attrs.TextAttr('description')
|
||||
airflow = attrs.ChoiceAttr('airflow')
|
||||
weight = attrs.WeightAttr('weight')
|
||||
end_of_life = attrs.DateTimeAttr('end_of_life', spec='date')
|
||||
|
||||
|
||||
class PlatformPanel(panels.NestedGroupObjectPanel):
|
||||
|
|
|
|||
Loading…
Reference in New Issue