diff --git a/accounts/signals.py b/accounts/signals.py
index dd50329..85726f9 100644
--- a/accounts/signals.py
+++ b/accounts/signals.py
@@ -7,6 +7,8 @@ from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_noop
+from ponyconf.decorators import disable_for_loaddata
+
from .models import Connector, Participation, Profile, Transport
@@ -36,8 +38,8 @@ def on_user_logged_out(sender, request, **kwargs):
messages.success(request, _('Goodbye!'), fail_silently=True) # FIXME
+@receiver(post_save, sender=User, weak=False, dispatch_uid='create_profile')
+@disable_for_loaddata
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
-
-post_save.connect(create_profile, sender=User, weak=False, dispatch_uid='create_profile')
diff --git a/conversations/signals.py b/conversations/signals.py
index 27fc328..e3c7d4c 100644
--- a/conversations/signals.py
+++ b/conversations/signals.py
@@ -3,6 +3,7 @@ from django.db.models import Q
from django.db.models.signals import post_save
from django.dispatch import receiver
+from ponyconf.decorators import disable_for_loaddata
from accounts.models import Participation
from proposals.models import Talk
from proposals.signals import talk_added, talk_edited
@@ -11,6 +12,7 @@ from .models import ConversationAboutTalk, ConversationWithParticipant, Message
@receiver(post_save, sender=Participation, dispatch_uid="Create ConversationWithParticipant")
+@disable_for_loaddata
def create_conversation_with_participant(sender, instance, created, **kwargs):
if not created:
return
@@ -19,6 +21,7 @@ def create_conversation_with_participant(sender, instance, created, **kwargs):
@receiver(post_save, sender=Talk, dispatch_uid="Create ConversationAboutTalk")
+@disable_for_loaddata
def create_conversation_about_talk(sender, instance, created, **kwargs):
if not created:
return
@@ -53,6 +56,7 @@ def notify_talk_edited(sender, instance, author, **kwargs):
@receiver(post_save, sender=Message, dispatch_uid="Notify new message")
+@disable_for_loaddata
def notify_new_message(sender, instance, created, **kwargs):
if not created:
# Possibly send a modification notification?
diff --git a/planning/utils.py b/planning/utils.py
index 8591dcd..a873ebe 100644
--- a/planning/utils.py
+++ b/planning/utils.py
@@ -10,7 +10,7 @@ from itertools import islice
from .models import Room
-from proposals.models import Talk
+from proposals.models import Conference, Talk
Event = namedtuple('Event', ['talk', 'row', 'rowcount'])
@@ -18,6 +18,7 @@ Event = namedtuple('Event', ['talk', 'row', 'rowcount'])
class Program:
def __init__(self, site, empty_rooms=False, talk_filter=None):
+ self.site = site
self.talks = Talk.objects.\
filter(site=site, room__isnull=False, start_date__isnull=False).\
filter(Q(duration__gt=0) | Q(event__duration__gt=0)).\
@@ -49,17 +50,6 @@ class Program:
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)
-
def _add_talk(self, talk):
room = talk.room
dt1 = talk.start_date
@@ -150,9 +140,111 @@ class Program:
timeslot = '
%s – %s | ' % tuple(map(date_to_string, [start, end]))
return style, timeslot
- def __str__(self):
+ def as_html(self):
template = """"""
+
+ 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)
+
return mark_safe(template % {
'header': self._header(),
'body': self._body(),
})
+
+ def as_xml(self):
+ result = """
+
+%(conference)s
+%(days)s
+
+"""
+
+ if not len(self.days):
+ return result % {'conference': '', 'days': ''}
+
+ conference = Conference.objects.get(site=self.site)
+ 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(conference.venue.split('\n')),
+ 'city': 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),
+ }
+ days_xml += """
+ %(start)s
+ %(duration)s
+ %(room)s
+ %(slug)s
+ %(title)s
+
+
+ %(type)s
+
+ %(abstract)s
+ %(description)s
+
+%(persons)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),
+ 'type': escape(talk.event.name),
+ 'abstract': escape(talk.abstract),
+ 'description': escape(talk.description),
+ 'persons': persons,
+ }
+ 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 __str__(self):
+ return self.as_html()
diff --git a/planning/views.py b/planning/views.py
index 839b9b8..9fedbae 100644
--- a/planning/views.py
+++ b/planning/views.py
@@ -1,6 +1,7 @@
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
from ponyconf.mixins import OnSiteFormMixin
@@ -40,6 +41,10 @@ class RoomDetail(StaffRequiredMixin, RoomMixin, DetailView):
@staff_required
def program(request):
program = Program(site=get_current_site(request))
- return render(request, 'planning/program.html', {
- 'program': program,
- })
+ f = request.GET.get('format')
+ if f == 'xml':
+ return HttpResponse(program.as_xml(), content_type="application/xml")
+ else:
+ return render(request, 'planning/program.html', {
+ 'program': program,
+ })
diff --git a/ponyconf/decorators.py b/ponyconf/decorators.py
new file mode 100644
index 0000000..0399d79
--- /dev/null
+++ b/ponyconf/decorators.py
@@ -0,0 +1,10 @@
+from functools import wraps
+
+
+def disable_for_loaddata(signal_handler):
+ @wraps(signal_handler)
+ def wrapper(*args, **kwargs):
+ if kwargs.get('raw'):
+ return
+ signal_handler(*args, **kwargs)
+ return wrapper
diff --git a/proposals/forms.py b/proposals/forms.py
index ca44b7a..d0620c4 100644
--- a/proposals/forms.py
+++ b/proposals/forms.py
@@ -179,4 +179,10 @@ class TrackForm(forms.ModelForm):
return name
-ConferenceForm = modelform_factory(Conference, fields=['cfp_opening_date', 'cfp_closing_date', 'home'])
+ConferenceForm = modelform_factory(Conference,
+ fields=['cfp_opening_date', 'cfp_closing_date', 'venue', 'city', 'home'],
+ widgets={
+ 'cfp_opening_date': forms.TextInput(),
+ 'cfp_closing_date': forms.TextInput(),
+ 'venue': forms.Textarea(attrs={'rows': 4}),
+ })
diff --git a/proposals/migrations/0024_auto_20161024_1313.py b/proposals/migrations/0024_auto_20161024_1313.py
new file mode 100644
index 0000000..2ba4eb7
--- /dev/null
+++ b/proposals/migrations/0024_auto_20161024_1313.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.1 on 2016-10-24 13:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('proposals', '0023_merge_20161019_2040'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='conference',
+ name='city',
+ field=models.CharField(blank=True, default='', max_length=64),
+ ),
+ migrations.AddField(
+ model_name='conference',
+ name='venue',
+ field=models.TextField(blank=True, default=''),
+ ),
+ ]
diff --git a/proposals/models.py b/proposals/models.py
index 921d407..78ca1e8 100644
--- a/proposals/models.py
+++ b/proposals/models.py
@@ -24,6 +24,8 @@ class Conference(models.Model):
site = models.OneToOneField(Site, on_delete=models.CASCADE)
home = models.TextField(blank=True, default="")
+ venue = models.TextField(blank=True, default="")
+ city = models.CharField(max_length=64, blank=True, default="")
cfp_opening_date = models.DateTimeField(null=True, blank=True, default=None)
cfp_closing_date = models.DateTimeField(null=True, blank=True, default=None)
diff --git a/proposals/signals.py b/proposals/signals.py
index 8abd801..42cb775 100644
--- a/proposals/signals.py
+++ b/proposals/signals.py
@@ -4,6 +4,7 @@ from django.contrib.sites.models import Site
from django.utils.translation import ugettext_noop
from django.conf import settings
+from ponyconf.decorators import disable_for_loaddata
from accounts.models import Participation
from .models import Conference, Talk, Topic, Event
@@ -14,11 +15,13 @@ talk_edited = Signal(providing_args=["sender", "instance", "author"])
@receiver(post_save, sender=Site, dispatch_uid="Create Conference for Site")
+@disable_for_loaddata
def create_conference(sender, instance, **kwargs):
Conference.objects.get_or_create(site=instance)
@receiver(post_save, sender=Site, dispatch_uid="Create default events type for Site")
+@disable_for_loaddata
def create_events(sender, instance, **kwargs):
if not Event.objects.filter(site=instance).exists():
Event.objects.bulk_create([
@@ -36,7 +39,7 @@ def call_first_site_post_save(apps, **kwargs):
site.first().save()
-@receiver(m2m_changed, sender=Talk.speakers.through, dispatch_uid="Create Participation for speakers")
+#@receiver(m2m_changed, sender=Talk.speakers.through, dispatch_uid="Create Participation for speakers")
def create_participation_for_speakers(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if action != "pre_add":
pass
@@ -44,7 +47,7 @@ def create_participation_for_speakers(sender, instance, action, reverse, model,
Participation.objects.get_or_create(user=speaker, site=instance.site)
-@receiver(m2m_changed, sender=Topic.reviewers.through, dispatch_uid="Create Participation for reviewers")
+#@receiver(m2m_changed, sender=Topic.reviewers.through, dispatch_uid="Create Participation for reviewers")
def create_participation_for_reviewers(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if action != "pre_add":
pass
diff --git a/proposals/templates/proposals/conference.html b/proposals/templates/proposals/conference.html
index ebeb333..0b5dbac 100644
--- a/proposals/templates/proposals/conference.html
+++ b/proposals/templates/proposals/conference.html
@@ -15,6 +15,7 @@
{% csrf_token %}
{% bootstrap_field form.cfp_opening_date addon_after='' %}
{% bootstrap_field form.cfp_closing_date addon_after='' %}
+ {% bootstrap_form form exclude='cfp_opening_date,cfp_closing_date,home' %}
- Editor
- Preview
diff --git a/volunteers/migrations/0003_auto_20161024_1313.py b/volunteers/migrations/0003_auto_20161024_1313.py
new file mode 100644
index 0000000..12905a6
--- /dev/null
+++ b/volunteers/migrations/0003_auto_20161024_1313.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.1 on 2016-10-24 13:13
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('volunteers', '0002_activity_slug'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='activity',
+ name='participants',
+ field=models.ManyToManyField(blank=True, related_name='activities', to=settings.AUTH_USER_MODEL, verbose_name='Participants'),
+ ),
+ ]