Bladeren bron

Cleanup and documentation for #1344

jeremystretch 4 jaren geleden
bovenliggende
commit
487d67768b

+ 5 - 0
docs/core-functionality/contacts.md

@@ -0,0 +1,5 @@
+# Contacts
+
+{!models/tenancy/contact.md!}
+{!models/tenancy/contactgroup.md!}
+{!models/tenancy/contactrole.md!}

+ 31 - 0
docs/models/tenancy/contact.md

@@ -0,0 +1,31 @@
+# Contacts
+
+A contact represent an individual or group that has been associated with an object in NetBox for administrative reasons. For example, you might assign one or more operational contacts to each site. Contacts can be arranged within nested contact groups.
+
+Each contact must include a name, which is unique to its parent group (if any). The following optional descriptors are also available:
+
+* Title
+* Phone
+* Email
+* Address
+
+## Contact Assignment
+
+Each contact can be assigned to one or more objects, allowing for the efficient reuse of contact information. When assigning a contact to an object, the user may optionally specify a role and/or priority (primary, secondary, tertiary, or inactive) to better convey the nature of the contact's relationship to the assigned object.
+
+The following models support the assignment of contacts:
+
+* circuits.Circuit
+* circuits.Provider
+* dcim.Device
+* dcim.Location
+* dcim.Manufacturer
+* dcim.PowerPanel
+* dcim.Rack
+* dcim.Region
+* dcim.Site
+* dcim.SiteGroup
+* tenancy.Tenant
+* virtualization.Cluster
+* virtualization.ClusterGroup
+* virtualization.VirtualMachine

+ 3 - 0
docs/models/tenancy/contactgroup.md

@@ -0,0 +1,3 @@
+# Contact Groups
+
+Contacts can be organized into arbitrary groups. These groups can be recursively nested for convenience. Each contact within a group must have a unique name, but other attributes can be repeated.

+ 3 - 0
docs/models/tenancy/contactrole.md

@@ -0,0 +1,3 @@
+# Contact Roles
+
+Contacts can be organized by functional roles, which are fully customizable by the user. For example, you might create roles for administrative, operational, or emergency contacts.

+ 1 - 0
mkdocs.yml

@@ -62,6 +62,7 @@ nav:
         - Circuits: 'core-functionality/circuits.md'
         - Power Tracking: 'core-functionality/power.md'
         - Tenancy: 'core-functionality/tenancy.md'
+        - Contacts: 'core-functionality/contacts.md'
     - Customization:
         - Custom Fields: 'customization/custom-fields.md'
         - Custom Validation: 'customization/custom-validation.md'

+ 5 - 2
netbox/tenancy/api/serializers.py

@@ -1,8 +1,9 @@
 from django.contrib.auth.models import ContentType
 from rest_framework import serializers
 
-from netbox.api import ContentTypeField
+from netbox.api import ChoiceField, ContentTypeField
 from netbox.api.serializers import NestedGroupModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer
+from tenancy.choices import ContactPriorityChoices
 from tenancy.models import *
 from .nested_serializers import *
 
@@ -93,9 +94,11 @@ class ContactAssignmentSerializer(PrimaryModelSerializer):
     )
     contact = NestedContactSerializer()
     role = NestedContactRoleSerializer(required=False, allow_null=True)
+    priority = ChoiceField(choices=ContactPriorityChoices, required=False)
 
     class Meta:
         model = ContactAssignment
         fields = [
-            'id', 'url', 'display', 'content_type', 'object_id', 'contact', 'role', 'created', 'last_updated',
+            'id', 'url', 'display', 'content_type', 'object_id', 'contact', 'role', 'priority', 'created',
+            'last_updated',
         ]

+ 5 - 4
netbox/tenancy/filtersets.py

@@ -2,8 +2,8 @@ import django_filters
 from django.db.models import Q
 
 from extras.filters import TagFilter
-from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
-from utilities.filters import TreeNodeMultipleChoiceFilter
+from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
+from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
 from .models import *
 
 
@@ -168,7 +168,8 @@ class ContactFilterSet(PrimaryModelFilterSet):
         )
 
 
-class ContactAssignmentFilterSet(OrganizationalModelFilterSet):
+class ContactAssignmentFilterSet(ChangeLoggedModelFilterSet):
+    content_type = ContentTypeFilter()
     contact_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Contact.objects.all(),
         label='Contact (ID)',
@@ -186,4 +187,4 @@ class ContactAssignmentFilterSet(OrganizationalModelFilterSet):
 
     class Meta:
         model = ContactAssignment
-        fields = ['id', 'priority']
+        fields = ['id', 'content_type_id', 'priority']

+ 15 - 0
netbox/tenancy/forms/bulk_edit.py

@@ -96,6 +96,21 @@ class ContactBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBul
         queryset=ContactGroup.objects.all(),
         required=False
     )
+    title = forms.CharField(
+        max_length=100,
+        required=False
+    )
+    phone = forms.CharField(
+        max_length=50,
+        required=False
+    )
+    email = forms.EmailField(
+        required=False
+    )
+    address = forms.CharField(
+        max_length=200,
+        required=False
+    )
 
     class Meta:
         nullable_fields = ['group', 'title', 'phone', 'email', 'address', 'comments']

+ 8 - 2
netbox/tenancy/forms/models.py

@@ -109,10 +109,16 @@ class ContactForm(BootstrapMixin, CustomFieldModelForm):
 class ContactAssignmentForm(BootstrapMixin, forms.ModelForm):
     group = DynamicModelChoiceField(
         queryset=ContactGroup.objects.all(),
-        required=False
+        required=False,
+        initial_params={
+            'contacts': '$contact'
+        }
     )
     contact = DynamicModelChoiceField(
-        queryset=Contact.objects.all()
+        queryset=Contact.objects.all(),
+        query_params={
+            'group_id': '$group'
+        }
     )
     role = DynamicModelChoiceField(
         queryset=ContactRole.objects.all()

+ 3 - 0
netbox/tenancy/models.py

@@ -259,3 +259,6 @@ class ContactAssignment(ChangeLoggedModel):
 
     class Meta:
         ordering = ('priority', 'contact')
+
+    def __str__(self):
+        return f"{self.contact} ({self.get_priority_display()})" if self.priority else self.name