Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
forms.py 15.23 KiB
import datetime
from collections import OrderedDict

from django import forms
from django.forms import ModelForm, Form
from django.utils.dates import MONTHS

from models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink
from models.constants import SUBJECT_TYPE_CHOICES, SCREENING_NUMBER_PREFIXES_FOR_TYPE

"""
Possible redundancy, but if need arises, contents of forms can be easily customized
"""

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))
DATEPICKER_DATE_ATTRS = {
    'class': 'datepicker',
    'data-date-format': 'yyyy-mm-dd',
    'data-date-orientation': 'bottom'
}
DATETIMEPICKER_DATE_ATTRS = {
    'class': 'datetimepicker',
    'data-date-format': 'Y-MM-DD HH:mm',
}
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")


class SubjectAddForm(ModelForm):
    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)
        return 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)
        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
                                                    )
    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

    def clean(self):
        validate_subject_nd_number(self, self.cleaned_data)

    class Meta:
        model = Subject
        fields = '__all__'


class WorkerAddForm(ModelForm):
    class Meta:
        model = Worker
        exclude = ['appointments']


class WorkerDetailForm(ModelForm):
    class Meta:
        model = Worker
        fields = '__all__'


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

    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

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

    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

    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):
    datetime_begin = forms.DateField(label="Visit begins on",
                                     widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
                                     )
    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())

    class Meta:
        model = Visit
        exclude = ['is_finished']


class VisitAddForm(ModelForm):
    subject = forms.ModelChoiceField(queryset=Subject.objects.order_by('last_name', 'first_name'))
    datetime_begin = forms.DateField(label="Visit begins on",
                                     widget=forms.TextInput(attrs=DATEPICKER_DATE_ATTRS)
                                     )
    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())

    class Meta:
        model = Visit
        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")