From 7b9e252a970accb4d22a3614777263e1c6d8d882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lie=20Bouttier?= Date: Sun, 16 Oct 2016 23:36:03 +0200 Subject: [PATCH] program --- planning/templates/planning/program.html | 13 +++ planning/urls.py | 1 + planning/utils.py | 110 ++++++++++++++++++ planning/views.py | 11 +- ponyconf/templates/base.html | 3 + .../migrations/0021_auto_20161016_2121.py | 26 +++++ proposals/models.py | 11 +- requirements.txt | 1 + 8 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 planning/templates/planning/program.html create mode 100644 planning/utils.py create mode 100644 proposals/migrations/0021_auto_20161016_2121.py diff --git a/planning/templates/planning/program.html b/planning/templates/planning/program.html new file mode 100644 index 0000000..595a5ed --- /dev/null +++ b/planning/templates/planning/program.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} + +{% load i18n %} + +{% block planningtab %} active{% endblock %} + +{% block content %} + +

{% trans "Program" %}

+ +{{ program }} + +{% endblock %} diff --git a/planning/urls.py b/planning/urls.py index 87add48..c7bc1d3 100644 --- a/planning/urls.py +++ b/planning/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ url(r'^room/add/$', views.RoomCreate.as_view(), name='add-room'), url(r'^room/(?P[-\w]+)/$', views.RoomDetail.as_view(), name='show-room'), url(r'^room/(?P[-\w]+)/edit/$', views.RoomUpdate.as_view(), name='edit-room'), + url(r'^program/$', views.program, name='program'), ] diff --git a/planning/utils.py b/planning/utils.py new file mode 100644 index 0000000..8b3ff43 --- /dev/null +++ b/planning/utils.py @@ -0,0 +1,110 @@ +from django.db.models import Q +from django.utils.safestring import mark_safe +from django.utils.timezone import localtime + +from datetime import datetime, timedelta +from copy import deepcopy +from collections import OrderedDict, namedtuple +from itertools import islice + +from .models import Room + +from proposals.models import Talk + + +Event = namedtuple('Event', ['talk', 'row', 'rowcount']) + + +class Program: + def __init__(self, site): + self.rooms = Room.objects.filter(site=site) + self.talks = Talk.objects.\ + filter(site=site, room__in=self.rooms.all(), start_date__isnull=False).\ + filter(Q(duration__gt=0) | Q(event__duration__gt=0)).\ + exclude(accepted=False).\ + order_by('start_date') + + self.timeslots = [] + for talk in self.talks.all(): + duration = talk.estimated_duration() + assert(duration) + d1 = talk.start_date + d2 = d1 + timedelta(minutes=duration) + if d1 not in self.timeslots: + self.timeslots.append(d1) + if d2 not in self.timeslots: + self.timeslots.append(d2) + self.timeslots = sorted(self.timeslots) + + self.cols = OrderedDict([(room, 1) for room in self.rooms]) + self.rows = OrderedDict([(timeslot, OrderedDict([(room, []) for room in self.rooms])) for timeslot in self.timeslots[:-1]]) + + for talk in self.talks: + self._add_talk(talk) + + def _add_talk(self, talk): + room = talk.room + d1 = self.timeslots.index(talk.start_date) + d2 = self.timeslots.index(talk.start_date + timedelta(minutes=talk.duration)) + col = None + for row, timeslot in enumerate(islice(self.timeslots, d1, d2)): + if col is None: + col = 0 + while col < len(self.rows[timeslot][room]) and self.rows[timeslot][room][col]: + col += 1 + self.cols[room] = max(self.cols[room], col+1) + event = Event(talk=talk, row=row, rowcount=d2-d1) + while len(self.rows[timeslot][room]) <= col: + self.rows[timeslot][room].append(None) + self.rows[timeslot][room][col] = event + + def _header(self): + output = 'Room' + room_cell = '%(name)s
%(label)s' + for room, colspan in self.cols.items(): + options = ' colspan="%d"' % colspan + output += room_cell % {'name': room.name, 'label': room.label, 'options': options} + return '%s' % output + + def _body(self): + row = '%(timeslot)s%(content)s' + cell = '%(content)s' + output = [] + for ts, rooms in self.rows.items(): + content = '' + for room, events in rooms.items(): + for i in range(self.cols[room]): + options = '' + cellcontent = '' + if i < len(events) and events[i]: + event = events[i] + if event.row != 0: + continue + options = ' rowspan="%d" bgcolor="%s"' % (event.rowcount, event.talk.event.color) + cellcontent = str(event.talk) + ' — ' + event.talk.get_speakers_str() + content += cell % {'options': options, 'content': cellcontent} + style, timeslot = self._timeslot(ts) + output.append(row % { + 'style': style, + 'timeslot': timeslot, + 'content': content, + }) + return '\n'.join(output) + + def _timeslot(self, ts): + template = '%(content)s' + start = ts + end = self.timeslots[self.timeslots.index(ts)+1] + duration = (end - start).seconds / 60 + print(start, end, duration) + date_to_string = lambda date: datetime.strftime(localtime(date), '%H:%M') + style = 'height: %dpx;' % int(duration * 0.8) + timeslot = '%s – %s' % tuple(map(date_to_string, [start, end])) + return style, timeslot + + def __str__(self): + template = """\n%(header)s\n%(body)s\n
""" + return mark_safe(template % { + 'header': self._header(), + 'body': self._body(), + }) diff --git a/planning/views.py b/planning/views.py index 5ddc752..cc5d574 100644 --- a/planning/views.py +++ b/planning/views.py @@ -5,9 +5,11 @@ from django.views.generic import CreateView, DetailView, ListView, UpdateView from accounts.mixins import OrgaRequiredMixin, StaffRequiredMixin from proposals.mixins import OnSiteFormMixin +from proposals.models import Talk + from .models import Room from .forms import RoomForm - +from .utils import Program class RoomMixin(object): def get_queryset(self): @@ -30,3 +32,10 @@ class RoomUpdate(OrgaRequiredMixin, RoomMixin, RoomFormMixin, UpdateView): class RoomDetail(StaffRequiredMixin, RoomMixin, DetailView): pass + + +def program(request): + program = Program(site=get_current_site(request)) + return render(request, 'planning/program.html', { + 'program': program, + }) diff --git a/ponyconf/templates/base.html b/ponyconf/templates/base.html index 07aac35..b2041ac 100644 --- a/ponyconf/templates/base.html +++ b/ponyconf/templates/base.html @@ -71,6 +71,9 @@
  •  {% trans "Rooms" %}
  • +
  • +  {% trans "Program" %} +