Newer
Older
import logging
from django import forms
from django.forms import ModelForm, Form
from web.models import Subject, StudySubject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, \
AppointmentTypeLink, \
Availability, Holiday
from web.models.constants import SUBJECT_TYPE_CHOICES, SCREENING_NUMBER_PREFIXES_FOR_TYPE, COUNTRY_OTHER_ID
from web.views.notifications import get_filter_locations
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))
'class': 'datepicker',
'data-date-format': 'yyyy-mm-dd',
'class': 'datetimepicker',
'data-date-format': 'Y-MM-DD HH:mm',
TIMEPICKER_DATE_ATTRS = {
'class': 'datetimepicker',
'data-date-format': 'HH:mm',
'data-date-stepping': 15,
}
logger = logging.getLogger(__name__)
def validate_subject_nd_number(self, cleaned_data):
if cleaned_data['nd_number'] != "":
subjects_from_db = StudySubject.objects.filter(nd_number=cleaned_data['nd_number'])
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")
Piotr Gawron
committed
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 = StudySubject.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")
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
)
exclude = ['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)
return screening_number
screening_number = self.build_screening_number(cleaned_data)
if screening_number is not None:
cleaned_data['screening_number'] = screening_number
subjects_from_db = StudySubject.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 = StudySubject.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:
class StudySubjectEditForm(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_resigned = kwargs.get('was_resigned', False)
if 'was_resigned' in kwargs:
kwargs.pop('was_resigned')
super(StudySubjectEditForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.id:
self.fields['screening_number'].widget.attrs['readonly'] = True
if was_resigned:
self.fields['resigned'].disabled = True
def clean(self):
validate_subject_nd_number(self, self.cleaned_data)
validate_subject_mpower_number(self, self.cleaned_data)
Piotr Gawron
committed
validate_subject_resign_reason(self, self.cleaned_data)
fields = '__all__'
class WorkerAddForm(ModelForm):
class Meta:
model = Worker
exclude = ['appointments']
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__'
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)
raise TypeError("User not defined")
self.user = Worker.get_by_user(user)
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
Piotr Gawron
committed
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)
if appointment_type not in appointment_types_from_links:
# we create new appointment links for appointments types that have been added
appointment_type_link = AppointmentTypeLink(appointment=appointment,
appointment_type=appointment_type)
appointment_type_link.save()
class AppointmentAddForm(ModelForm):
class Meta:
model = Appointment
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)
raise TypeError("User not defined")
self.user = Worker.get_by_user(user)
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
Piotr Gawron
committed
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):
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())
exclude = ['is_finished', 'visit_number']
Piotr Gawron
committed
subject = forms.ModelChoiceField(queryset=StudySubject.objects.order_by('subject__last_name', 'subject__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())
exclude = ['is_finished', 'visit_number']
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']
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")
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
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:
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)
class SubjectAddForm(ModelForm):
date_born = forms.DateField(label="Date of birth",
widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
required=False
)
class Meta:
model = Subject
fields = '__all__'
exclude = ['dead']
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()
validate_subject_country(self, cleaned_data)
return cleaned_data
class SubjectEditForm(ModelForm):
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)
if 'was_dead' in kwargs:
kwargs.pop('was_dead')
super(SubjectEditForm, self).__init__(*args, **kwargs)
if was_dead:
self.fields['dead'].disabled = True
def clean(self):
validate_subject_country(self, self.cleaned_data)
class Meta:
model = Subject
fields = '__all__'