Files
@ 7fac10241ec7
Branch filter:
Location: symposion_app/vendor/symposion/schedule/forms.py - annotation
7fac10241ec7
6.9 KiB
text/x-python
Improve attendee reports
Display attendee profile data in normal table without DataTables so
sorting is not applied, causing data to be confusing to read.
Include item quantity in attendee data report for accurate schwag packing.
Display attendee profile data in normal table without DataTables so
sorting is not applied, causing data to be confusing to read.
Include item quantity in attendee data report for accurate schwag packing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | ee1f4d92a531 ee1f4d92a531 b154d90eed40 4d1e9cf78e31 ee1f4d92a531 ee1f4d92a531 6d2cc584cfae ee1f4d92a531 4d1e9cf78e31 7c102aefa3a5 6d2cc584cfae 4d1e9cf78e31 97d55b8876f9 6d2cc584cfae 6d2cc584cfae 6d2cc584cfae 36ab6d599ffc fb5eaea880cb fb5eaea880cb 7c102aefa3a5 19d826ad00de 7c102aefa3a5 c582b2b3e23e c582b2b3e23e 19d826ad00de 19d826ad00de 19d826ad00de 36ab6d599ffc 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 7c102aefa3a5 7c102aefa3a5 19d826ad00de 19d826ad00de 19d826ad00de 36ab6d599ffc 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 19d826ad00de 5b93b39f67e0 19d826ad00de 19d826ad00de 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 fb5eaea880cb fb5eaea880cb fb5eaea880cb 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 b154d90eed40 b154d90eed40 b154d90eed40 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 3239bbe29a41 3239bbe29a41 3239bbe29a41 3239bbe29a41 3239bbe29a41 3239bbe29a41 3239bbe29a41 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 b154d90eed40 b154d90eed40 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 62289cad36c5 62289cad36c5 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 e6da1641935a 97d55b8876f9 97d55b8876f9 97d55b8876f9 97d55b8876f9 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 650ddd2bcc36 | import csv
import time
from io import TextIOWrapper
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 symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
SlotRoom, ProposalBase)
class SlotEditForm(forms.Form):
required_css_class = 'label-required'
def __init__(self, *args, **kwargs):
self.slot = kwargs.pop("slot")
super(SlotEditForm, self).__init__(*args, **kwargs)
# @@@ TODO - Make this configurable
if self.slot.kind.label in ["talk", "tutorial", "keynote"]:
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()
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)
kwargs["required"] = True
kwargs["queryset"] = queryset
return forms.ModelChoiceField(**kwargs)
def build_content_override_field(self):
kwargs = {
"label": "Content",
"required": False,
"initial": self.slot.content_override,
"widget": forms.Textarea,
}
return forms.CharField(**kwargs)
class ScheduleSectionForm(forms.Form):
required_css_class = 'label-required'
ROOM_KEY = 'room'
DATE_KEY = 'date'
START_KEY = 'time_start'
END_KEY = 'time_end'
EXCLUSIVE = 'exclusive'
PROPOSAL = 'proposal_id'
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')
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
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")
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)
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")
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]))
)
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]))
)
created_items.append(slot)
try:
with transaction.atomic():
SlotRoom.objects.create(slot=slot, room=room)
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
)
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.'
|