from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _

from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup
from tenancy.models import Tenant, TenantGroup
from utilities.forms import (
    add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
    CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField,
    JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES,
)
from virtualization.models import Cluster, ClusterGroup
from .choices import *
from .models import ConfigContext, CustomField, ImageAttachment, JournalEntry, ObjectChange, Tag
from .utils import FeatureQuery


#
# Custom fields
#

class CustomFieldForm(forms.Form):
    """
    Extend Form to include custom field support.
    """
    model = None

    def __init__(self, *args, **kwargs):
        if self.model is None:
            raise NotImplementedError("CustomFieldForm must specify a model class.")
        self.custom_fields = []

        super().__init__(*args, **kwargs)

        # Append relevant custom fields to the form instance
        obj_type = ContentType.objects.get_for_model(self.model)
        for cf in CustomField.objects.filter(content_types=obj_type):
            field_name = 'cf_{}'.format(cf.name)
            self.fields[field_name] = cf.to_form_field()

            # Annotate the field in the list of CustomField form fields
            self.custom_fields.append(field_name)


class CustomFieldModelForm(forms.ModelForm):
    """
    Extend ModelForm to include custom field support.
    """
    def __init__(self, *args, **kwargs):

        self.obj_type = ContentType.objects.get_for_model(self._meta.model)
        self.custom_fields = []

        super().__init__(*args, **kwargs)

        self._append_customfield_fields()

    def _append_customfield_fields(self):
        """
        Append form fields for all CustomFields assigned to this model.
        """
        # Append form fields; assign initial values if modifying and existing object
        for cf in CustomField.objects.filter(content_types=self.obj_type):
            field_name = 'cf_{}'.format(cf.name)
            if self.instance.pk:
                self.fields[field_name] = cf.to_form_field(set_initial=False)
                self.fields[field_name].initial = self.instance.custom_field_data.get(cf.name)
            else:
                self.fields[field_name] = cf.to_form_field()

            # Annotate the field in the list of CustomField form fields
            self.custom_fields.append(field_name)

    def clean(self):

        # Save custom field data on instance
        for cf_name in self.custom_fields:
            self.instance.custom_field_data[cf_name[3:]] = self.cleaned_data.get(cf_name)

        return super().clean()


class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm):

    def _append_customfield_fields(self):

        # Append form fields
        for cf in CustomField.objects.filter(content_types=self.obj_type):
            field_name = 'cf_{}'.format(cf.name)
            self.fields[field_name] = cf.to_form_field(for_csv_import=True)

            # Annotate the field in the list of CustomField form fields
            self.custom_fields.append(field_name)


