Skip to content
Snippets Groups Projects
forms.py 17 KiB
Newer Older
import datetime
from collections import OrderedDict
from django.forms import ModelForm, Form
from django.utils.dates import MONTHS
from web.models.constants import GLOBAL_STUDY_ID
from web.models import StudySubject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink, \
    Availability, Holiday
from web.models.constants import SUBJECT_TYPE_CHOICES
from web.models.worker_study_role import STUDY_ROLE_CHOICES, WorkerStudyRole
from web.views.notifications import get_filter_locations
Jacek Lebioda's avatar
Jacek Lebioda committed
Possible redundancy, but if need arises, contents of forms can be easily customized
DATE_FORMAT_TIME = "%H:%M"
CURRENT_YEAR = datetime.datetime.now().year
YEAR_CHOICES = tuple(range(CURRENT_YEAR, CURRENT_YEAR - 120, -1))
FUTURE_YEAR_CHOICES = tuple(range(CURRENT_YEAR, CURRENT_YEAR + 5, 1))
Jacek Lebioda's avatar
Jacek Lebioda committed
DATEPICKER_DATE_ATTRS = {
    'class': 'datepicker',
    'data-date-format': 'yyyy-mm-dd',
Jacek Lebioda's avatar
Jacek Lebioda committed
    'data-date-orientation': 'bottom'
}
DATETIMEPICKER_DATE_ATTRS = {
    'class': 'datetimepicker',
    'data-date-format': 'Y-MM-DD HH:mm',
TIMEPICKER_DATE_ATTRS = {
    'class': 'datetimepicker',
    'data-date-format': 'HH:mm',
    'data-date-stepping': 15,
}
START_YEAR_STATISTICS = 2015
APPOINTMENT_TYPES_FIELD_POSITION = 1
def get_worker_from_args(kwargs):
    user = kwargs.pop('user', None)
    if user is None:
        raise TypeError("User not defined")
    result = Worker.get_by_user(user)
    if result is None:
        raise TypeError("Worker not defined for: " + user.username)
    return result


class WorkerForm(ModelForm):
    class Meta:
        model = Worker
    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('role_choices', STUDY_ROLE_CHOICES)
        super(WorkerForm, self).__init__(*args, **kwargs)
        self.fields['role'] = forms.ChoiceField(label='Role', choices=choices)
    def save(self, commit=True):
        instance = super(WorkerForm, self).save(commit)
        roles = WorkerStudyRole.objects.filter(worker=instance, study_id=GLOBAL_STUDY_ID)
        if roles.count() > 0:
            roles.update(role=self.cleaned_data['role'])
        else:
            WorkerStudyRole.objects.create(worker=instance, study_id=GLOBAL_STUDY_ID, role=self.cleaned_data['role'])


class AppointmentDetailForm(ModelForm):
    class Meta:
        model = Appointment
        fields = '__all__'
    datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)',
                                        widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
                                        )

class AppointmentEditForm(ModelForm):
    class Meta:
        model = Appointment
        fields = '__all__'
        exclude = ['appointment_types']
    datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)',
                                        widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
                                        )
Jacek Lebioda's avatar
Jacek Lebioda committed

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        if user is None:
            raise TypeError("User not defined")
        self.user = Worker.get_by_user(user)
        if self.user is None:
            raise TypeError("Worker not defined for: " + user.username)
        super(AppointmentEditForm, self).__init__(*args, **kwargs)
        if 'instance' in kwargs:
            initial_appointment_types = AppointmentTypeLink.objects.filter(appointment=kwargs['instance']).values_list(
                'appointment_type', flat=True)
        else:
            initial_appointment_types = []
        fields = OrderedDict()
        for i, tuple in enumerate(self.fields.items()):
            key, value = tuple
            fields[key] = value
            if i == APPOINTMENT_TYPES_FIELD_POSITION:
                fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False,
                                                                             widget=forms.CheckboxSelectMultiple,
                                                                             queryset=AppointmentType.objects.all(),
                                                                             initial=initial_appointment_types)
        self.fields = fields
        self.fields['worker_assigned'].queryset = Worker.objects.filter(
            locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name')
        self.fields['location'].queryset = get_filter_locations(self.user)

    def clean_location(self):
        location = self.cleaned_data['location']
        if self.user.locations.filter(id=location.id).count() == 0:
            self.add_error('location', "You cannot create appointment for this location")
        else:
            return location

    def save(self, commit=True):
        appointment = super(AppointmentEditForm, self).save(commit)
        # if appointment date change, remove appointment_type links
        if 'datetime_when' in self.changed_data:
            AppointmentTypeLink.objects.filter(appointment=appointment).delete()
            appointment_type_links = []
        else:
            appointment_type_links = AppointmentTypeLink.objects.filter(appointment=appointment).all()
        appointment_types_from_links = []
        appointment_types = self.cleaned_data['appointment_types']
        for appointment_type_link in appointment_type_links:
            if appointment_type_link.appointment_type not in appointment_types:
                # we delete appointment links for appointments types that have been removed
                appointment_type_link.delete()
            else:
                appointment_types_from_links.append(appointment_type_link.appointment_type)
        for appointment_type in appointment_types:
            if appointment_type not in appointment_types_from_links:
Valentin Groues's avatar
Valentin Groues committed
                # we create new appointment links for appointments types that have been added
                appointment_type_link = AppointmentTypeLink(appointment=appointment,
                                                            appointment_type=appointment_type)
        return appointment

class AppointmentAddForm(ModelForm):
    class Meta:
        model = Appointment
        exclude = ['status', 'appointment_types']
    datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)',
                                        widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
                                        )
