work on #6, needs tests
This commit is contained in:
parent
2f75033dc5
commit
4d1e9cf78e
3 changed files with 152 additions and 7 deletions
|
@ -1,9 +1,17 @@
|
||||||
|
import csv
|
||||||
|
import time
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.db import IntegrityError, transaction
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from markitup.widgets import MarkItUpWidget
|
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):
|
class SlotEditForm(forms.Form):
|
||||||
|
@ -40,3 +48,106 @@ class SlotEditForm(forms.Form):
|
||||||
"initial": self.slot.content_override,
|
"initial": self.slot.content_override,
|
||||||
}
|
}
|
||||||
return forms.CharField(**kwargs)
|
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.template import loader, Context
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
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.models import Schedule, Day, Slot, Presentation
|
||||||
from symposion.schedule.timetable import TimeTable
|
from symposion.schedule.timetable import TimeTable
|
||||||
|
|
||||||
|
@ -100,11 +101,24 @@ def schedule_edit(request, slug=None):
|
||||||
|
|
||||||
schedule = fetch_schedule(slug)
|
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_qs = Day.objects.filter(schedule=schedule)
|
||||||
days = [TimeTable(day) for day in days_qs]
|
days = [TimeTable(day) for day in days_qs]
|
||||||
ctx = {
|
ctx = {
|
||||||
"schedule": schedule,
|
"schedule": schedule,
|
||||||
"days": days,
|
"days": days,
|
||||||
|
"form": form
|
||||||
}
|
}
|
||||||
return render(request, "schedule/schedule_edit.html", ctx)
|
return render(request, "schedule/schedule_edit.html", ctx)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,13 @@
|
||||||
{% include "schedule/_edit_grid.html" %}
|
{% include "schedule/_edit_grid.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</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 class="modal fade hide in" id="slotEditModal"></div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -41,5 +47,19 @@
|
||||||
e.preventDefault();
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in a new issue