Skip to content
Snippets Groups Projects
forms.py 20 KiB
Newer Older
import datetime
from collections import OrderedDict
from django.forms import ModelForm, Form
from django.utils.dates import MONTHS
from models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink, \
    Availability, Holiday
from models.constants import SUBJECT_TYPE_CHOICES, SCREENING_NUMBER_PREFIXES_FOR_TYPE
from 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 validate_subject_nd_number(self, cleaned_data):
    if cleaned_data['nd_number'] != "":
        subjects_from_db = Subject.objects.filter(nd_number=cleaned_data['nd_number'])
        if subjects_from_db:
            if subjects_from_db[0].screening_number != cleaned_data.get('screening_number', ''):
                self.add_error('nd_number', "ND number already in use")
def validate_subject_mpower_number(self, cleaned_data):
    if cleaned_data['mpower_id'] != "":
        subjects_from_db = Subject.objects.filter(mpower_id=cleaned_data['mpower_id'])
        if subjects_from_db:
            if subjects_from_db[0].screening_number != cleaned_data.get('screening_number', ''):
                self.add_error('mpower_id', "mPower number already in use")


class SubjectAddForm(ModelForm):
Jacek Lebioda's avatar
Jacek Lebioda committed
    date_born = forms.DateField(label="Date of birth",
                                widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
                                required=False
                                )
    datetime_contact_reminder = forms.DateTimeField(label="Contact on",
                                                    widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
                                                    required=False
                                                    )
    class Meta:
        model = Subject
        fields = '__all__'
        exclude = ['dead', 'resigned']
    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)
        self.fields['screening_number'].required = False

    def build_screening_number(self, cleaned_data):
        screening_number = cleaned_data.get('screening_number', None)
        if not screening_number:
            prefix_screening_number = self.get_prefix_screening_number()
            if prefix_screening_number is not None:
                screening_number = get_new_screening_number(prefix_screening_number)
    def clean(self):
        cleaned_data = super(SubjectAddForm, self).clean()
        screening_number = self.build_screening_number(cleaned_data)
        if screening_number is not None:
            cleaned_data['screening_number'] = screening_number
            subjects_from_db = Subject.objects.filter(screening_number=screening_number)

            if len(subjects_from_db) > 0:
                self.add_error('screening_number', "Screening number already in use")
        validate_subject_nd_number(self, cleaned_data)
        validate_subject_mpower_number(self, cleaned_data)
        return cleaned_data

    def get_prefix_screening_number(self):
        default_location = self.cleaned_data.get('default_location', None)
        screening_number_prefix = None
        if default_location is not None and default_location.prefix:
            screening_number_prefix = default_location.prefix
        else:
            subject_type = self.cleaned_data.get('type', None)
            if subject_type is not None:
                screening_number_prefix = SCREENING_NUMBER_PREFIXES_FOR_TYPE[subject_type]
        if screening_number_prefix is None:
            return None
        prefix_screening_number = screening_number_prefix + "-"
        return prefix_screening_number
def get_new_screening_number(screening_number_prefix):
    result_number = 0
    subjects = Subject.objects.filter(screening_number__contains=screening_number_prefix)
    for subject in subjects:
        screening_numbers = subject.screening_number.split(";")
        for screening_number in screening_numbers:
            screening_number = screening_number.strip()
            if screening_number.startswith(screening_number_prefix):
                number = screening_number[len(screening_number_prefix):]
                try:
                    result_number = max(result_number, int(number))
                except ValueError:
                    pass

    return screening_number_prefix + str(result_number + 1).zfill(3)

class SubjectDetailForm(ModelForm):
    class Meta:
        model = Subject
        fields = '__all__'

class SubjectEditForm(ModelForm):
    datetime_contact_reminder = forms.DateTimeField(label="Contact on",
                                                    widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
                                                    required=False
                                                    )
Jacek Lebioda's avatar
Jacek Lebioda committed
    date_born = forms.DateField(label="Date of birth",
                                widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
                                required=False
                                )
    def __init__(self, *args, **kwargs):
        was_dead = kwargs.get('was_dead', False)
        was_resigned = kwargs.get('was_resigned', False)
        if 'was_resigned' in kwargs:
            kwargs.pop('was_resigned')
        if 'was_dead' in kwargs:
            kwargs.pop('was_dead')
        super(SubjectEditForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['screening_number'].widget.attrs['readonly'] = True
        if was_dead:
            self.fields['dead'].disabled = True
        if was_resigned:
            self.fields['resigned'].disabled = True
        validate_subject_nd_number(self, self.cleaned_data)
        validate_subject_mpower_number(self, self.cleaned_data)
    class Meta:
        model = Subject
        fields = '__all__'
class WorkerAddForm(ModelForm):
    class Meta:
        model = Worker
class WorkerEditForm(ModelForm):
    class Meta:
        model = Worker
        fields = '__all__'


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(ModelForm, 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)
        AppointmentTypeLink.objects.filter(appointment=appointment).delete()
        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 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
Jacek Lebioda's avatar
Jacek Lebioda committed
        exclude = ['is_finished']
Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed

Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed
class VisitAddForm(ModelForm):
    subject = forms.ModelChoiceField(queryset=Subject.objects.order_by('last_name', '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
Piotr Matyjaszyk's avatar
Piotr Matyjaszyk committed
        exclude = ['is_finished']

    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 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:
            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
                 (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)