class CustomFieldBulkEditForm(BulkEditForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.custom_fields = []
        self.obj_type = ContentType.objects.get_for_model(self.model)

        # Add all applicable CustomFields to the form
        custom_fields = CustomField.objects.filter(content_types=self.obj_type)
        for cf in custom_fields:
            # Annotate non-required custom fields as nullable
            if not cf.required:
                self.nullable_fields.append(cf.name)
            self.fields[cf.name] = cf.to_form_field(set_initial=False, enforce_required=False)
            # Annotate this as a custom field
            self.custom_fields.append(cf.name)


class CustomFieldFilterForm(forms.Form):

    def __init__(self, *args, **kwargs):

        self.obj_type = ContentType.objects.get_for_model(self.model)

        super().__init__(*args, **kwargs)

        # Add all applicable CustomFields to the form
        custom_fields = CustomField.objects.filter(content_types=self.obj_type).exclude(
            filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
        )
        for cf in custom_fields:
            field_name = 'cf_{}'.format(cf.name)
            self.fields[field_name] = cf.to_form_field(set_initial=True, enforce_required=False)


#
# Tags
#

class TagForm(BootstrapMixin, forms.ModelForm):
    slug = SlugField()

    class Meta:
        model = Tag
        fields = [
            'name', 'slug', 'color', 'description'
        ]
        fieldsets = (
            ('Tag', ('name', 'slug', 'color', 'description')),
        )


class TagCSVForm(CSVModelForm):
    slug = SlugField()

    class Meta:
        model = Tag
        fields = Tag.csv_headers
        help_texts = {
            'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
        }


class AddRemoveTagsForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Add add/remove tags fields
        self.fields['add_tags'] = DynamicModelMultipleChoiceField(
            queryset=Tag.objects.all(),
            required=False
        )
        self.fields['remove_tags'] = DynamicModelMultipleChoiceField(
            queryset=Tag.objects.all(),
            required=False
        )


class TagFilterForm(BootstrapMixin, forms.Form):
    model = Tag
    q = forms.CharField(
        required=False,
        label=_('Search')
    )
    content_type_id = ContentTypeMultipleChoiceField(
        queryset=ContentType.objects.filter(FeatureQuery('tags').get_query()),
        required=False,
        label=_('Tagged object type')
    )


class TagBulkEditForm(BootstrapMixin, BulkEditForm):
    pk = forms.ModelMultipleChoiceField(
        queryset=Tag.objects.all(),
        widget=forms.MultipleHiddenInput
    )
    color = forms.CharField(
        max_length=6,
        required=False,
        widget=ColorSelect()
    )
    description = forms.CharField(
        max_length=200,
        required=False
    )

    class Meta:
        nullable_fields = ['description']


#
# Config contexts
#

class ConfigContextForm(BootstrapMixin, forms.ModelForm):
    regions = DynamicModelMultipleChoiceField(
        queryset=Region.objects.all(),
        required=False
    )
    site_groups = DynamicModelMultipleChoiceField(
        queryset=SiteGroup.objects.all(),
        required=False
    )
    sites = DynamicModelMultipleChoiceField(
        queryset=Site.objects.all(),
        required=False
    )
    device_types = DynamicModelMultipleChoiceField(
        queryset=DeviceType.objects.all(),
        required=False
    )
    roles = DynamicModelMultipleChoiceField(
        queryset=DeviceRole.objects.all(),
        required=False
    )
    platforms = DynamicModelMultipleChoiceField(
        queryset=Platform.objects.all(),
        required=False
    )
    cluster_groups = DynamicModelMultipleChoiceField(
        queryset=ClusterGroup.objects.all(),
        required=False
    )
    clusters = DynamicModelMultipleChoiceField(
        queryset=Cluster.objects.all(),
        required=False
    )
    tenant_groups = DynamicModelMultipleChoiceField(
        queryset=TenantGroup.objects.all(),
        required=False
    )
    tenants = DynamicModelMultipleChoiceField(
        queryset=Tenant.objects.all(),
        required=False
    )
    tags = DynamicModelMultipleChoiceField(
        queryset=Tag.objects.all(),
        required=False
    )
    data = JSONField(
        label=''
    )

    class Meta:
        model = ConfigContext
        fields = (
            'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites', 'roles', 'device_types',
            'platforms', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
        )


class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
    pk = forms.ModelMultipleChoiceField(
        queryset=ConfigContext.objects.all(),
        widget=forms.MultipleHiddenInput
    )
    weight = forms.IntegerField(
        required=False,
        min_value=0
    )
    is_active = forms.NullBooleanField(
        required=False,
        widget=BulkEditNullBooleanSelect()
    )
    description = forms.CharField(
        required=False,
        max_length=100
    )

    class Meta:
        nullable_fields = [
            'description',
        ]


class ConfigContextFilterForm(BootstrapMixin, forms.Form):
    field_order = [
        'q', 'region_id', 'site_group_id', 'site_id', 'role_id', 'platform_id', 'cluster_group_id', 'cluster_id',
        'tenant_group_id', 'tenant_id',
    ]
    q = forms.CharField(
        required=False,
        label=_('Search')
    )
    region_id = DynamicModelMultipleChoiceField(
        queryset=Region.objects.all(),
        required=False,
        label=_('Regions')
    )
    site_group_id = DynamicModelMultipleChoiceField(
        queryset=SiteGroup.objects.all(),
        required=False,
        label=_('Site groups')
    )
    site_id = DynamicModelMultipleChoiceField(
        queryset=Site.objects.all(),
        required=False,
        label=_('Sites')
    )
    device_type_id = DynamicModelMultipleChoiceField(
        queryset=DeviceType.objects.all(),
        required=False,
        label=_('Device types')
    )
    role_id = DynamicModelMultipleChoiceField(
        queryset=DeviceRole.objects.all(),
        required=False,
        label=_('Roles')
    )
    platform_id = DynamicModelMultipleChoiceField(
        queryset=Platform.objects.all(),
        required=False,
        label=_('Platforms')
    )
    cluster_group_id = DynamicModelMultipleChoiceField(
        queryset=ClusterGroup.objects.all(),
        required=False,
        label=_('Cluster groups')
    )
    cluster_id = DynamicModelMultipleChoiceField(
        queryset=Cluster.objects.all(),
        required=False,
        label=_('Clusters')
    )
    tenant_group_id = DynamicModelMultipleChoiceField(
        queryset=TenantGroup.objects.all(),
        required=False,
        label=_('Tenant groups')
    )
    tenant_id = DynamicModelMultipleChoiceField(
        queryset=Tenant.objects.all(),
        required=False,
        label=_('Tenant')
    )
    tag = DynamicModelMultipleChoiceField(
        queryset=Tag.objects.all(),
        to_field_name='slug',
        required=False,
        label=_('Tags')
    )


#
# Filter form for local config context data
#

class LocalConfigContextFilterForm(forms.Form):
    local_context_data = forms.NullBooleanField(
        required=False,
        label=_('Has local config context data'),
        widget=StaticSelect2(
            choices=BOOLEAN_WITH_BLANK_CHOICES
        )
    )


#
# Image attachments
#

class ImageAttachmentForm(BootstrapMixin, forms.ModelForm):

    class Meta:
        model = ImageAttachment
        fields = [
            'name', 'image',
        ]


#
# Journal entries
#

class JournalEntryForm(BootstrapMixin, forms.ModelForm):
    comments = CommentField()

    class Meta:
        model = JournalEntry
        fields = ['assigned_object_type', 'assigned_object_id', 'kind', 'comments']
        widgets = {
            'assigned_object_type': forms.HiddenInput,
            'assigned_object_id': forms.HiddenInput,
        }


class JournalEntryBulkEditForm(BootstrapMixin, BulkEditForm):
    pk = forms.ModelMultipleChoiceField(
        queryset=JournalEntry.objects.all(),
        widget=forms.MultipleHiddenInput
    )
    kind = forms.ChoiceField(
        choices=JournalEntryKindChoices,
        required=False
    )
    comments = forms.CharField(
        required=False,
        widget=forms.Textarea()
    )

    class Meta:
        nullable_fields = []


class JournalEntryFilterForm(BootstrapMixin, forms.Form):
    model = JournalEntry
    q = forms.CharField(
        required=False,
        label=_('Search')
    )
    created_after = forms.DateTimeField(
        required=False,
        label=_('After'),
        widget=DateTimePicker()
    )
    created_before = forms.DateTimeField(
        required=False,
        label=_('Before'),
        widget=DateTimePicker()
    )
    created_by_id = DynamicModelMultipleChoiceField(
        queryset=User.objects.all(),
        required=False,
        label=_('User'),
        widget=APISelectMultiple(
            api_url='/api/users/users/',
        )
    )
    assigned_object_type_id = DynamicModelMultipleChoiceField(
        queryset=ContentType.objects.all(),
        required=False,
        label=_('Object Type'),
        widget=APISelectMultiple(
            api_url='/api/extras/content-types/',
        )
    )
    kind = forms.ChoiceField(
        choices=add_blank_choice(JournalEntryKindChoices),
        required=False,
        widget=StaticSelect2()
    )


#
# Change logging
#

class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
    model = ObjectChange
    q = forms.CharField(
        required=False,
        label=_('Search')
    )
    time_after = forms.DateTimeField(
        required=False,
        label=_('After'),
        widget=DateTimePicker()
    )
    time_before = forms.DateTimeField(
        required=False,
        label=_('Before'),
        widget=DateTimePicker()
    )
    action = forms.ChoiceField(
        choices=add_blank_choice(ObjectChangeActionChoices),
        required=False,
        widget=StaticSelect2()
    )
    user_id = DynamicModelMultipleChoiceField(
        queryset=User.objects.all(),
        required=False,
        label=_('User'),
        widget=APISelectMultiple(
            api_url='/api/users/users/',
        )
    )
    changed_object_type_id = DynamicModelMultipleChoiceField(
        queryset=ContentType.objects.all(),
        required=False,
        label=_('Object Type'),
        widget=APISelectMultiple(
            api_url='/api/extras/content-types/',
        )
    )


#
# Scripts
#

class ScriptForm(BootstrapMixin, forms.Form):
    _commit = forms.BooleanField(
        required=False,
        initial=True,
        label="Commit changes",
        help_text="Commit changes to the database (uncheck for a dry-run)"
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Move _commit to the end of the form
        commit = self.fields.pop('_commit')
        self.fields['_commit'] = commit

    @property
    def requires_input(self):
        """
        A boolean indicating whether the form requires user input (ignore the _commit field).
        """
        return bool(len(self.fields) > 1)

[((139, 11, 139, 22), 'utilities.forms.SlugField', 'SlugField', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((152, 11, 152, 22), 'utilities.forms.SlugField', 'SlugField', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((201, 18, 204, 5), 'django.forms.CharField', 'forms.CharField', (), '', False, 'from django import forms\n'), ((259, 11, 261, 5), 'utilities.forms.JSONField', 'JSONField', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((276, 13, 279, 5), 'django.forms.IntegerField', 'forms.IntegerField', (), '', False, 'from django import forms\n'), ((284, 18, 287, 5), 'django.forms.CharField', 'forms.CharField', (), '', False, 'from django import forms\n'), ((394, 15, 394, 29), 'utilities.forms.CommentField', 'CommentField', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((410, 11, 413, 5), 'django.forms.ChoiceField', 'forms.ChoiceField', (), '', False, 'from django import forms\n'), ((510, 14, 515, 5), 'django.forms.BooleanField', 'forms.BooleanField', (), '', False, 'from django import forms\n'), ((38, 19, 38, 64), 'django.contrib.contenttypes.models.ContentType.objects.get_for_model', 'ContentType.objects.get_for_model', ({(38, 53, 38, 63): 'self.model'}, {}), '(self.model)', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((53, 24, 53, 75), 'django.contrib.contenttypes.models.ContentType.objects.get_for_model', 'ContentType.objects.get_for_model', ({(53, 58, 53, 74): 'self._meta.model'}, {}), '(self._meta.model)', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((104, 24, 104, 69), 'django.contrib.contenttypes.models.ContentType.objects.get_for_model', 'ContentType.objects.get_for_model', ({(104, 58, 104, 68): 'self.model'}, {}), '(self.model)', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((121, 24, 121, 69), 'django.contrib.contenttypes.models.ContentType.objects.get_for_model', 'ContentType.objects.get_for_model', ({(121, 58, 121, 68): 'self.model'}, {}), '(self.model)', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((158, 21, 158, 85), 'django.utils.safestring.mark_safe', 'mark_safe', ({(158, 31, 158, 84): '"""RGB color in hexadecimal (e.g. <code>00ff00</code>)"""'}, {}), "('RGB color in hexadecimal (e.g. <code>00ff00</code>)')", False, 'from django.utils.safestring import mark_safe\n'), ((182, 14, 182, 25), 'django.utils.translation.gettext', '_', ({(182, 16, 182, 24): '"""Search"""'}, {}), "('Search')", True, 'from django.utils.translation import gettext as _\n'), ((187, 14, 187, 37), 'django.utils.translation.gettext', '_', ({(187, 16, 187, 36): '"""Tagged object type"""'}, {}), "('Tagged object type')", True, 'from django.utils.translation import gettext as _\n'), ((199, 15, 199, 28), 'utilities.forms.ColorSelect', 'ColorSelect', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((216, 17, 216, 37), 'dcim.models.Region.objects.all', 'Region.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((220, 17, 220, 40), 'dcim.models.SiteGroup.objects.all', 'SiteGroup.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((224, 17, 224, 35), 'dcim.models.Site.objects.all', 'Site.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((228, 17, 228, 41), 'dcim.models.DeviceType.objects.all', 'DeviceType.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((232, 17, 232, 41), 'dcim.models.DeviceRole.objects.all', 'DeviceRole.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((236, 17, 236, 39), 'dcim.models.Platform.objects.all', 'Platform.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((240, 17, 240, 43), 'virtualization.models.ClusterGroup.objects.all', 'ClusterGroup.objects.all', ({}, {}), '()', False, 'from virtualization.models import Cluster, ClusterGroup\n'), ((244, 17, 244, 38), 'virtualization.models.Cluster.objects.all', 'Cluster.objects.all', ({}, {}), '()', False, 'from virtualization.models import Cluster, ClusterGroup\n'), ((248, 17, 248, 42), 'tenancy.models.TenantGroup.objects.all', 'TenantGroup.objects.all', ({}, {}), '()', False, 'from tenancy.models import Tenant, TenantGroup\n'), ((252, 17, 252, 37), 'tenancy.models.Tenant.objects.all', 'Tenant.objects.all', ({}, {}), '()', False, 'from tenancy.models import Tenant, TenantGroup\n'), ((282, 15, 282, 42), 'utilities.forms.BulkEditNullBooleanSelect', 'BulkEditNullBooleanSelect', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((302, 14, 302, 25), 'django.utils.translation.gettext', '_', ({(302, 16, 302, 24): '"""Search"""'}, {}), "('Search')", True, 'from django.utils.translation import gettext as _\n'), ((305, 17, 305, 37), 'dcim.models.Region.objects.all', 'Region.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((307, 14, 307, 26), 'django.utils.translation.gettext', '_', ({(307, 16, 307, 25): '"""Regions"""'}, {}), "('Regions')", True, 'from django.utils.translation import gettext as _\n'), ((310, 17, 310, 40), 'dcim.models.SiteGroup.objects.all', 'SiteGroup.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((312, 14, 312, 30), 'django.utils.translation.gettext', '_', ({(312, 16, 312, 29): '"""Site groups"""'}, {}), "('Site groups')", True, 'from django.utils.translation import gettext as _\n'), ((315, 17, 315, 35), 'dcim.models.Site.objects.all', 'Site.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((317, 14, 317, 24), 'django.utils.translation.gettext', '_', ({(317, 16, 317, 23): '"""Sites"""'}, {}), "('Sites')", True, 'from django.utils.translation import gettext as _\n'), ((320, 17, 320, 41), 'dcim.models.DeviceType.objects.all', 'DeviceType.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((322, 14, 322, 31), 'django.utils.translation.gettext', '_', ({(322, 16, 322, 30): '"""Device types"""'}, {}), "('Device types')", True, 'from django.utils.translation import gettext as _\n'), ((325, 17, 325, 41), 'dcim.models.DeviceRole.objects.all', 'DeviceRole.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((327, 14, 327, 24), 'django.utils.translation.gettext', '_', ({(327, 16, 327, 23): '"""Roles"""'}, {}), "('Roles')", True, 'from django.utils.translation import gettext as _\n'), ((330, 17, 330, 39), 'dcim.models.Platform.objects.all', 'Platform.objects.all', ({}, {}), '()', False, 'from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup\n'), ((332, 14, 332, 28), 'django.utils.translation.gettext', '_', ({(332, 16, 332, 27): '"""Platforms"""'}, {}), "('Platforms')", True, 'from django.utils.translation import gettext as _\n'), ((335, 17, 335, 43), 'virtualization.models.ClusterGroup.objects.all', 'ClusterGroup.objects.all', ({}, {}), '()', False, 'from virtualization.models import Cluster, ClusterGroup\n'), ((337, 14, 337, 33), 'django.utils.translation.gettext', '_', ({(337, 16, 337, 32): '"""Cluster groups"""'}, {}), "('Cluster groups')", True, 'from django.utils.translation import gettext as _\n'), ((340, 17, 340, 38), 'virtualization.models.Cluster.objects.all', 'Cluster.objects.all', ({}, {}), '()', False, 'from virtualization.models import Cluster, ClusterGroup\n'), ((342, 14, 342, 27), 'django.utils.translation.gettext', '_', ({(342, 16, 342, 26): '"""Clusters"""'}, {}), "('Clusters')", True, 'from django.utils.translation import gettext as _\n'), ((345, 17, 345, 42), 'tenancy.models.TenantGroup.objects.all', 'TenantGroup.objects.all', ({}, {}), '()', False, 'from tenancy.models import Tenant, TenantGroup\n'), ((347, 14, 347, 32), 'django.utils.translation.gettext', '_', ({(347, 16, 347, 31): '"""Tenant groups"""'}, {}), "('Tenant groups')", True, 'from django.utils.translation import gettext as _\n'), ((350, 17, 350, 37), 'tenancy.models.Tenant.objects.all', 'Tenant.objects.all', ({}, {}), '()', False, 'from tenancy.models import Tenant, TenantGroup\n'), ((352, 14, 352, 25), 'django.utils.translation.gettext', '_', ({(352, 16, 352, 24): '"""Tenant"""'}, {}), "('Tenant')", True, 'from django.utils.translation import gettext as _\n'), ((358, 14, 358, 23), 'django.utils.translation.gettext', '_', ({(358, 16, 358, 22): '"""Tags"""'}, {}), "('Tags')", True, 'from django.utils.translation import gettext as _\n'), ((369, 14, 369, 48), 'django.utils.translation.gettext', '_', ({(369, 16, 369, 47): '"""Has local config context data"""'}, {}), "('Has local config context data')", True, 'from django.utils.translation import gettext as _\n'), ((370, 15, 372, 9), 'utilities.forms.StaticSelect2', 'StaticSelect2', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((416, 15, 416, 31), 'django.forms.Textarea', 'forms.Textarea', ({}, {}), '()', False, 'from django import forms\n'), ((427, 14, 427, 25), 'django.utils.translation.gettext', '_', ({(427, 16, 427, 24): '"""Search"""'}, {}), "('Search')", True, 'from django.utils.translation import gettext as _\n'), ((431, 14, 431, 24), 'django.utils.translation.gettext', '_', ({(431, 16, 431, 23): '"""After"""'}, {}), "('After')", True, 'from django.utils.translation import gettext as _\n'), ((432, 15, 432, 31), 'utilities.forms.DateTimePicker', 'DateTimePicker', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((436, 14, 436, 25), 'django.utils.translation.gettext', '_', ({(436, 16, 436, 24): '"""Before"""'}, {}), "('Before')", True, 'from django.utils.translation import gettext as _\n'), ((437, 15, 437, 31), 'utilities.forms.DateTimePicker', 'DateTimePicker', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((440, 17, 440, 35), 'django.contrib.auth.models.User.objects.all', 'User.objects.all', ({}, {}), '()', False, 'from django.contrib.auth.models import User\n'), ((442, 14, 442, 23), 'django.utils.translation.gettext', '_', ({(442, 16, 442, 22): '"""User"""'}, {}), "('User')", True, 'from django.utils.translation import gettext as _\n'), ((443, 15, 445, 9), 'utilities.forms.APISelectMultiple', 'APISelectMultiple', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((448, 17, 448, 42), 'django.contrib.contenttypes.models.ContentType.objects.all', 'ContentType.objects.all', ({}, {}), '()', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((450, 14, 450, 30), 'django.utils.translation.gettext', '_', ({(450, 16, 450, 29): '"""Object Type"""'}, {}), "('Object Type')", True, 'from django.utils.translation import gettext as _\n'), ((451, 15, 453, 9), 'utilities.forms.APISelectMultiple', 'APISelectMultiple', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((456, 16, 456, 57), 'utilities.forms.add_blank_choice', 'add_blank_choice', ({(456, 33, 456, 56): 'JournalEntryKindChoices'}, {}), '(JournalEntryKindChoices)', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((458, 15, 458, 30), 'utilities.forms.StaticSelect2', 'StaticSelect2', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((470, 14, 470, 25), 'django.utils.translation.gettext', '_', ({(470, 16, 470, 24): '"""Search"""'}, {}), "('Search')", True, 'from django.utils.translation import gettext as _\n'), ((474, 14, 474, 24), 'django.utils.translation.gettext', '_', ({(474, 16, 474, 23): '"""After"""'}, {}), "('After')", True, 'from django.utils.translation import gettext as _\n'), ((475, 15, 475, 31), 'utilities.forms.DateTimePicker', 'DateTimePicker', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((479, 14, 479, 25), 'django.utils.translation.gettext', '_', ({(479, 16, 479, 24): '"""Before"""'}, {}), "('Before')", True, 'from django.utils.translation import gettext as _\n'), ((480, 15, 480, 31), 'utilities.forms.DateTimePicker', 'DateTimePicker', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((483, 16, 483, 59), 'utilities.forms.add_blank_choice', 'add_blank_choice', ({(483, 33, 483, 58): 'ObjectChangeActionChoices'}, {}), '(ObjectChangeActionChoices)', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((485, 15, 485, 30), 'utilities.forms.StaticSelect2', 'StaticSelect2', ({}, {}), '()', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((488, 17, 488, 35), 'django.contrib.auth.models.User.objects.all', 'User.objects.all', ({}, {}), '()', False, 'from django.contrib.auth.models import User\n'), ((490, 14, 490, 23), 'django.utils.translation.gettext', '_', ({(490, 16, 490, 22): '"""User"""'}, {}), "('User')", True, 'from django.utils.translation import gettext as _\n'), ((491, 15, 493, 9), 'utilities.forms.APISelectMultiple', 'APISelectMultiple', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n'), ((496, 17, 496, 42), 'django.contrib.contenttypes.models.ContentType.objects.all', 'ContentType.objects.all', ({}, {}), '()', False, 'from django.contrib.contenttypes.models import ContentType\n'), ((498, 14, 498, 30), 'django.utils.translation.gettext', '_', ({(498, 16, 498, 29): '"""Object Type"""'}, {}), "('Object Type')", True, 'from django.utils.translation import gettext as _\n'), ((499, 15, 501, 9), 'utilities.forms.APISelectMultiple', 'APISelectMultiple', (), '', False, 'from utilities.forms import add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES\n')]