diff --git a/planning/__init__.py b/planning/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/planning/admin.py b/planning/admin.py
deleted file mode 100644
index 9b4cda0..0000000
--- a/planning/admin.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.contrib import admin
-
-from planning.models import Room
-from ponyconf.admin import SiteAdminMixin
-
-
-class RoomAdmin(SiteAdminMixin, admin.ModelAdmin):
- list_display = ('name', 'label', 'capacity')
-
-
-admin.site.register(Room, RoomAdmin)
diff --git a/planning/apps.py b/planning/apps.py
deleted file mode 100644
index 9694dbf..0000000
--- a/planning/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class PlanningConfig(AppConfig):
- name = 'planning'
diff --git a/planning/forms.py b/planning/forms.py
deleted file mode 100644
index 95443d0..0000000
--- a/planning/forms.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django import forms
-
-from .models import Room
-
-
-class RoomForm(forms.ModelForm):
- class Meta:
- model = Room
- fields = ['name', 'label', 'capacity']
-
- def __init__(self, *args, **kwargs):
- self.site = kwargs.pop('site')
- super().__init__(*args, **kwargs)
-
- def clean_name(self):
- name = self.cleaned_data['name']
- if (not self.instance or self.instance.name != name) \
- and Room.objects.filter(site=self.site, name=name).exists():
- raise self.instance.unique_error_message(self._meta.model, ['name'])
- return name
diff --git a/planning/migrations/__init__.py b/planning/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/planning/models.py b/planning/models.py
deleted file mode 100644
index 4454805..0000000
--- a/planning/models.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from django.db import models
-
-from django.contrib.sites.models import Site
-from django.core.urlresolvers import reverse
-from django.db.models import Q
-
-from autoslug import AutoSlugField
-
-
-class Room(models.Model):
-
- site = models.ForeignKey(Site, on_delete=models.CASCADE)
- slug = AutoSlugField(populate_from='name')
- name = models.CharField(max_length=256, blank=True, default="")
- label = models.CharField(max_length=256, blank=True, default="")
- capacity = models.IntegerField(default=0)
-
- class Meta:
- unique_together = ['site', 'name']
- ordering = ['name']
-
- def __str__(self):
- return self.name
-
- def get_absolute_url(self):
- return reverse('show-room', kwargs={'slug': self.slug})
-
- @property
- def talks(self):
- return self.talk_set.exclude(accepted=False)
-
- @property
- def talks_by_date(self):
- return self.talks.filter(start_date__isnull=False).exclude(duration=0, event__duration=0).order_by('start_date').all()
-
- @property
- def unscheduled_talks(self):
- return self.talks.filter(Q(start_date__isnull=True) | Q(duration=0, event__duration=0)).all()
diff --git a/planning/templates/planning/public-program.html b/planning/templates/planning/public-program.html
deleted file mode 100644
index b66f6be..0000000
--- a/planning/templates/planning/public-program.html
+++ /dev/null
@@ -1,14 +0,0 @@
-{% extends '_base.html' %}
-{% load i18n %}
-
-{% block body %}
-
-
-
-
{{ site.name }} – {% trans "Schedule" %}
-
-{{ program }}
-
-
-
-{% endblock %}
diff --git a/planning/templates/planning/room_detail.html b/planning/templates/planning/room_detail.html
deleted file mode 100644
index 15af3c0..0000000
--- a/planning/templates/planning/room_detail.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{% extends 'staff.html' %}
-
-{% load i18n %}
-
-{% block roomstab %} class="active"{% endblock %}
-
-{% block content %}
-
-{{ room.name }}
- {{ room.label }}
-
-
-
-{% trans "Scheduled talks" %}
-{% for talk in room.talks_by_date %}
-{% if forloop.first %}{% endif %}
--
- {{ talk }} – {{ talk.get_speakers_str }}
- {{ talk.start_date }} – {% if talk.end_date %}{{ talk.end_date|date:"H:i" }}{% else %}?{% endif %}
-
-{% if forloop.last %}
{% endif %}
-{% empty %}
-{% trans "No talks." %}
-{% endfor %}
-
-{% trans "Unscheduled talks" %}
-{% for talk in room.unscheduled_talks %}
-{% if forloop.first %}{% endif %}
--
- {{ talk }} – {{ talk.get_speakers_str }}
-
-{% if forloop.last %}
{% endif %}
-{% empty %}
-{% trans "No talks." %}
-{% endfor %}
-
-{% endblock %}
diff --git a/planning/templates/planning/room_form.html b/planning/templates/planning/room_form.html
deleted file mode 100644
index d658f2a..0000000
--- a/planning/templates/planning/room_form.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends 'staff.html' %}
-
-{% load i18n %}
-
-{% block roomstab %} class="active"{% endblock %}
-
-{% block css %}
-{{ block.super }}
-{{ form.media.css }}
-{% endblock %}
-
-{% block content %}
-
-{% if room %}{% trans "Modify the room" %}{% else %}{% trans "Add a room" %}{% endif %}
-
-{% include "_form.html" %}
-
-{% endblock %}
-
-{% block js_end %}
-{{ block.super }}
-{{ form.media.js }}
-{% endblock %}
diff --git a/planning/templates/planning/room_list.html b/planning/templates/planning/room_list.html
deleted file mode 100644
index 001a9d5..0000000
--- a/planning/templates/planning/room_list.html
+++ /dev/null
@@ -1,53 +0,0 @@
-{% extends 'staff.html' %}
-
-{% load bootstrap3 accounts_tags i18n %}
-
-{% block roomstab %} class="active"{% endblock %}
-
-{% block content %}
-
-{% trans "Rooms" %}
-
-{% if request|orga %}
-{% trans "Add a room" %}
-{% endif %}
-
-
- {% for room in room_list %}
-
-
- {% if room.label %}
{{ room.label }}
{% endif %}
-
- {{ room.capacity }} {% trans "place" %}{{ room.capacity|pluralize }}
- {% if request|staff %}
- |
-
- {{ room.talks.count }} {% trans "talk" %}{{ room.talks.count|pluralize }}
-
- {% if request|orga %}
- |
- {% bootstrap_icon "pencil" %}
- {% endif %}
- {% endif %}
-
-
- {% cycle '' '
' %}
- {% cycle '' '' '
' %}
- {% empty %}
-
{% trans "No rooms." %}
- {% endfor %}
-
-
-{% endblock %}
-
-{% block js_end %}
-{{ block.super }}
-
-{% endblock %}
diff --git a/planning/templates/planning/schedule.html b/planning/templates/planning/schedule.html
deleted file mode 100644
index e8e6367..0000000
--- a/planning/templates/planning/schedule.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends 'staff.html' %}
-
-{% load i18n %}
-
-{% block scheduletab %} class="active"{% endblock %}
-
-{% block content %}
-
-{% trans "Schedule" %}
-
-{{ program }}
-
-{% endblock %}
diff --git a/planning/tests.py b/planning/tests.py
deleted file mode 100644
index 7ce503c..0000000
--- a/planning/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/planning/urls.py b/planning/urls.py
deleted file mode 100644
index ff9a096..0000000
--- a/planning/urls.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.conf.urls import url
-
-from planning import views
-
-
-urlpatterns = [
- url(r'^room/$', views.RoomList.as_view(), name='list-rooms'),
- 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'^schedule/$', views.program_pending, name='show-schedule'),
- url(r'^schedule/public/$', views.program_public, name='public-schedule'),
- url(r'^planning/program/public/$', views.program_public),
-]
diff --git a/planning/utils.py b/planning/utils.py
deleted file mode 100644
index 646d387..0000000
--- a/planning/utils.py
+++ /dev/null
@@ -1,328 +0,0 @@
-from django.db.models import Q
-from django.utils.safestring import mark_safe
-from django.utils.html import escape
-from django.utils.timezone import localtime
-from django.core.cache import cache
-from django.core.urlresolvers import reverse
-
-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 Conference, Talk
-
-
-Event = namedtuple('Event', ['talk', 'row', 'rowcount'])
-
-
-class Program:
- def __init__(self, site, pending=False, cache=True):
- self.site = site
- self.pending = pending
- self.cache = cache
- self.initialized = False
-
- def _lazy_init(self):
- self.conference = Conference.objects.get(site=self.site)
- self.talks = Talk.objects.\
- exclude(event__label__exact='').\
- filter(site=self.site, room__isnull=False, start_date__isnull=False).\
- filter(Q(duration__gt=0) | Q(event__duration__gt=0))
-
- if self.pending:
- self.talks = self.talks.exclude(accepted=False)
- else:
- self.talks = self.talks.filter(accepted=True)
-
- self.talks = self.talks.order_by('start_date')
-
- self.rooms = Room.objects.filter(talk__in=self.talks.all()).order_by('name').distinct()
-
- self.days = {}
- for talk in self.talks.all():
- duration = talk.estimated_duration
- assert(duration)
- dt1 = talk.start_date
- d1 = localtime(dt1).date()
- if d1 not in self.days.keys():
- self.days[d1] = {'timeslots': []}
- dt2 = dt1 + timedelta(minutes=duration)
- d2 = localtime(dt2).date()
- if d2 not in self.days.keys():
- self.days[d2] = {'timeslots': []}
- if dt1 not in self.days[d1]['timeslots']:
- self.days[d1]['timeslots'].append(dt1)
- if dt2 not in self.days[d2]['timeslots']:
- self.days[d2]['timeslots'].append(dt2)
-
- self.cols = OrderedDict([(room, 1) for room in self.rooms])
- for day in self.days.keys():
- self.days[day]['timeslots'] = sorted(self.days[day]['timeslots'])
- self.days[day]['rows'] = OrderedDict([(timeslot, OrderedDict([(room, []) for room in self.rooms])) for timeslot in self.days[day]['timeslots'][:-1]])
-
- for talk in self.talks.exclude(plenary=True).all():
- self._add_talk(talk)
-
- for talk in self.talks.filter(plenary=True).all():
- self._add_talk(talk)
-
- self.initialized = True
-
- def _add_talk(self, talk):
- room = talk.room
- dt1 = talk.start_date
- d1 = localtime(dt1).date()
- dt2 = talk.start_date + timedelta(minutes=talk.estimated_duration)
- d2 = localtime(dt2).date()
- assert(d1 == d2) # this is a current limitation
- dt1 = self.days[d1]['timeslots'].index(dt1)
- dt2 = self.days[d1]['timeslots'].index(dt2)
- col = None
- for row, timeslot in enumerate(islice(self.days[d1]['timeslots'], dt1, dt2)):
- if col is None:
- col = 0
- while col < len(self.days[d1]['rows'][timeslot][room]) and self.days[d1]['rows'][timeslot][room][col]:
- col += 1
- self.cols[room] = max(self.cols[room], col+1)
- event = Event(talk=talk, row=row, rowcount=dt2-dt1)
- while len(self.days[d1]['rows'][timeslot][room]) <= col:
- self.days[d1]['rows'][timeslot][room].append(None)
- self.days[d1]['rows'][timeslot][room][col] = event
-
- def _html_header(self):
- output = 'Room | '
- room_cell = '%(name)s %(label)s | '
- for room, colspan in self.cols.items():
- options = ' style="min-width: 100px;" colspan="%d"' % colspan
- output += room_cell % {'name': escape(room.name), 'label': escape(room.label), 'options': options}
- return '%s
' % output
-
- def _html_body(self):
- output = ''
- for day in sorted(self.days.keys()):
- output += self._html_day_header(day)
- output += self._html_day(day)
- return output
-
- def _html_day_header(self, day):
- row = '%(day)s |
'
- colcount = 1
- for room, col in self.cols.items():
- colcount += col
- return row % {
- 'colcount': colcount,
- 'day': datetime.strftime(day, '%A %d %B'),
- }
-
- def _html_day(self, day):
- output = []
- rows = self.days[day]['rows']
- for ts, rooms in rows.items():
- output.append(self._html_row(day, ts, rooms))
- return '\n'.join(output)
-
- def _html_row(self, day, ts, rooms):
- row = '%(timeslot)s%(content)s
'
- cell = '%(content)s | '
- content = ''
- for room, events in rooms.items():
- colspan = 1
- for i in range(self.cols[room]):
- options = ' colspan="%d"' % colspan
- 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 = escape(str(event.talk)) + '
' + escape(event.talk.get_speakers_str()) + ''
- elif (i+1 > len(events) or not events[i+1]) and i+1 < self.cols[room]:
- colspan += 1
- continue
- colspan = 1
- content += cell % {'options': options, 'content': mark_safe(cellcontent)}
- style, timeslot = self._html_timeslot(day, ts)
- return row % {
- 'style': style,
- 'timeslot': timeslot,
- 'content': content,
- }
-
- def _html_timeslot(self, day, ts):
- template = '%(content)s | '
- start = ts
- end = self.days[day]['timeslots'][self.days[day]['timeslots'].index(ts)+1]
- duration = (end - start).seconds / 60
- date_to_string = lambda date: datetime.strftime(localtime(date), '%H:%M')
- style = 'height: %dpx;' % int(duration * 1.2)
- timeslot = '%s – %s | ' % tuple(map(date_to_string, [start, end]))
- return style, timeslot
-
- def _as_html(self):
- template = """"""
- if not self.initialized:
- self._lazy_init()
- return template % {
- 'header': self._html_header(),
- 'body': self._html_body(),
- }
-
- def _as_xml(self):
- if not self.initialized:
- self._lazy_init()
- result = """
-
-%(conference)s
-%(days)s
-
-"""
-
- if not len(self.days):
- return result % {'conference': '', 'days': ''}
-
- conference_xml = """
- %(title)s
-
- %(venue)s
- %(city)s
- %(start_date)s
- %(end_date)s
- %(days_count)s
- 09:00:00
- 00:05:00
-
-""" % {
- 'title': self.site.name,
- 'venue': ', '.join(map(lambda x: x.strip(), self.conference.venue.split('\n'))),
- 'city': self.conference.city,
- 'start_date': sorted(self.days.keys())[0].strftime('%Y-%m-%d'),
- 'end_date': sorted(self.days.keys(), reverse=True)[0].strftime('%Y-%m-%d'),
- 'days_count': len(self.days),
- }
-
- days_xml = ''
- for index, day in enumerate(sorted(self.days.keys())):
- days_xml += '\n' % {
- 'index': index + 1,
- 'date': day.strftime('%Y-%m-%d'),
- }
- for room in self.rooms.all():
- days_xml += ' \n' % room.name
- for talk in self.talks.filter(room=room).order_by('start_date'):
- if localtime(talk.start_date).date() != day:
- continue
- duration = talk.estimated_duration
- persons = ''
- for speaker in talk.speakers.all():
- persons += ' %(person)s\n' % {
- 'person_id': speaker.id,
- 'person': str(speaker.profile),
- }
- links = ''
- registration = ''
- if talk.registration_required and self.conference.subscriptions_open:
- links += mark_safe("""
- %(link)s""" % {
- 'link': reverse('register-for-a-talk', args=[talk.slug]),
- })
- registration = """
- %(max)s
- %(remain)s""" % {
- 'max': talk.attendees_limit,
- 'remain': talk.remaining_attendees or 0,
- }
- if talk.materials:
- links += mark_safe("""
- %(link)s""" % {
- 'link': talk.materials.url,
- })
- if talk.video:
- links += mark_safe("""
- %(link)s""" % {
- 'link': talk.video,
- })
- days_xml += """
- %(start)s
- %(duration)s
- %(room)s
- %(slug)s
- %(title)s
-
-
- %(type)s
-
- %(abstract)s
- %(description)s
-
-%(persons)s
- %(links)s
- %(registration)s
- \n""" % {
- 'id': talk.id,
- 'start': localtime(talk.start_date).strftime('%H:%M'),
- 'duration': '%02d:%02d' % (talk.estimated_duration / 60, talk.estimated_duration % 60),
- 'room': escape(room.name),
- 'slug': escape(talk.slug),
- 'title': escape(talk.title),
- 'track': escape(talk.track or ''),
- 'type': escape(talk.event.label),
- 'abstract': escape(talk.abstract),
- 'description': escape(talk.description),
- 'persons': persons,
- 'links': links,
- 'registration': registration,
- }
- days_xml += ' \n'
- days_xml += '\n'
-
- return result % {
- 'conference': '\n'.join(map(lambda x: ' ' + x, conference_xml.split('\n'))),
- 'days': '\n'.join(map(lambda x: ' ' + x, days_xml.split('\n'))),
- }
-
- def _as_ics(self):
- if not self.initialized:
- self._lazy_init()
- talks = [ICS_TALK.format(site=self.site, talk=talk) for talk in self.talks]
- return ICS_MAIN.format(site=self.site, talks='\n'.join(talks))
-
- def render(self, output='html'):
- if self.cache:
- cache_entry = 'program.%s.%s' % ('pending' if self.pending else 'final', output)
- result = cache.get(cache_entry)
- if not result:
- result = getattr(self, '_as_%s' % output)()
- cache.set(cache_entry, result, 3 * 60 * 60) # 3H
- return mark_safe(result)
- else:
- return mark_safe(getattr(self, '_as_%s' % output)())
-
- def __str__(self):
- return self.render()
-
-
-# FIXME definitely the wrong place for this, but hey, other templates are already here :P
-
-ICS_MAIN = """BEGIN:VCALENDAR
-PRODID:-//{site.domain}//{site.name}//FR
-X-WR-CALNAME:PonyConf
-X-WR-TIMEZONE:Europe/Paris
-VERSION:2.0
-CALSCALE:GREGORIAN
-METHOD:PUBLISH
-{talks}
-END:VCALENDAR"""
-
-ICS_TALK = """BEGIN:VEVENT
-DTSTART:{talk.dtstart}
-DTEND:{talk.dtend}
-SUMMARY:{talk.title}
-LOCATION:{talk.room}
-STATUS: CONFIRMED
-DESCRIPTION:{talk.abstract}\n---\n\n{talk.description}
-UID:{site.domain}/{talk.id}
-END:VEVENT
-"""
diff --git a/planning/views.py b/planning/views.py
deleted file mode 100644
index 7ca326f..0000000
--- a/planning/views.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from django.shortcuts import render
-from django.contrib.sites.shortcuts import get_current_site
-from django.views.generic import CreateView, DetailView, ListView, UpdateView
-from django.http import HttpResponse, Http404
-
-from ponyconf.mixins import OnSiteFormMixin
-
-from accounts.decorators import orga_required, staff_required
-from accounts.mixins import OrgaRequiredMixin, StaffRequiredMixin
-
-from proposals.models import Talk
-
-from .models import Room
-from .forms import RoomForm
-from .utils import Program
-
-
-class RoomMixin(object):
- def get_queryset(self):
- return Room.objects.filter(site=get_current_site(self.request)).all()
-
-
-class RoomFormMixin(OnSiteFormMixin):
- form_class = RoomForm
-
-
-class RoomList(StaffRequiredMixin, RoomMixin, ListView):
- pass
-
-
-class RoomCreate(OrgaRequiredMixin, RoomMixin, RoomFormMixin, CreateView):
- model = Room
-
-class RoomUpdate(OrgaRequiredMixin, RoomMixin, RoomFormMixin, UpdateView):
- pass
-
-class RoomDetail(StaffRequiredMixin, RoomMixin, DetailView):
- pass
-
-
-@staff_required
-def program_pending(request):
- output = request.GET.get('format', 'html')
- return program(request, pending=True, output=output, html_template='schedule.html', cache=False)
-
-
-def program_public(request):
- output = request.GET.get('format', 'html')
- return program(request, pending=False, output=output)
-
-
-def program(request, pending=False, output='html', html_template='public-program.html', cache=True):
- program = Program(site=get_current_site(request), pending=pending, cache=cache)
- if output == 'html':
- return render(request, 'planning/' + html_template, {'program': program.render('html')})
- elif output == 'xml':
- return HttpResponse(program.render('xml'), content_type="application/xml")
- elif output == 'ics':
- return HttpResponse(program.render('ics'), content_type="text/calendar")
- else:
- raise Http404("Format not available")