Jacek Lebioda's avatar
Jacek Lebioda committed

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        if user is None:
            raise TypeError("User not defined")
        self.user = Worker.get_by_user(user)
        if self.user is None:
            raise TypeError("Worker not defined for: " + user.username)

        super(ModelForm, self).__init__(*args, **kwargs)
        fields = OrderedDict()
        for i, tuple in enumerate(self.fields.items()):
            key, value = tuple
            fields[key] = value
            if i == APPOINTMENT_TYPES_FIELD_POSITION:
                fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False,
                                                                             widget=forms.CheckboxSelectMultiple,
                                                                             queryset=AppointmentType.objects.all(),
                                                                             )
        self.fields = fields
        self.fields['worker_assigned'].queryset = Worker.objects.filter(
            locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name')
        self.fields['location'].queryset = get_filter_locations(self.user)

    def clean_location(self):
        location = self.cleaned_data['location']
        if self.user.locations.filter(id=location.id).count() == 0:
            self.add_error('location', "You cannot create appointment for this location")
        else:
            return location

    def save(self, commit=True):
        appointment = super(AppointmentAddForm, self).save(commit)
        appointment_types = self.cleaned_data['appointment_types']
        for appointment_type in appointment_types:
            appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type)
            appointment_type_link.save()
        return appointment

class VisitDetailForm(ModelForm):
Jacek Lebioda's avatar
Jacek Lebioda committed
    datetime_begin = forms.DateField(label="Visit begins on",
                                     widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
                                     )
Jacek Lebioda's avatar
Jacek Lebioda committed
    datetime_end = forms.DateField(label="Visit ends on",
                                   widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
                                   )
    post_mail_sent = forms.RadioSelect()
    appointment_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple,
                                                       queryset=AppointmentType.objects.all())
        model = Visit
        exclude = ['is_finished', 'visit_number']
Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed

Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed
class VisitAddForm(ModelForm):
    subject = forms.ModelChoiceField(
        queryset=StudySubject.objects.order_by('subject__last_name', 'subject__first_name'))
Jacek Lebioda's avatar
Jacek Lebioda committed
    datetime_begin = forms.DateField(label="Visit begins on",
                                     widget=forms.TextInput(attrs=DATEPICKER_DATE_ATTRS)
                                     )
Jacek Lebioda's avatar
Jacek Lebioda committed
    datetime_end = forms.DateField(label="Visit ends on",
                                   widget=forms.TextInput(attrs=DATEPICKER_DATE_ATTRS)
                                   )
    appointment_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple,
                                                       queryset=AppointmentType.objects.all())
Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed
    class Meta:
        model = Visit
        exclude = ['is_finished', 'visit_number']

    def clean(self):
        super(VisitAddForm, self).clean()
        if 'datetime_begin' not in self.cleaned_data or 'datetime_end' not in self.cleaned_data:
            return
        if self.cleaned_data['datetime_begin'] >= self.cleaned_data['datetime_end']:
            self.add_error('datetime_begin', "Start date must be before end date")
            self.add_error('datetime_end', "End date must be after start date")
class ContactAttemptForm(ModelForm):
    datetime_when = forms.DateTimeField(label='When? (YYYY-MM-DD HH:MM)',
                                        widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
                                        )

    class Meta:
        model = ContactAttempt
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        if user is None:
            raise TypeError("User not defined")
        self.user = Worker.get_by_user(user)
        if self.user is None:
            raise TypeError("Worker not defined for: " + user.username)
        subject = kwargs.pop('subject', None)
        super(ContactAttemptForm, self).__init__(*args, **kwargs)
        self.fields['subject'].initial = subject.id
        self.fields['subject'].disabled = True
        self.fields['worker'].initial = self.user


