159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
import datetime
|
|
from django.db import models
|
|
from utils.models import UUIDModel, CreatedUpdatedModel
|
|
from program.models import EventType
|
|
from django.contrib.postgres.fields import DateTimeRangeField
|
|
from psycopg2.extras import DateTimeTZRange
|
|
from django.core.exceptions import ValidationError
|
|
from datetime import timedelta
|
|
|
|
|
|
class Camp(CreatedUpdatedModel, UUIDModel):
|
|
class Meta:
|
|
verbose_name = 'Camp'
|
|
verbose_name_plural = 'Camps'
|
|
|
|
title = models.CharField(
|
|
verbose_name='Title',
|
|
help_text='Title of the camp, ie. Bornhack 2016.',
|
|
max_length=255,
|
|
)
|
|
|
|
tagline = models.CharField(
|
|
verbose_name='Tagline',
|
|
help_text='Tagline of the camp, ie. "Initial Commit"',
|
|
max_length=255,
|
|
)
|
|
|
|
slug = models.SlugField(
|
|
verbose_name='Url Slug',
|
|
help_text='The url slug to use for this camp'
|
|
)
|
|
|
|
buildup = DateTimeRangeField(
|
|
verbose_name='Buildup Period',
|
|
help_text='The camp buildup period.',
|
|
)
|
|
|
|
camp = DateTimeRangeField(
|
|
verbose_name='Camp Period',
|
|
help_text='The camp period.',
|
|
)
|
|
|
|
teardown = DateTimeRangeField(
|
|
verbose_name='Teardown period',
|
|
help_text='The camp teardown period.',
|
|
)
|
|
|
|
def clean(self):
|
|
''' Make sure the dates make sense - meaning no overlaps and buildup before camp before teardown '''
|
|
errors = []
|
|
# sanity checking for buildup
|
|
if self.buildup.lower > self.buildup.upper:
|
|
errors.append(ValidationError({'buildup', 'Start of buildup must be before end of buildup'}))
|
|
|
|
# sanity checking for camp
|
|
if self.camp.lower > self.camp.upper:
|
|
errors.append(ValidationError({'camp', 'Start of camp must be before end of camp'}))
|
|
|
|
# sanity checking for teardown
|
|
if self.teardown.lower > self.teardown.upper:
|
|
errors.append(ValidationError({'teardown', 'Start of teardown must be before end of teardown'}))
|
|
|
|
# check for overlaps buildup vs. camp
|
|
if self.buildup.upper > self.camp.lower:
|
|
msg = "End of buildup must not be after camp start"
|
|
errors.append(ValidationError({'buildup', msg}))
|
|
errors.append(ValidationError({'camp', msg}))
|
|
|
|
# check for overlaps camp vs. teardown
|
|
if self.camp.upper > self.teardown.lower:
|
|
msg = "End of camp must not be after teardown start"
|
|
errors.append(ValidationError({'camp', msg}))
|
|
errors.append(ValidationError({'teardown', msg}))
|
|
|
|
if errors:
|
|
raise ValidationError(errors)
|
|
|
|
def __unicode__(self):
|
|
return "%s - %s" % (self.title, self.tagline)
|
|
|
|
@property
|
|
def event_types(self):
|
|
# return all event types with at least one event in this camp
|
|
return EventType.objects.filter(event__instances__isnull=False, event__camp=self).distinct()
|
|
|
|
@property
|
|
def logo_small(self):
|
|
return 'img/%(slug)s/logo/%(slug)s-logo-small.png' % {'slug': self.slug}
|
|
|
|
@property
|
|
def logo_large(self):
|
|
return 'img/%(slug)s/logo/%(slug)s-logo-large.png' % {'slug': self.slug}
|
|
|
|
def get_days(self, camppart):
|
|
'''
|
|
Returns a list of DateTimeTZRanges representing the days during the specified part of the camp.
|
|
'''
|
|
if not hasattr(self, camppart):
|
|
print("nonexistant field/attribute")
|
|
return False
|
|
|
|
field = getattr(self, camppart)
|
|
|
|
if not hasattr(field, '__class__') or not hasattr(field.__class__, '__name__') or not field.__class__.__name__ == 'DateTimeTZRange':
|
|
print("this attribute is not a datetimetzrange field: %s" % field)
|
|
return False
|
|
|
|
daycount = (field.upper - field.lower).days
|
|
days = []
|
|
for i in range(0, daycount):
|
|
if i == 0:
|
|
# on the first day use actual start time instead of midnight
|
|
days.append(
|
|
DateTimeTZRange(
|
|
field.lower,
|
|
(field.lower+timedelta(days=i+1)).replace(hour=0)
|
|
)
|
|
)
|
|
elif i == daycount-1:
|
|
# on the last day use actual end time instead of midnight
|
|
days.append(
|
|
DateTimeTZRange(
|
|
(field.lower+timedelta(days=i)).replace(hour=0),
|
|
field.lower+timedelta(days=i+1)
|
|
)
|
|
)
|
|
else:
|
|
# neither first nor last day, goes from midnight to midnight
|
|
days.append(
|
|
DateTimeTZRange(
|
|
(field.lower+timedelta(days=i)).replace(hour=0),
|
|
(field.lower+timedelta(days=i+1)).replace(hour=0)
|
|
)
|
|
)
|
|
return days
|
|
|
|
@property
|
|
def buildup_days(self):
|
|
'''
|
|
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
|
'''
|
|
return self.get_days('buildup')
|
|
|
|
@property
|
|
def camp_days(self):
|
|
'''
|
|
Returns a list of DateTimeTZRanges representing the days during the camp.
|
|
'''
|
|
return self.get_days('camp')
|
|
|
|
@property
|
|
def teardown_days(self):
|
|
'''
|
|
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
|
'''
|
|
return self.get_days('teardown')
|
|
|
|
|