Files
@ 7fac10241ec7
Branch filter:
Location: symposion_app/pinaxcon/registrasion/management/commands/update_schedule.py - annotation
7fac10241ec7
6.3 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 | 2591a943feec 2591a943feec 92adbb33140e ed21bc03359b 8badb0cb99fa ed21bc03359b 92adbb33140e ed21bc03359b 619a31148786 619a31148786 92adbb33140e 2591a943feec 2591a943feec 2591a943feec a7f26de2febc 2591a943feec 2591a943feec 619a31148786 619a31148786 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 619a31148786 ed21bc03359b 2591a943feec 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 2591a943feec 0d380cd8fc0a ed21bc03359b 619a31148786 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 2591a943feec 619a31148786 0d380cd8fc0a 619a31148786 619a31148786 619a31148786 0d380cd8fc0a 619a31148786 619a31148786 2591a943feec 2591a943feec a7f26de2febc 619a31148786 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 0d380cd8fc0a 283aa5d22bb2 283aa5d22bb2 619a31148786 0d380cd8fc0a 0d380cd8fc0a 2591a943feec ed21bc03359b ed21bc03359b ed21bc03359b 0d380cd8fc0a 619a31148786 0d380cd8fc0a 619a31148786 0d380cd8fc0a 0d380cd8fc0a ed21bc03359b ed21bc03359b ed21bc03359b 619a31148786 0d380cd8fc0a a7f26de2febc 619a31148786 0d380cd8fc0a ed21bc03359b 619a31148786 ed21bc03359b ed21bc03359b 619a31148786 0d380cd8fc0a 0d380cd8fc0a 619a31148786 0d380cd8fc0a 619a31148786 ed21bc03359b ed21bc03359b 619a31148786 ed21bc03359b 619a31148786 ed21bc03359b ed21bc03359b 619a31148786 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a ed21bc03359b 0d380cd8fc0a 0d380cd8fc0a a7f26de2febc 0d380cd8fc0a 0d380cd8fc0a ed21bc03359b 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 20469223cb05 92adbb33140e 92adbb33140e 0d380cd8fc0a 8badb0cb99fa 92adbb33140e 92adbb33140e 92adbb33140e 92adbb33140e 92adbb33140e 92adbb33140e 20469223cb05 20469223cb05 20469223cb05 283aa5d22bb2 20469223cb05 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 20469223cb05 20469223cb05 20469223cb05 92adbb33140e a23f22dd0900 a23f22dd0900 92adbb33140e 20469223cb05 a23f22dd0900 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 283aa5d22bb2 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 0d380cd8fc0a 283aa5d22bb2 | from django.core.management.base import BaseCommand
from symposion.conference.models import Section, current_conference
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Schedule, Slot, SlotRoom)
from symposion.proposals.models import ProposalBase
from dateutil.parser import parse
from collections import Counter
from pathlib import Path
import csv
class Command(BaseCommand):
known_headers = ["date", "start time", "end time", "kind", "rooms"]
SLOTS = 'slots'
TALKS = 'talks'
help = "Updates the schedule based on two csv files, "\
"one that gives all the talk slots, the other the talks."
def add_arguments(self, parser):
parser.add_argument(self.SLOTS, type=Path)
parser.add_argument(self.TALKS, type=Path)
def parse_slots(self, options):
self.date_strs = set()
self.room_names = set()
self.slotkind_names = set()
self.slot_details = {}
slot_type_count = Counter()
with open(options[self.SLOTS]) as csv_file:
csv_reader = csv.reader(csv_file)
headers = next(csv_reader)
assert headers == self.known_headers
for row in csv_reader:
assert len(row) == len(self.known_headers)
rowdate, start_time, end_time, kind, rooms = row
self.slotkind_names.add(kind)
if rowdate:
date = rowdate
self.date_strs.add(date)
assert kind, "kind cannot be blank"
slot_type_count[(date, kind)] += 1
kindslot = f"{kind} {slot_type_count[(date, kind)]}"
if rooms.strip():
for room in rooms.split(';'):
room = room.strip()
self.room_names.add(room)
self.slot_details[(date, kindslot, room)] = (start_time, end_time)
elif self.exclusive(kind):
self.slot_details[(date, kindslot, None)] = (start_time, end_time)
def parse_talks(self, options):
self.talks = {}
with open(options[self.TALKS]) as csv_file:
csv_reader = csv.reader(csv_file)
self.used_rooms = next(csv_reader)
assert self.used_rooms[0] == '', "Cell (1, 1) must be empty"
for room in self.used_rooms[1:]:
assert room in self.room_names, f"Unknown room: {room}"
for row in csv_reader:
cell = row[0]
if cell.rsplit(' ', 1)[0] in self.slotkind_names:
kindslot = cell
for i, room in enumerate(self.used_rooms[1:], 1):
talk_id = row[i]
if not talk_id:
continue
assert (date, kindslot, room) in self.slot_details,\
f"Slot ({date}, '{kindslot}', '{room}') not found"
self.talks[(date, kindslot, room)] = int(talk_id)
else:
assert parse(row[0]), "Not a date: {row[0]}"
date = row[0]
for col in row[1:]:
assert col == '', f"All other columns must be empty: {date}"
def create_simple_models(self):
self.days = {}
for date in self.date_strs:
day, _created = Day.objects.get_or_create(
schedule=self.schedule, date=date)
self.days[date] = day
self.rooms = {}
for room_name in self.room_names:
room, _created = Room.objects.get_or_create(
schedule=self.schedule,
name=room_name,
order=self.used_rooms.index(room_name))
self.rooms[room_name] = room
self.slotkinds = {}
for slotkind_name in self.slotkind_names:
slotkind, _created = SlotKind.objects.get_or_create(
schedule=self.schedule, label=slotkind_name)
self.slotkinds[slotkind_name] = slotkind
def create_complex_models(self):
for details, talk_id in self.talks.items():
date, kindslot, room_name = details
kind_name = kindslot.rsplit(' ', 1)[0]
(start_time, end_time) = self.slot_details[(date, kindslot, room_name)]
proposal = ProposalBase.objects.filter(pk=talk_id).first()
assert proposal, f"Could not find proposal {talk_id}"
preso = Presentation.objects.filter(proposal_base=proposal).first()
assert preso, f"Could not find Presentation for talk {talk_id}"
if not preso.slot:
exclusive = self.exclusive(kind_name)
preso.slot = Slot.objects.create(
day=self.days[date],
kind=self.slotkinds[kind_name],
start=start_time,
end=end_time,
exclusive=exclusive)
# FIXME update the preso.slot, e.g. if the day/time changes
preso.save()
# FIXME what happens if the room changes?
slotroom, _create = SlotRoom.objects.get_or_create(
slot=preso.slot,
room=self.rooms[room_name])
def create_exclusive_models(self):
for (date, kindslot, room) in self.slot_details.keys():
if room is None:
start_time, end_time = self.slot_details[(date, kindslot, room)]
kind_name = kindslot.rsplit(' ', 1)[0]
slot = Slot.objects.create(
day=self.days[date],
kind=self.slotkinds[kind_name],
start=start_time,
end=end_time,
exclusive=True)
slot.save()
def exclusive(self, kind_name):
# TODO the exclusive list should not be hard coded, another csv file maybe.
return kind_name in ["plenary", "morning tea", "lunch", "afternoon tea"]
def handle(self, *args, **options):
self.parse_slots(options)
self.parse_talks(options)
conf = current_conference()
section = Section.objects.filter(conference=conf, slug="main").all().first()
self.schedule, _created = Schedule.objects.get_or_create(section=section)
self.create_simple_models()
self.create_complex_models()
self.create_exclusive_models()
|