from datetime import date, datetime, time, timedelta
from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, ValidationError, MultipleObjectsReturned
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from tinymce import models as tinymce_models
from .utils import get_automation_id_choices
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 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())
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"))
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"), blank=True, null=True, upload_to='show_images')
image_enabled = models.BooleanField(_("show Image"), default=True)
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)])
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)
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)