class ContactAttemptEditForm(ModelForm):
    datetime_when = forms.DateTimeField(label='When? (YYYY-MM-DD HH:MM)',
                                        widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
                                        )

    class Meta:
        model = ContactAttempt
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        if user is None:
            raise TypeError("User not defined")
        self.user = Worker.get_by_user(user)
        if self.user is None:
            raise TypeError("Worker not defined for: " + user.username)
        super(ContactAttemptEditForm, self).__init__(*args, **kwargs)
        self.fields['subject'].disabled = True


class KitRequestForm(Form):
    start_date = forms.DateField(label="From date",
                                 widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
                                 required=False
                                 )

    end_date = forms.DateField(label="End date",
                               widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
                               required=False
                               )


class StatisticsForm(Form):
    def __init__(self, *args, **kwargs):
        super(StatisticsForm, self).__init__(*args)
        visit_choices = kwargs['visit_choices']
        month = kwargs['month']
        year = kwargs['year']
        now = datetime.datetime.now()
        year_now = now.year
        number_of_years_for_statistics = year_now - START_YEAR_STATISTICS + 2

        year_choices = [(START_YEAR_STATISTICS + i, START_YEAR_STATISTICS + i) for i in
                        range(0, number_of_years_for_statistics + 1)]
        self.fields['month'] = forms.ChoiceField(choices=MONTHS.items(), initial=month)
        self.fields['year'] = forms.ChoiceField(choices=year_choices, initial=year)
        choices = [(-1, "all")]
        choices.extend(SUBJECT_TYPE_CHOICES.items())
        self.fields['subject_type'] = forms.ChoiceField(choices=choices, initial="-1")
        self.fields['visit'] = forms.ChoiceField(choices=visit_choices, initial="-1")


class AvailabilityAddForm(ModelForm):
    available_from = forms.TimeField(label="Available from",
                                     widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS),
                                     initial="8:00",
                                     )
    available_till = forms.TimeField(label="Available until",
                                     widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS),
                                     initial="17:00",
                                     )

    class Meta:
        model = Availability
        fields = '__all__'

    def clean(self):
        worker = Worker.objects.get(id=self.cleaned_data["person"].id)
        availabilities = worker.availability_set.all()
        for availability in availabilities:
            validate_availability_conflict(self, self.cleaned_data, availability)


class AvailabilityEditForm(ModelForm):
    available_from = forms.TimeField(label="Available from",
                                     widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS),
                                     )
    available_till = forms.TimeField(label="Available until",
                                     widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS),
                                     )

    class Meta:
        model = Availability
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(ModelForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance is not None:
            self.availability_id = instance.id
        self.fields['person'].disabled = True

    def clean(self):
        worker = Worker.objects.get(id=self.cleaned_data["person"].id)
        availabilities = worker.availability_set.all()
        for availability in availabilities:
Piotr Gawron's avatar
Piotr Gawron committed
            if availability.id != self.availability_id:
                validate_availability_conflict(self, self.cleaned_data, availability)


def validate_availability_conflict(self, cleaned_data, availability):
    start_hour = self.cleaned_data.get("available_from", None)
    end_hour = self.cleaned_data.get("available_till", None)
    if availability.day_number == self.cleaned_data.get("day_number", None) and \
            ((start_hour <= availability.available_from < end_hour) or
Piotr Gawron's avatar
Piotr Gawron committed
             (start_hour < availability.available_till <= end_hour) or
             (availability.available_from <= start_hour < availability.available_till) or
             (availability.available_from < end_hour <= availability.available_till)):
        error = "User has defined availability for this day that overlaps: " + availability.available_from.strftime(
            DATE_FORMAT_TIME) + ", " + availability.available_till.strftime(DATE_FORMAT_TIME)
        self.add_error('day_number', error)
        self.add_error('available_from', error)
        self.add_error('available_till', error)


class HolidayAddForm(ModelForm):
    datetime_start = forms.DateTimeField(widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
                                         initial=datetime.datetime.now().replace(hour=8, minute=0),
                                         )
    datetime_end = forms.DateTimeField(widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
                                       initial=datetime.datetime.now().replace(hour=17, minute=0),
                                       )

    class Meta:
        model = Holiday
        fields = '__all__'

    def clean(self):
        worker = Worker.objects.get(id=self.cleaned_data["person"].id)
        availabilities = worker.availability_set.all()
        for availability in availabilities:
            validate_availability_conflict(self, self.cleaned_data, availability)