Files @ 252697b842c0
Branch filter:

Location: symposion_app/vendor/symposion/schedule/forms.py - annotation

Joel Addison
Update to Django 2.2

Upgrade site and modules to Django 2.2. Remove and replace obsolete
functionality with current equivalents. Update requirements to latest
versions where possible. Remove unused dependencies.
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
252697b842c0
252697b842c0
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')
        else:
            self.encoding = "utf-8"
        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.'