Upgrade to Pro — share decks privately, control downloads, hide ads and more …

pv.helsinki.at: Die Programmverwaltung von Radi...

pv.helsinki.at: Die Programmverwaltung von Radio Helsinki

Der Web-Auftritt vom Radio Helsinki (http://helsinki.at) wurde bei den Linuxtagen 2011 präsentiert und ist seit Juni 2011 on-line. Die Website vereint Plone als Content Management System und Django in der Programmverwaltung. Dieser Vortrag gibt einen Einblick in die bisherigen Erkenntnisse aus dem Projekt und ins besondere in die Realisierung der Programmverwaltung und einen Ausblick auf die weiteren Pläne.

Ernesto Rico Schmidt

April 28, 2012
Tweet

More Decks by Ernesto Rico Schmidt

Other Decks in Programming

Transcript

  1. Radio Helsinki Freies Radio 92,6 MHz im Raum Graz Mehr

    als 120 ehrenamtliche Sendungsmacher Nichtkommerzialität Offener Zugang Buntes, schnell abwechselndes Programm Viele kurzfristige Sondesendungen und Programmänerungen
  2. old.helsinki.at Von 2001 bis 2011 PHP/MySQL Kein Framework Kein Content

    Management System Erstes Web und PHP-Projekt HTML-Tabellen Zwei SQL-Tabellen 2005 Sendungen seit 2001
  3. Django http://www.djangoproject.com https://github.com/django/django ursprünglich von Adrian Holovaty, Simon Wilson, Jacob

    Kaplan-Moss und Wilson Miner für World-Online in 2003 enwicklet. offen (BSD-Lizenz) seit 2005 Version 1.0 in September 2008 Version 1.1 in Juli 2009 Version 1.2 in Mai 2010 Version 1.3 in März 2011 Version 1.4 in März 2012 Prominente Beispiele: http://disqus.com http://testflightapp.com http://instagram.com
  4. Django Neues in der Version 1.4 Unterstützung für Zeitzonen Unterstützung

    für Selenium Neues default-Layout für Projekte und manage.py Custom Templates für Projekte und Applikationen Besseres WSGI-Unterstützung QuerySet.select_for_update() Model.object.bulk_create() QuerySet.prefetch_related() und vieles mehr
  5. Das Model class Show(models.Model): predecessor = models.ForeignKey(’self’, ...) hosts =

    models.ManyToManyField(Host, ...) owners = models.ManyToManyField(User, ...) broadcastformat = models.ForeignKey(BroadcastFormat, ...) showinformation = models.ManyToManyField(ShowInformation, ...) showtopic = models.ManyToManyField(ShowTopic, ...) musicfocus = models.ManyToManyField(MusicFocus, ...) name = models.CharField(max_length=255)
  6. Das Model # Show slug = models.CharField(...) image = models.ImageField(...)

    image_enabled = models.BooleanField(...) short_description = models.CharField(...) description = tinymce_models.HTMLField(...) email = models.EmailField(...) website = models.URLField(...) cba_series_id = models.IntegerField(...) @models.permalink def get_absolute_url(self): return (’show-detail’, [self.slug])
  7. Das Model 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(...) freq = models.IntegerField(choices=FREQ_CHOICES) interval = models.IntegerField(...) bysetpos = models.IntegerField(... choices=BYSETPOS_CHOICES, ...) count = models.IntegerField(...)
  8. Das Model class ProgramSlot(models.Model): BYWEEKDAY_CHOICES = ( (0, ’MON’), (1,

    ’TUE’), (2, ’WED’), (3, ’THU’), (4, ’FRI’), (5, ’SAT’), (6, ’SUN’), ) rrule = models.ForeignKey(RRule, ...) byweekday = models.IntegerField(choices=BYWEEKDAY_CHOICES) show = models.ForeignKey(Show, ...) dstart = models.DateField() tstart = models.TimeField() tend = models.TimeField() until = models.DateField() is_repetition = models.BooleanField(...)
  9. Das Model # ProgramSlot def save(self, *args, **kwargs): old =

    ProgramSlot.objects.get(pk=self.pk) if self.pk super(ProgramSlot, self).save(*args, **kwargs) ... 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))
  10. Das Model # ProgramSlot.save if not old: for k in

    range(len(starts)): timeslot = TimeSlot.objects.create( programslot=self, start=starts[k], end=ends[k]) elif self.until > old.until: for k in range(len(starts)): if starts[k].date() > old.until: timeslot = TimeSlot.objects.create( programslot=self, start=starts[k], end=ends[k])
  11. Das Model class TimeSlot(models.Model): programslot = models.ForeignKey(ProgramSlot, ...) start =

    models.DateTimeField(unique=True) end = models.DateTimeField() show = models.ForeignKey(Show, ...) objects = TimeSlotManager()
  12. Das Model class TimeSlotManager(models.Manager): def get_or_create_current(self): 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.objects.filter( end__lte=datetime.now()).order_by(’-start’)[0]
  13. Das Model # TimeslotManager.get_or_create_current next = TimeSlot.objects.filter( start__gte=datetime.now())[0] dstart =

    previous.end.date() tstart = previous.end.time() until, tend = next.start.date(), next.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]
  14. Das Model class TimeSlot(models.Model): programslot = models.ForeignKey(ProgramSlot, ...) start =

    models.DateTimeField(unique=True) end = models.DateTimeField() show = models.ForeignKey(Show, ...) def save(self, *args, **kwargs): self.show = self.programslot.show super(TimeSlot, self).save(*args, **kwargs) @models.permalink def get_absolute_url(self): return (’timeslot-detail’, [self.id])
  15. Das Model class Note(models.Model): STATUS_CHOICES = ( (0, ’Cancellation’), (1,

    ’Recommendation’), (2, ’Repetition’), ) timeslot = models.OneToOneField(TimeSlot) owner = models.ForeignKey(User, ...) title = models.CharField(...) content = tinymce_models.HTMLField() status = models.IntegerField(choices=STATUS_CHOICES, default=1) cba_entry_id = models.IntegerField(...) start = models.DateTimeField(...) show = models.ForeignKey(Show, ...)
  16. Die Views def recommendations(request, template_name=’program/recommendations.html’): now = datetime.now() end =

    now + timedelta(weeks=1) queryset = TimeSlot.objects.filter( Q(note__isnull=False, note__status=1, start__range=(now, end)) | Q(show__broadcastformat__slug=’sondersendung’, start__range=(now, end))).order_by(’start’)[:20] return list_detail.object_list(request, queryset=queryset, template_name=template_name, template_object_name=’recommendation’)
  17. Die Views def day_schedule(request, year=None, month=None, day=None): if year is

    None and month is None and day is None: today = datetime.combine(date.today(), time(6, 0)) else: today = datetime.strptime(’%s__%s__%s__06__00’ % \ (year, month, day), ’%Y__%m__%d__%H__%M’) tomorrow = today+timedelta(days=1) recommendations = Note.objects.filter(status=1, timeslot__start__range=(today, tomorrow)) default_show = Show.objects.get(pk=1) extra_context = dict(day=today, recommendations=recommendations, default_show=default_sh timeslots = TimeSlot.objects.get_day_timeslots(today) extra_context[’timeslots’] = timeslots return simple.direct_to_template(request, extra_context=extra_context, template=’program/day_schedule.html’)
  18. Die Views def current_show(request): current = TimeSlot.objects.get_or_create_current() previous = current.get_previous_by_start()

    next = current.get_next_by_start() after_next = next.get_next_by_start() extra_context = dict(current=current, previous=previous, next=next, after_next=after_next) return simple.direct_to_template(request, template=’program/boxes/current.html’, extra_context=extra_context)
  19. Die Views def week_schedule(request, year=None, week=None): if year is None

    and week is None: year, week = datetime.strftime(datetime.today(), ’%Y__%W’).split(’__’) monday = datetime.strptime(’%s__%s__1__06__00’ % \ (year, week), ’%Y__%W__%w__%H__%M’) tuesday = monday+timedelta(days=1) wednesday = monday+timedelta(days=2) thursday = monday+timedelta(days=3) friday = monday+timedelta(days=4) saturday = monday+timedelta(days=5) sunday = monday+timedelta(days=6) default_show = Show.objects.get(pk=1) extra_context = dict(monday=monday, tuesday=tuesday, wednesday=wednesday, thursday=thursday, friday=friday, saturday=saturday, sunday=sunday, default_show=default_show)
  20. Die Views extra_context[’monday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(monday) extra_context[’tuesday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(tuesday)

    extra_context[’wednesday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(wednesday) extra_context[’thursday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(thursday) extra_context[’friday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(friday) extra_context[’saturday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(saturday) extra_context[’sunday_timeslots’] = \ TimeSlot.objects.get_day_timeslots(sunday) return simple.direct_to_template(request, template=’program/week_schedule.html’, extra_context=extra_context)
  21. Die URLs hosts = { ’queryset’: Host.objects.filter( shows__programslots__until__gte=\ date.today()).distinct(), ’template_object_name’:

    ’host’ } shows = { ’queryset’: Show.objects.filter( programslots__until__gt=\ date.today()).exclude(id=1).distinct(), ’template_object_name’: ’show’ } timeslots = { ’queryset’: TimeSlot.objects.all(), ’template_object_name’: ’timeslot’ }
  22. Templates helsinki/templates/program boxes broadcastformat.html current.html musicfocus.html recommendations.html showinformation.html showtopic.html day_schedule.html

    host_detail.html host_list.html recommendations.html show_detail.html show_list.html timeslot_detail.html week_schedule.html week_schedule_timeslot.html
  23. Die URLs urlpatterns = patterns(’’, url(r’^today/?$’, day_schedule), url(r’^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2}) day_schedule), url(r’^(?P<year>\d{4})/(?P<week>\d{1,2})/?$’,

    week_schedule), url(r’^current_box/?$’, cache_page(current_show, 60)), url(r’^hosts/?$’, object_list, hosts), url(r’^hosts/(?P<object_id>\d+)/?$’, object_detail, hosts, name=’host-detail’), url(r’^tips/?$’, recommendations), url(r’^shows/?$’, show_list), url(r’^shows/(?P<slug>[\w-]+)/?$’, object_detail, shows, name=’show-detail’), url(r’^(?P<object_id>\d+)/?$’, object_detail, timeslots, name=’timeslot-detail’), url(r’^week/?$’, week_schedule),
  24. Ausblick http://helsinki.at https://github.com/nnrcschmdt/helsinki Datenbanktransaktionen Update auf Django 1.4 Dashboard-Applikation statt

    Admin-Inteface Bessere Vermeidung und Lösung von Konflikten: Validierung Integration der Sendungsautomatisierung (Rivendell)