symposion_app/vendor/symposion/schedule/forms.py

190 lines
6.9 KiB
Python
Raw Normal View History

import csv
import time
from io import TextIOWrapper
2014-02-28 15:55:54 +00:00
from datetime import datetime
2012-08-31 03:24:08 +00:00
from django import forms
from django.contrib import messages
2014-02-28 15:55:54 +00:00
from django.db import IntegrityError, transaction
from django.db.models import Q
2012-08-31 03:24:08 +00:00
2014-02-28 15:55:54 +00:00
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
SlotRoom, ProposalBase)
2012-08-31 03:24:08 +00:00
class SlotEditForm(forms.Form):
2014-07-30 18:19:26 +00:00
required_css_class = 'label-required'
def __init__(self, *args, **kwargs):
2012-10-26 21:32:03 +00:00
self.slot = kwargs.pop("slot")
super(SlotEditForm, self).__init__(*args, **kwargs)
# @@@ TODO - Make this configurable
if self.slot.kind.label in ["talk", "tutorial", "keynote"]:
2012-10-26 21:32:03 +00:00
self.fields["presentation"] = self.build_presentation_field()
else:
self.fields["content_override"] = self.build_content_override_field()
2014-07-30 18:19:26 +00:00
2012-10-26 21:32:03 +00:00
def build_presentation_field(self):
kwargs = {}
queryset = Presentation.objects.all()
queryset = queryset.exclude(cancelled=True)
queryset = queryset.order_by("proposal_base__pk")
if self.slot.content:
queryset = queryset.filter(Q(slot=None) | Q(pk=self.slot.content.pk))
kwargs["required"] = False
kwargs["initial"] = self.slot.content
else:
queryset = queryset.filter(slot=None)
2012-10-26 21:32:03 +00:00
kwargs["required"] = True
kwargs["queryset"] = queryset
return forms.ModelChoiceField(**kwargs)
2014-07-30 18:19:26 +00:00
2012-10-26 21:32:03 +00:00
def build_content_override_field(self):
kwargs = {
"label": "Content",
"required": False,
"initial": self.slot.content_override,
"widget": forms.Textarea,
2012-10-26 21:32:03 +00:00
}
return forms.CharField(**kwargs)
2014-03-03 18:47:46 +00:00
class ScheduleSectionForm(forms.Form):
required_css_class = 'label-required'
2014-03-03 18:47:46 +00:00
ROOM_KEY = 'room'
DATE_KEY = 'date'
START_KEY = 'time_start'
END_KEY = 'time_end'
EXCLUSIVE = 'exclusive'
PROPOSAL = 'proposal_id'
2014-03-03 18:47:46 +00:00
KIND = 'kind'
filename = forms.FileField(
label='Select a CSV file to import:',
required=False
)
def __init__(self, *args, **kwargs):
self.schedule = kwargs.pop("schedule")
if 'encoding' in kwargs:
self.encoding = kwargs['encoding']
kwargs.pop('encoding')
2014-03-03 18:47:46 +00:00
super(ScheduleSectionForm, self).__init__(*args, **kwargs)
def clean_filename(self):
if 'submit' in self.data:
fname = self.cleaned_data.get('filename')
if not fname or not fname.name.endswith('.csv'):
raise forms.ValidationError(u'Please upload a .csv file')
return fname
def _get_start_end_times(self, data):
"Return start and end time objects"
times = []
for x in [data[self.START_KEY], data[self.END_KEY]]:
try:
time_obj = time.strptime(x, '%I:%M %p')
except:
return messages.ERROR, u'Malformed time found: %s.' % x
time_obj = datetime(100, 1, 1, time_obj.tm_hour, time_obj.tm_min, 00)
times.append(time_obj.time())
return times
def _build_rooms(self, data):
"Get or Create Rooms based on schedule type and set of Tracks"
created_rooms = []
rooms = sorted(set([x[self.ROOM_KEY] for x in data]))
for i, room in enumerate(rooms):
try:
room = Room.objects.get(schedule=self.schedule, name=room)
created = False
except Room.DoesNotExist:
room = Room.objects.create(
schedule=self.schedule, name=room, order=i
)
created = True
2014-03-03 18:47:46 +00:00
if created:
created_rooms.append(room)
return created_rooms
def _build_days(self, data):
"Get or Create Days based on schedule type and set of Days"
created_days = []
days = set([x[self.DATE_KEY] for x in data])
for day in days:
try:
date = datetime.strptime(day, "%Y-%m-%d")
2014-03-03 18:47:46 +00:00
except ValueError:
[x.delete() for x in created_days]
return messages.ERROR, u'Malformed data found: %s.' % day
day, created = Day.objects.get_or_create(
schedule=self.schedule, date=date
)
if created:
created_days.append(day)
return created_days
def build_schedule(self):
created_items = []
f = TextIOWrapper(self.cleaned_data.get('filename'), encoding=self.encoding)
reader = csv.DictReader(f)
2014-03-03 18:47:46 +00:00
data = [dict((k.strip(), v.strip()) for k, v in x.items()) for x in reader]
# build rooms
created_items.extend(self._build_rooms(data))
# build_days
created_items.extend(self._build_days(data))
# build Slot -> SlotRoom
for row in data:
room = Room.objects.get(
schedule=self.schedule, name=row[self.ROOM_KEY]
)
date = datetime.strptime(row[self.DATE_KEY], "%Y-%m-%d")
2014-03-03 18:47:46 +00:00
day = Day.objects.get(schedule=self.schedule, date=date)
start, end = self._get_start_end_times(row)
slot_kind, created = SlotKind.objects.get_or_create(
label=row[self.KIND], schedule=self.schedule
)
if created:
created_items.append(slot_kind)
if row[self.KIND] == 'plenary':
slot, created = Slot.objects.get_or_create(
kind=slot_kind, day=day, start=start, end=end, exclusive=bool(int(row[self.EXCLUSIVE]))
2014-03-03 18:47:46 +00:00
)
if created:
created_items.append(slot)
else:
slot = Slot.objects.create(
kind=slot_kind, day=day, start=start, end=end, exclusive=bool(int(row[self.EXCLUSIVE]))
2014-03-03 18:47:46 +00:00
)
created_items.append(slot)
try:
2014-03-03 18:47:46 +00:00
with transaction.atomic():
SlotRoom.objects.create(slot=slot, room=room)
2014-03-03 18:47:46 +00:00
except IntegrityError:
# delete all created objects and report error
for x in created_items:
x.delete()
return messages.ERROR, u'An overlap occurred; the import was cancelled.'
if row[self.PROPOSAL]:
proposal = ProposalBase.objects.get(id=row[self.PROPOSAL])
Presentation.objects.get_or_create(
slot=slot,
section_id=1,
proposal_base=proposal,
speaker=proposal.speaker,
additional_speakers=proposal.additional_speakers,
title=proposal.title,
abstract=proposal.abstract
)
2014-03-03 18:47:46 +00:00
return messages.SUCCESS, u'Your schedule has been imported.'
def delete_schedule(self):
self.schedule.day_set.all().delete()
return messages.SUCCESS, u'Your schedule has been deleted.'