Closes #1283: Added a time zone field to the site model

This commit is contained in:
Jeremy Stretch 2017-12-19 17:24:14 -05:00
parent 9984238f2a
commit b20258c66e
10 changed files with 83 additions and 10 deletions

View File

@ -20,7 +20,7 @@ from extras.api.customfields import CustomFieldModelSerializer
from ipam.models import IPAddress, VLAN
from tenancy.api.serializers import NestedTenantSerializer
from users.api.serializers import NestedUserSerializer
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
from utilities.api import ChoiceFieldSerializer, TimeZoneField, ValidatedModelSerializer
from virtualization.models import Cluster
@ -58,13 +58,14 @@ class WritableRegionSerializer(ValidatedModelSerializer):
class SiteSerializer(CustomFieldModelSerializer):
region = NestedRegionSerializer()
tenant = NestedTenantSerializer()
time_zone = TimeZoneField(required=False)
class Meta:
model = Site
fields = [
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields', 'count_prefixes',
'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'physical_address',
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
'count_prefixes', 'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
]
@ -77,12 +78,13 @@ class NestedSiteSerializer(serializers.ModelSerializer):
class WritableSiteSerializer(CustomFieldModelSerializer):
time_zone = TimeZoneField(required=False)
class Meta:
model = Site
fields = [
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'physical_address',
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
]

View File

@ -7,6 +7,7 @@ from django.contrib.auth.models import User
from django.contrib.postgres.forms.array import SimpleArrayField
from django.db.models import Count, Q
from mptt.forms import TreeNodeChoiceField
from timezone_field import TimeZoneFormField
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
from ipam.models import IPAddress, VLAN, VLANGroup
@ -96,7 +97,7 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
model = Site
fields = [
'name', 'slug', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'physical_address',
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'time_zone', 'comments',
]
widgets = {
'physical_address': SmallTextarea(attrs={'rows': 3}),
@ -135,7 +136,7 @@ class SiteCSVForm(forms.ModelForm):
model = Site
fields = [
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
'contact_name', 'contact_phone', 'contact_email', 'comments',
'contact_name', 'contact_phone', 'contact_email', 'time_zone', 'comments',
]
help_texts = {
'name': 'Site name',
@ -149,9 +150,10 @@ class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
asn = forms.IntegerField(min_value=1, max_value=4294967295, required=False, label='ASN')
time_zone = TimeZoneFormField(required=False)
class Meta:
nullable_fields = ['region', 'tenant', 'asn']
nullable_fields = ['region', 'tenant', 'asn', 'time_zone']
class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-19 21:53
from __future__ import unicode_literals
from django.db import migrations
import timezone_field.fields
class Migration(migrations.Migration):
dependencies = [
('dcim', '0053_platform_manufacturer'),
]
operations = [
migrations.AddField(
model_name='site',
name='time_zone',
field=timezone_field.fields.TimeZoneField(blank=True),
),
]

View File

@ -14,6 +14,7 @@ from django.db.models import Count, Q, ObjectDoesNotExist
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from mptt.models import MPTTModel, TreeForeignKey
from timezone_field import TimeZoneField
from circuits.models import Circuit
from extras.models import CustomFieldModel, CustomFieldValue, ImageAttachment
@ -86,6 +87,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
tenant = models.ForeignKey(Tenant, related_name='sites', blank=True, null=True, on_delete=models.PROTECT)
facility = models.CharField(max_length=50, blank=True)
asn = ASNField(blank=True, null=True, verbose_name='ASN')
time_zone = TimeZoneField(blank=True)
physical_address = models.CharField(max_length=200, blank=True)
shipping_address = models.CharField(max_length=200, blank=True)
contact_name = models.CharField(max_length=50, blank=True)
@ -98,7 +100,8 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
objects = SiteManager()
csv_headers = [
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'contact_name', 'contact_phone', 'contact_email',
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'contact_name', 'contact_phone',
'contact_email',
]
class Meta:
@ -118,6 +121,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
self.tenant.name if self.tenant else None,
self.facility,
self.asn,
self.time_zone,
self.contact_name,
self.contact_phone,
self.contact_email,

View File

@ -134,6 +134,7 @@ INSTALLED_APPS = (
'mptt',
'rest_framework',
'rest_framework_swagger',
'timezone_field',
'circuits',
'dcim',
'ipam',

View File

@ -1,5 +1,6 @@
{% extends '_base.html' %}
{% load static from staticfiles %}
{% load tz %}
{% load helpers %}
{% block content %}
@ -105,6 +106,17 @@
{% endif %}
</td>
</tr>
<tr>
<td>Time Zone</td>
<td>
{% if site.time_zone %}
{{ site.time_zone }} (UTC {{ site.time_zone|tzoffset }})<br />
<small class="text-muted">Site time: {% timezone site.time_zone %}{% now "SHORT_DATETIME_FORMAT" %}{% endtimezone %}</small>
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td>
</tr>
</table>
</div>
<div class="panel panel-default">

View File

@ -10,6 +10,7 @@
{% render_field form.region %}
{% render_field form.facility %}
{% render_field form.asn %}
{% render_field form.time_zone %}
</div>
</div>
<div class="panel panel-default">

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals
from collections import OrderedDict
import pytz
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
@ -97,6 +98,23 @@ class ContentTypeFieldSerializer(Field):
raise ValidationError("Invalid content type")
class TimeZoneField(Field):
"""
Represent a pytz time zone.
"""
def to_representation(self, obj):
return obj.zone if obj else None
def to_internal_value(self, data):
if not data:
return ""
try:
return pytz.timezone(str(data))
except pytz.exceptions.UnknownTimeZoneError:
raise ValidationError('Invalid time zone "{}"'.format(data))
#
# Viewsets
#

View File

@ -1,5 +1,8 @@
from __future__ import unicode_literals
import datetime
import pytz
from django import template
from django.utils.safestring import mark_safe
from markdown import markdown
@ -117,6 +120,14 @@ def example_choices(field, arg=3):
return ', '.join(examples) or 'None'
@register.filter()
def tzoffset(value):
"""
Returns the hour offset of a given time zone using the current time.
"""
return datetime.datetime.now(value).strftime('%z')
#
# Tags
#

View File

@ -5,6 +5,7 @@ django-filter>=1.1.0
django-mptt==0.8.7
django-rest-swagger>=2.1.0
django-tables2>=1.10.0
django-timezone-field>=2.0
djangorestframework>=3.6.4
graphviz>=0.6
Markdown>=2.6.7