From f63b0b9cda89be316b675bc8d81cc41930e55923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lie=20Bouttier?= Date: Mon, 18 Sep 2017 13:41:35 +0200 Subject: [PATCH] remove old planning app --- planning/__init__.py | 0 planning/admin.py | 11 - planning/apps.py | 5 - planning/forms.py | 20 -- planning/migrations/__init__.py | 0 planning/models.py | 38 -- .../templates/planning/public-program.html | 14 - planning/templates/planning/room_detail.html | 37 -- planning/templates/planning/room_form.html | 23 -- planning/templates/planning/room_list.html | 53 --- planning/templates/planning/schedule.html | 13 - planning/tests.py | 3 - planning/urls.py | 14 - planning/utils.py | 328 ------------------ planning/views.py | 61 ---- 15 files changed, 620 deletions(-) delete mode 100644 planning/__init__.py delete mode 100644 planning/admin.py delete mode 100644 planning/apps.py delete mode 100644 planning/forms.py delete mode 100644 planning/migrations/__init__.py delete mode 100644 planning/models.py delete mode 100644 planning/templates/planning/public-program.html delete mode 100644 planning/templates/planning/room_detail.html delete mode 100644 planning/templates/planning/room_form.html delete mode 100644 planning/templates/planning/room_list.html delete mode 100644 planning/templates/planning/schedule.html delete mode 100644 planning/tests.py delete mode 100644 planning/urls.py delete mode 100644 planning/utils.py delete mode 100644 planning/views.py 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 %} -{% empty %} -{% trans "No talks." %} -{% endfor %} - -

{% trans "Unscheduled talks" %}

-{% for talk in room.unscheduled_talks %} -{% if forloop.first %}{% 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 %} -
-

- {{ room }} - -

- {% 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 = """\n%(header)s\n%(body)s\n
""" - 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 - - %(track)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")