work on #6, needs tests
This commit is contained in:
parent
2f75033dc5
commit
4d1e9cf78e
3 changed files with 152 additions and 7 deletions
|
@ -1,13 +1,21 @@
|
|||
import csv
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import Q
|
||||
|
||||
from markitup.widgets import MarkItUpWidget
|
||||
|
||||
from symposion.schedule.models import Presentation
|
||||
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
|
||||
SlotRoom)
|
||||
|
||||
|
||||
class SlotEditForm(forms.Form):
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.slot = kwargs.pop("slot")
|
||||
super(SlotEditForm, self).__init__(*args, **kwargs)
|
||||
|
@ -16,7 +24,7 @@ class SlotEditForm(forms.Form):
|
|||
self.fields["presentation"] = self.build_presentation_field()
|
||||
else:
|
||||
self.fields["content_override"] = self.build_content_override_field()
|
||||
|
||||
|
||||
def build_presentation_field(self):
|
||||
kwargs = {}
|
||||
queryset = Presentation.objects.all()
|
||||
|
@ -31,7 +39,7 @@ class SlotEditForm(forms.Form):
|
|||
kwargs["required"] = True
|
||||
kwargs["queryset"] = queryset
|
||||
return forms.ModelChoiceField(**kwargs)
|
||||
|
||||
|
||||
def build_content_override_field(self):
|
||||
kwargs = {
|
||||
"label": "Content",
|
||||
|
@ -40,3 +48,106 @@ class SlotEditForm(forms.Form):
|
|||
"initial": self.slot.content_override,
|
||||
}
|
||||
return forms.CharField(**kwargs)
|
||||
|
||||
|
||||
class ScheduleSectionForm(forms.Form):
|
||||
ROOM_KEY = 'room'
|
||||
DATE_KEY = 'date'
|
||||
START_KEY = 'time_start'
|
||||
END_KEY = 'time_end'
|
||||
KIND = 'kind'
|
||||
|
||||
filename = forms.FileField(
|
||||
label='Select a CSV file to import:',
|
||||
required=False
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.schedule = kwargs.pop("schedule")
|
||||
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"
|
||||
start_time = time.strptime(data[self.START_KEY], '%I:%M %p')
|
||||
start = datetime(100, 1, 1, start_time.tm_hour, start_time.tm_min, 00)
|
||||
end_time = time.strptime(data[self.END_KEY], '%I:%M %p')
|
||||
end = datetime(100, 1, 1, end_time.tm_hour, end_time.tm_min, 00)
|
||||
return start.time(), end.time()
|
||||
|
||||
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):
|
||||
room, created = Room.objects.get_or_create(
|
||||
schedule=self.schedule, name=room, order=i
|
||||
)
|
||||
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:
|
||||
date = datetime.strptime(day, "%m/%d/%Y")
|
||||
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 = []
|
||||
reader = csv.DictReader(self.cleaned_data.get('filename'))
|
||||
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], "%m/%d/%Y")
|
||||
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
|
||||
)
|
||||
if created:
|
||||
created_items.append(slot)
|
||||
else:
|
||||
slot = Slot.objects.create(
|
||||
kind=slot_kind, day=day, start=start, end=end
|
||||
)
|
||||
created_items.append(slot)
|
||||
try:
|
||||
SlotRoom.objects.create(slot=slot, room=room)
|
||||
except IntegrityError:
|
||||
transaction.rollback()
|
||||
# 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.'
|
||||
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.'
|
||||
|
|
|
@ -3,8 +3,9 @@ from django.shortcuts import render, get_object_or_404, redirect
|
|||
from django.template import loader, Context
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
|
||||
from symposion.schedule.forms import SlotEditForm
|
||||
from symposion.schedule.forms import SlotEditForm, ScheduleSectionForm
|
||||
from symposion.schedule.models import Schedule, Day, Slot, Presentation
|
||||
from symposion.schedule.timetable import TimeTable
|
||||
|
||||
|
@ -100,11 +101,24 @@ def schedule_edit(request, slug=None):
|
|||
|
||||
schedule = fetch_schedule(slug)
|
||||
|
||||
if request.method == "POST":
|
||||
form = ScheduleSectionForm(
|
||||
request.POST, request.FILES, schedule=schedule
|
||||
)
|
||||
if form.is_valid():
|
||||
if 'submit' in form.data:
|
||||
msg = form.build_schedule()
|
||||
elif 'delete' in form.data:
|
||||
msg = form.delete_schedule()
|
||||
messages.add_message(request, msg[0], msg[1])
|
||||
else:
|
||||
form = ScheduleSectionForm(schedule=schedule)
|
||||
days_qs = Day.objects.filter(schedule=schedule)
|
||||
days = [TimeTable(day) for day in days_qs]
|
||||
ctx = {
|
||||
"schedule": schedule,
|
||||
"days": days,
|
||||
"form": form
|
||||
}
|
||||
return render(request, "schedule/schedule_edit.html", ctx)
|
||||
|
||||
|
|
|
@ -18,13 +18,19 @@
|
|||
<div class="row">
|
||||
<div class="span12">
|
||||
<h1>Schedule Edit</h1>
|
||||
|
||||
|
||||
{% for timetable in days %}
|
||||
<h2>{{ timetable.day.date }}</h2>
|
||||
{% include "schedule/_edit_grid.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if request.user.is_staff %}
|
||||
<form id="schedule-builder" action="." method="post" enctype="multipart/form-data">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" name="submit" value="Submit" />
|
||||
<input type="submit" id="delete" name="delete" value="Delete Schedule" />
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="modal fade hide in" id="slotEditModal"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -41,5 +47,19 @@
|
|||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
$(function() {
|
||||
//submit event handler
|
||||
$("form#schedule-builder :submit").click(function(e) {
|
||||
var name = this.name;
|
||||
if(name == 'delete') {
|
||||
if (!confirm("Are you sure you want to delete the schedule?"))
|
||||
{
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue