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