import os
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, ValidationError, MultipleObjectsReturned
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from tinymce import models as tinymce_models
from datetime import date, datetime, time, timedelta
from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule
from utils import get_automation_id_choices, hash_file
class BroadcastFormat(models.Model):
format = models.CharField(_("Format"), max_length=32)
slug = models.SlugField(_("Slug"), max_length=32, unique=True)
color = models.CharField(_("Color"), max_length=7, default='#ffffff')
text_color = models.CharField(_("Text color"), max_length=7, default='#000000')
enabled = models.BooleanField(_("Enabled"), default=True)
class Meta:
ordering = ('format',)
verbose_name = _("Broadcast format")
verbose_name_plural = _("Broadcast formats")
def admin_color(self):
return u'%s/%s' % (
self.color, self.text_color, self.color, self.text_color)
admin_color.short_description = _("Color")
admin_color.allow_tags = True
def __unicode__(self):
return u'%s' % self.format
class ShowInformation(models.Model):
information = models.CharField(_("Information"), max_length=32)
abbrev = models.CharField(_("Abbreviation"), max_length=4, unique=True)
slug = models.SlugField(_("Slug"), max_length=32, unique=True)
button = models.ImageField(_("Button image"), blank=True, null=True, upload_to='buttons')
button_hover = models.ImageField(_("Button image (hover)"), blank=True, null=True, upload_to='buttons')
big_button = models.ImageField(_("Big button image"), blank=True, null=True, upload_to='buttons')
class Meta:
ordering = ('information',)
verbose_name = _("Show information")
verbose_name_plural = _("Show information")
def admin_buttons(self):
buttons = []
if self.button:
buttons.append(u'' % self.button.url)
else:
buttons.append(u'x')
if self.button_hover:
buttons.append(u'' % self.button_hover.url)
else:
buttons.append(u'x')
if self.big_button:
buttons.append(u'' % self.big_button.url)
else:
buttons.append(u'x')
return ' '.join(buttons)
admin_buttons.short_description = _("Buttons")
admin_buttons.allow_tags = True
def button_url(self):
if self.button:
return self.button.url
else:
return '/site_media/buttons/default-11.png'
def button_hover_url(self):
if self.button_hover:
return self.button_hover.url
else:
return '/site_media/buttons/default-11.png'
def big_button_url(self):
if self.big_button:
return self.big_button.url
else:
return '/site_media/buttons/default-17.png'
def __unicode__(self):
return u'%s' % self.information
class ShowTopic(models.Model):
topic = models.CharField(_("Show topic"), max_length=32)
abbrev = models.CharField(_("Abbreviation"), max_length=4, unique=True)
slug = models.SlugField(_("Slug"), max_length=32, unique=True)
button = models.ImageField(_("Button image"), blank=True, null=True, upload_to='buttons')
button_hover = models.ImageField(_("Button image (hover)"), blank=True, null=True, upload_to='buttons')
big_button = models.ImageField(_("Big button image"), blank=True, null=True, upload_to='buttons')
class Meta:
ordering = ('topic',)
verbose_name = _("Show topic")
verbose_name_plural = _("Show topics")
def admin_buttons(self):
buttons = []
if self.button:
buttons.append(u'' % self.button.url)
else:
buttons.append(u'x')
if self.button_hover:
buttons.append(u'' % self.button_hover.url)
else:
buttons.append(u'x')
if self.big_button:
buttons.append(u'' % self.big_button.url)
else:
buttons.append(u'x')
return ' '.join(buttons)
admin_buttons.short_description = _("Buttons")
admin_buttons.allow_tags = True
def button_url(self):
if self.button:
return self.button.url
else:
return '/site_media/buttons/default-11.png'
def button_hover_url(self):
if self.button_hover:
return self.button_hover.url
else:
return '/site_media/buttons/default-11.png'
def big_button_url(self):
if self.big_button:
return self.big_button.url
else:
return '/site_media/buttons/default-17.png'
def __unicode__(self):
return u'%s' % self.topic
class MusicFocus(models.Model):
focus = models.CharField(_("Focus"), max_length=32)
abbrev = models.CharField(_("Abbreviation"), max_length=4, unique=True)
slug = models.SlugField(_("Slug"), max_length=32, unique=True)
button = models.ImageField(_("Button image"), blank=True, null=True, upload_to='buttons')
button_hover = models.ImageField(_("Button image (hover)"), blank=True, null=True, upload_to='buttons')
big_button = models.ImageField(_("Big button image"), blank=True, null=True, upload_to='buttons')
class Meta:
ordering = ('focus',)
verbose_name = _("Music focus")
verbose_name_plural = _("Music focus")
def admin_buttons(self):
buttons = []
if self.button:
buttons.append(u'' % self.button.url)
else:
buttons.append(u'x')
if self.button_hover:
buttons.append(u'' % self.button_hover.url)
else:
buttons.append(u'x')
if self.big_button:
buttons.append(u'' % self.big_button.url)
else:
buttons.append(u'x')
return ' '.join(buttons)
admin_buttons.short_description = _("Buttons")
admin_buttons.allow_tags = True
def button_url(self):
if self.button:
return self.button.url
else:
return '/site_media/buttons/default-11.png'
def button_hover_url(self):
if self.button_hover:
return self.button_hover.url
else:
return '/site_media/buttons/default-11.png'
def big_button_url(self):
if self.big_button:
return self.big_button.url
else:
return '/site_media/buttons/default-17.png'
def __unicode__(self):
return u'%s' % self.focus
class Language(models.Model):
slug = models.SlugField(_("Slug"), max_length=32, unique=True)
name = models.CharField(_("Name"), max_length=32)
native_name = models.CharField(_("Native Name"), max_length=32)
class Meta:
ordering = ('name',)
verbose_name = _("Language")
verbose_name_plural = _("Languages")
def __unicode__(self):
return '%s' % self.name
class Host(models.Model):
name = models.CharField(_("Name"), max_length=128)
is_always_visible = models.BooleanField(_("Is always visible"), default=False)
email = models.EmailField(_("E-Mail"), blank=True)
website = models.URLField(_("Website"), blank=True)
class Meta:
ordering = ('name',)
verbose_name = _("Host")
verbose_name_plural = _("Hosts")
def __unicode__(self):
return u'%s' % self.name
def get_absolute_url(self):
return reverse('host-detail', args=[str(self.id)])
def active_shows(self):
return self.shows.filter(programslots__until__gt=datetime.today).distinct()
def show_image_filename(instance, filename):
instance.image.open()
filename_base, filename_ext = os.path.splitext(filename)
return "shows/%s/%s%s" % (instance.slug, hash_file(instance.image.file), filename_ext)
class Show(models.Model):
predecessor = models.ForeignKey('self', blank=True, null=True, related_name='successors', verbose_name=_("Predecessor"))
hosts = models.ManyToManyField(Host, blank=True, related_name='shows', verbose_name=_("Hosts"))
owners = models.ManyToManyField(User, blank=True, related_name='shows', verbose_name=_("Owners"))
language = models.ManyToManyField(Language, blank=True, related_name='shows', verbose_name=_("Language"))
broadcastformat = models.ForeignKey(BroadcastFormat, related_name='shows', verbose_name=_("Broadcast format"))
showinformation = models.ManyToManyField(ShowInformation, blank=True, related_name='shows', verbose_name=_("Show information"))
showtopic = models.ManyToManyField(ShowTopic, blank=True, related_name='shows', verbose_name=_("Show topic"))
musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus"))
name = models.CharField(_("Name"), max_length=255)
slug = models.CharField(_("Slug"), max_length=255, unique=True)
image = models.ImageField(_("Image"), max_length=350, blank=True, null=True, upload_to=show_image_filename, help_text=_(
"optimal size 900x675 pixel or smaller, aspect ratio 4:3, file size should be smaller than 500kB"))
short_description = models.CharField(_("Short description"), max_length=64)
description = tinymce_models.HTMLField(_("Description"), blank=True, null=True)
email = models.EmailField(_("E-Mail"), blank=True, null=True)
website = models.URLField(_("Website"), blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
ordering = ('slug',)
verbose_name = _("Show")
verbose_name_plural = _("Shows")
def __unicode__(self):
return u'%04d | %s' % (self.id, self.name)
def get_absolute_url(self):
return reverse('show-detail', args=[self.slug])
def active_programslots(self):
return self.programslots.filter(until__gt=date.today())
class RRule(models.Model):
FREQ_CHOICES = (
(1, _("Monthly")),
(2, _("Weekly")),
(3, _("Daily")),
)
BYSETPOS_CHOICES = (
(1, _("First")),
(2, _("Second")),
(3, _("Third")),
(4, _("Fourth")),
(5, _("Fifth")),
(-1, _("Last")),
)
name = models.CharField(_("Name"), max_length=32, unique=True)
freq = models.IntegerField(_("Frequency"), choices=FREQ_CHOICES)
interval = models.IntegerField(_("Interval"), default=1)
bysetpos = models.IntegerField(_("Set position"), blank=True,
choices=BYSETPOS_CHOICES, null=True)
count = models.IntegerField(_("Count"), blank=True, null=True)
class Meta:
ordering = ('-freq', 'interval', 'bysetpos')
verbose_name = _("Recurrence rule")
verbose_name_plural = _("Recurrence rules")
def __unicode__(self):
return u'%s' % self.name
class ProgramSlot(models.Model):
BYWEEKDAY_CHOICES = (
(0, _("Monday")),
(1, _("Tuesday")),
(2, _("Wednesday")),
(3, _("Thursday")),
(4, _("Friday")),
(5, _("Saturday")),
(6, _("Sunday")),
)
rrule = models.ForeignKey(RRule, related_name='programslots', verbose_name=_("Recurrence rule"))
byweekday = models.IntegerField(_("Weekday"), choices=BYWEEKDAY_CHOICES)
show = models.ForeignKey(Show, related_name='programslots', verbose_name=_("Show"))
dstart = models.DateField(_("First date"))
tstart = models.TimeField(_("Start time"))
tend = models.TimeField(_("End time"))
until = models.DateField(_("Last date"))
is_repetition = models.BooleanField(_("Is repetition"), default=False)
automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices())
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
ordering = ('dstart', 'tstart')
unique_together = ('rrule', 'byweekday', 'dstart', 'tstart')
verbose_name = _("Program slot")
verbose_name_plural = _("Program slots")
def __unicode__(self):
weekday = self.BYWEEKDAY_CHOICES[self.byweekday][1]
tend = self.tend.strftime('%H:%M')
dstart = self.dstart.strftime('%d. %b %Y')
tstart = self.tstart.strftime('%H:%M')
if self.rrule.freq == 0:
return u'%s, %s - %s' % (dstart, tstart, tend)
if self.rrule.freq == 3:
return u'%s, %s - %s' % (self.rrule, tstart, tend)
else:
return u'%s, %s, %s - %s' % (weekday, self.rrule, tstart, tend)
def save(self, *args, **kwargs):
if self.pk:
old = ProgramSlot.objects.get(pk=self.pk)
if self.rrule != old.rrule:
raise ValidationError(u"Recurrence rule cannot ba changed")
if self.byweekday != old.byweekday:
raise ValidationError(u"Weekday cannot be changed")
if self.show != old.show:
raise ValidationError(u"Show cannot be changed")
if self.dstart != old.dstart:
raise ValidationError(u"First date cannot ba changed")
if self.tstart != old.tstart:
raise ValidationError(u"Start time cannot be changed")
if self.tend != old.tend:
raise ValidationError(u"End time cannot be changed")
if self.is_repetition != old.is_repetition:
raise ValidationError(u"Is repetition cannot be changed")
else:
old = False
super(ProgramSlot, self).save(*args, **kwargs)
if self.rrule.freq == 0:
byweekday_start = None
byweekday_end = None
elif self.rrule.freq == 3:
byweekday_start = (0, 1, 2, 3, 4, 5, 6)
byweekday_end = (0, 1, 2, 3, 4, 5, 6)
else:
byweekday_start = int(self.byweekday)
if self.tend < self.tstart:
if self.byweekday < 6:
byweekday_end = int(self.byweekday + 1)
else:
byweekday_end = 0
else:
byweekday_end = int(self.byweekday)
if self.tend < self.tstart:
dend = self.dstart + timedelta(days=+1)
else:
dend = self.dstart
starts = list(rrule(freq=self.rrule.freq,
dtstart=datetime.combine(self.dstart, self.tstart),
interval=self.rrule.interval,
until=self.until + relativedelta(days=+1),
bysetpos=self.rrule.bysetpos,
byweekday=byweekday_start))
ends = list(rrule(freq=self.rrule.freq,
dtstart=datetime.combine(dend, self.tend),
interval=self.rrule.interval,
until=self.until + relativedelta(days=+1),
bysetpos=self.rrule.bysetpos,
byweekday=byweekday_end))
if not old:
for k in range(min(len(starts), len(ends))):
TimeSlot.objects.create(programslot=self, start=starts[k], end=ends[k])
elif self.until > old.until:
for k in range(min(len(starts), len(ends))):
if starts[k].date() > old.until:
TimeSlot.objects.create(programslot=self, start=starts[k], end=ends[k])
class TimeSlotManager(models.Manager):
@staticmethod
def get_or_create_current():
try:
return TimeSlot.objects.get(start__lte=datetime.now, end__gt=datetime.now)
except MultipleObjectsReturned:
return TimeSlot.objects.filter(start__lte=datetime.now, end__gt=datetime.now)[0]
except ObjectDoesNotExist:
once = RRule.objects.get(pk=1)
today = date.today().weekday()
default = Show.objects.get(pk=1)
previous_timeslot = TimeSlot.objects.filter(end__lte=datetime.now).order_by('-start')[0]
next_timeslot = TimeSlot.objects.filter(start__gte=datetime.now)[0]
dstart, tstart = previous_timeslot.end.date(), previous_timeslot.end.time()
until, tend = next_timeslot.start.date(), next_timeslot.start.time()
new_programslot = ProgramSlot(rrule=once,
byweekday=today,
show=default,
dstart=dstart,
tstart=tstart,
tend=tend,
until=until)
try:
new_programslot.validate_unique()
new_programslot.save()
except ValidationError:
pass
else:
return new_programslot.timeslots.all()[0]
@staticmethod
def get_day_timeslots(day):
today = datetime.combine(day, time(6, 0))
tomorrow = today + timedelta(days=1)
return TimeSlot.objects.filter(Q(start__lte=today, end__gte=today) |
Q(start__gt=today, start__lt=tomorrow)).exclude(end=today)
@staticmethod
def get_24h_timeslots(start):
end = start + timedelta(hours=24)
return TimeSlot.objects.filter(Q(start__lte=start, end__gte=start) |
Q(start__gt=start, start__lt=end)).exclude(end=start)
class TimeSlot(models.Model):
programslot = models.ForeignKey(ProgramSlot, related_name='timeslots', verbose_name=_("Program slot"))
start = models.DateTimeField(_("Start time"), unique=True)
end = models.DateTimeField(_("End time"))
show = models.ForeignKey(Show, editable=False, related_name='timeslots')
objects = TimeSlotManager()
class Meta:
ordering = ('start', 'end')
verbose_name = _("Time slot")
verbose_name_plural = _("Time slots")
def __unicode__(self):
start = self.start.strftime('%d.%m.%Y %H:%M')
end = self.end.strftime('%H:%M')
return u'%s - %s | %s' % (start, end, self.show.name)
def save(self, *args, **kwargs):
self.show = self.programslot.show
super(TimeSlot, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('timeslot-detail', args=[str(self.id)])
def note_image_filename(instance, filename):
instance.image.open()
filename_base, filename_ext = os.path.splitext(filename)
return "notes/%s/%s%s" % (instance.show.slug, hash_file(instance.image.file), filename_ext)
class Note(models.Model):
STATUS_CHOICES = (
(0, _("Cancellation")),
(1, _("Recommendation")),
(2, _("Repetition")),
)
timeslot = models.OneToOneField(TimeSlot, verbose_name=_("Time slot"))
title = models.CharField(_("Title"), max_length=128)
image = models.ImageField(_("Image"), max_length=350, blank=True, null=True, upload_to=note_image_filename,
help_text=_("optimal size 900x675 pixel or smaller, aspect ratio 4:3, file size should be smaller than 500kB"))
content = tinymce_models.HTMLField(_("Content"))
status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1)
start = models.DateTimeField(editable=False)
show = models.ForeignKey(Show, editable=False, related_name='notes')
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
ordering = ('timeslot',)
verbose_name = _("Note")
verbose_name_plural = _("Notes")
def __unicode__(self):
return u'%s - %s' % (self.title, self.timeslot)
def save(self, *args, **kwargs):
self.start = self.timeslot.start
self.show = self.timeslot.programslot.show
super(Note, self).save(*args, **kwargs)