Skip to content
Snippets Groups Projects
forms.py 22.3 KiB
Newer Older
import datetime
from collections import OrderedDict
from django.forms import ModelForm, Form
from django.utils.dates import MONTHS
from web.models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink, \
from web.models.constants import SUBJECT_TYPE_CHOICES, SCREENING_NUMBER_PREFIXES_FOR_TYPE, COUNTRY_OTHER_ID
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 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_country(self, cleaned_data):
    if cleaned_data['country'].id == COUNTRY_OTHER_ID:
        self.add_error('country', "Select valid country")


def validate_subject_resign_reason(self, cleaned_data):
    if cleaned_data['resigned'] and cleaned_data['resign_reason'] == '':
        self.add_error('resign_reason', "Resign reason cannot be empty")


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', 'resign_reason']
    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_country(self, cleaned_data)
        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)
        validate_subject_country(self, self.cleaned_data)
        validate_subject_resign_reason(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)
        # 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=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
        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:
            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)