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 handle(self, *args, **options): date_strs = set() slotkind_names = set() room_names = set() slot_details = {} slot_type_count = Counter() conf = current_conference() section = Section.objects.filter(conference=conf, slug="main").all().first() schedule, _created = Schedule.objects.get_or_create(section=section) 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 slotkind_names.add(kind) if rowdate: date = rowdate date_strs.add(date) assert kind, "kind cannot be blank" slot_type_count[(date, kind)] += 1 kindslot = f"{kind} {slot_type_count[(date, kind)]}" for room in rooms.split(';'): room = room.strip() room_names.add(room) slot_details[(date, kindslot, room)] = (start_time, end_time) with open(options[self.TALKS]) as csv_file: csv_reader = csv.reader(csv_file) used_rooms = next(csv_reader) talks = {} assert used_rooms[0] == '', "Cell (1, 1) must be empty" for room in used_rooms[1:]: assert room in room_names, f"Unknown room: {room}" for row in csv_reader: cell = row[0] if cell.rsplit(' ', 1)[0] in slotkind_names: kindslot = cell for i, room in enumerate(used_rooms[1:], 1): talk_id = row[i] if not talk_id: continue assert (date, kindslot, room) in slot_details, f"Slot ({date}, '{kindslot}', '{room}') not found" 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}" days = {} for date in date_strs: day, _created = Day.objects.get_or_create( schedule=schedule, date=date) days[date] = day rooms = {} for room_name in room_names: room, _created = Room.objects.get_or_create( schedule=schedule, name=room_name, order=used_rooms.index(room_name)) rooms[room_name] = room slotkinds = {} for slotkind_name in slotkind_names: slotkind, _created = SlotKind.objects.get_or_create(schedule=schedule, label=slotkind_name) slotkinds[slotkind_name] = slotkind for details, talk_id in talks.items(): date, kindslot, room_name = details kind_name = kindslot.rsplit(' ', 1)[0] (start_time, end_time) = 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: # TODO the exclusive list should not be hard coded, another csv file maybe. exclusive = kind_name in ["plenary", "morning tea", "lunch", "afternoon tea"] preso.slot, _created = Slot.objects.get_or_create( day=days[date], kind=slotkinds[kind_name], start=start_time, end=end_time, exclusive=exclusive) preso.save() slotroom, _create = SlotRoom.objects.get_or_create(slot=preso.slot, room=rooms[room_name])