diff --git a/.gitignore b/.gitignore index a988c9e7f36bc5b8099b76117e708f4c45754dec..83c08fba63f2be651b4dbee75798abfe6c694147 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ smash/~/ # files uploaded and hosted by django smash/uploads/ +smash/smash/uploads/ # Disable python bytecode *.pyc diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py index aacd999186c4d76bd08a7545369d488530456558..94fa1fcfe15551d3d9510337db6ad1828158ca24 100644 --- a/smash/web/forms/study_subject_forms.py +++ b/smash/web/forms/study_subject_forms.py @@ -5,18 +5,31 @@ from django import forms from django.forms import ModelForm from web.forms.forms import DATETIMEPICKER_DATE_ATTRS, get_worker_from_args -from web.models import StudySubject, Study, StudyColumns +from web.models import StudySubject, Study, StudyColumns, VoucherType from web.models.constants import SCREENING_NUMBER_PREFIXES_FOR_TYPE +from web.widgets.secure_file_widget import SecuredFileWidget logger = logging.getLogger(__name__) -class StudySubjectAddForm(ModelForm): +class StudySubjectForm(ModelForm): datetime_contact_reminder = forms.DateTimeField(label="Contact on", widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS), required=False ) + referral_letter = forms.FileField(label='Select a file', widget=SecuredFileWidget(), required=False) + + voucher_types = forms.ModelMultipleChoiceField(required=False, + widget=forms.CheckboxSelectMultiple, + queryset=VoucherType.objects.all(), + ) + + def __init__(self, *args, **kwargs): + super(StudySubjectForm, self).__init__(*args, **kwargs) + + +class StudySubjectAddForm(StudySubjectForm): class Meta: model = StudySubject fields = '__all__' @@ -26,12 +39,12 @@ class StudySubjectAddForm(ModelForm): self.user = get_worker_from_args(kwargs) self.study = get_study_from_args(kwargs) - super(ModelForm, self).__init__(*args, **kwargs) + super(StudySubjectAddForm, self).__init__(*args, **kwargs) prepare_study_subject_fields(fields=self.fields, study=self.study) def save(self, commit=True): self.instance.study_id = self.study.id - return super(ModelForm, self).save(commit) + return super(StudySubjectAddForm, self).save(commit) def build_screening_number(self, cleaned_data): screening_number = cleaned_data.get('screening_number', None) @@ -83,7 +96,7 @@ def get_new_screening_number(screening_number_prefix): return screening_number_prefix + str(result_number + 1).zfill(3) -class StudySubjectDetailForm(ModelForm): +class StudySubjectDetailForm(StudySubjectForm): class Meta: model = StudySubject fields = '__all__' @@ -104,11 +117,7 @@ def get_study_from_study_subject_instance(study_subject): return Study(columns=StudyColumns()) -class StudySubjectEditForm(ModelForm): - datetime_contact_reminder = forms.DateTimeField(label="Contact on", - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS), - required=False - ) +class StudySubjectEditForm(StudySubjectForm): def __init__(self, *args, **kwargs): was_resigned = kwargs.get('was_resigned', False) @@ -166,6 +175,12 @@ def prepare_study_subject_fields(fields, study): prepare_field(fields, study.columns, 'pd_in_family') prepare_field(fields, study.columns, 'resigned') prepare_field(fields, study.columns, 'resign_reason') + prepare_field(fields, study.columns, 'referral_letter') + prepare_field(fields, study.columns, 'health_partner') + prepare_field(fields, study.columns, 'health_partner_feedback_agreement') + prepare_field(fields, study.columns, 'screening') + prepare_field(fields, study.columns, 'previously_in_study') + prepare_field(fields, study.columns, 'voucher_types') def validate_subject_screening_number(self, cleaned_data): diff --git a/smash/web/migrations/0097_auto_20171211_1616.py b/smash/web/migrations/0097_auto_20171211_1616.py new file mode 100644 index 0000000000000000000000000000000000000000..676c94f20f7f1553d4cc15445331f6b18548b849 --- /dev/null +++ b/smash/web/migrations/0097_auto_20171211_1616.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-11 16:16 +from __future__ import unicode_literals + +import django.core.files.storage +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0096_auto_20171208_1509'), + ] + + operations = [ + migrations.AddField( + model_name='studycolumns', + name='health_partner', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Health partner'), + ), + migrations.AddField( + model_name='studycolumns', + name='health_partner_feedback_agreement', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Agrees to give information to referral'), + ), + migrations.AddField( + model_name='studycolumns', + name='previously_in_study', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Previously in PDP study'), + ), + migrations.AddField( + model_name='studycolumns', + name='referral_letter', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Referral letter'), + ), + migrations.AddField( + model_name='studycolumns', + name='screening', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Screening'), + ), + migrations.AddField( + model_name='studycolumns', + name='voucher_types', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Voucher types'), + ), + migrations.AddField( + model_name='studycolumns', + name='vouchers', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Vouchers'), + ), + migrations.AddField( + model_name='studysubject', + name='health_partner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker', verbose_name=b'Health partner'), + ), + migrations.AddField( + model_name='studysubject', + name='health_partner_feedback_agreement', + field=models.BooleanField(default=False, verbose_name=b'Agrees to give information to referral'), + ), + migrations.AddField( + model_name='studysubject', + name='previously_in_study', + field=models.BooleanField(default=False, verbose_name=b'Previously in PDP study'), + ), + migrations.AddField( + model_name='studysubject', + name='referral_letter', + field=models.FileField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location=b'uploads'), upload_to=b'referral_letters', verbose_name=b'Referral letter'), + ), + migrations.AddField( + model_name='studysubject', + name='screening', + field=models.CharField(blank=True, max_length=1024, null=True, verbose_name=b'Screening'), + ), + migrations.AddField( + model_name='studysubject', + name='voucher_types', + field=models.ManyToManyField(blank=True, to='web.VoucherType', verbose_name=b'Voucher types'), + ), + migrations.AlterField( + model_name='studycolumns', + name='datetime_contact_reminder', + field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=True, verbose_name=b'Please make a contact on'), + ), + migrations.AlterField( + model_name='studysubject', + name='diagnosis', + field=models.CharField(blank=True, max_length=1024, null=True, verbose_name=b'Diagnosis'), + ), + migrations.AlterField( + model_name='voucher', + name='study_subject', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='vouchers', to='web.StudySubject'), + ), + ] diff --git a/smash/web/models/study_columns.py b/smash/web/models/study_columns.py index 7700a61be37532167f324d701fd8c771d9aecbe1..7acd12ba62fbfe7c027c02d5943bd28723b80c2d 100644 --- a/smash/web/models/study_columns.py +++ b/smash/web/models/study_columns.py @@ -78,7 +78,37 @@ class StudyColumns(models.Model): verbose_name='Resign reason' ) - datetime_contact_reminder = models.BooleanField(choices=BOOL_CHOICES, - default=True, - verbose_name='Last contact attempt' - ) + referral_letter = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Referral letter' + ) + + health_partner = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Health partner' + ) + + health_partner_feedback_agreement = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Agrees to give information to referral' + ) + + screening = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Screening' + ) + + previously_in_study = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Previously in PDP study', + ) + + voucher_types = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Voucher types', + ) + + vouchers = models.BooleanField(choices=BOOL_CHOICES, + default=False, + verbose_name='Vouchers', + ) diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py index 8bbeae76e223005167fc857d97465eff1c92af4f..705bd1905411777e0de4a7b57a52960f13278e99 100644 --- a/smash/web/models/study_subject.py +++ b/smash/web/models/study_subject.py @@ -1,9 +1,8 @@ # coding=utf-8 -from django.core.validators import RegexValidator from django.db import models -from constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES -from . import Appointment, Location, Visit +from web.models import VoucherType, Appointment, Location, Visit +from web.models.constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES, FILE_STORAGE class StudySubject(models.Model): @@ -92,11 +91,47 @@ class StudySubject(models.Model): blank=True, verbose_name='Referred by' ) - diagnosis = models.CharField(max_length=128, + referral_letter = models.FileField( + storage=FILE_STORAGE, + upload_to='referral_letters', + verbose_name='Referral letter', + blank=True, + null=True, + ) + + health_partner = models.ForeignKey("web.Worker", + verbose_name='Health partner', + null=True, + blank=True + ) + + health_partner_feedback_agreement = models.BooleanField( + verbose_name='Agrees to give information to referral', + default=False, + ) + + screening = models.CharField(max_length=1024, + null=True, + blank=True, + verbose_name='Screening' + ) + + diagnosis = models.CharField(max_length=1024, null=True, blank=True, verbose_name='Diagnosis' ) + + previously_in_study = models.BooleanField( + verbose_name='Previously in PDP study', + default=False, + ) + + voucher_types = models.ManyToManyField(VoucherType, + blank=True, + verbose_name='Voucher types' + ) + year_of_diagnosis = models.IntegerField( null=True, blank=True, diff --git a/smash/web/models/voucher.py b/smash/web/models/voucher.py index f7cc8f15c50c5c84cec75c33483678f4abab4ba0..340b78764e16e537b72809a29f767811b105650d 100644 --- a/smash/web/models/voucher.py +++ b/smash/web/models/voucher.py @@ -31,6 +31,7 @@ class Voucher(models.Model): StudySubject, on_delete=models.CASCADE, null=False, + related_name="vouchers", editable=False ) diff --git a/smash/web/templates/appointments/edit.html b/smash/web/templates/appointments/edit.html index 1d7cfa2ab4d86d3105f38041af240c4202339785..44a3ca2709e203ba4573717b306d30fcf6790aba 100644 --- a/smash/web/templates/appointments/edit.html +++ b/smash/web/templates/appointments/edit.html @@ -35,7 +35,7 @@ </div> <div class="box box-info"> - <form method="post" action="" class="form-horizontal"> + <form method="post" action="" enctype="multipart/form-data" class="form-horizontal"> {% csrf_token %} <fieldset> <div class="box-header with-border"> diff --git a/smash/web/templates/subjects/add.html b/smash/web/templates/subjects/add.html index 3bacf8021599c202596190da36dc00db059e1f88..bf01448f4625f7028478f21787460fabb27497d8 100644 --- a/smash/web/templates/subjects/add.html +++ b/smash/web/templates/subjects/add.html @@ -31,7 +31,7 @@ </div> <div class="row"> - <form method="post" action="" class="form-horizontal"> + <form method="post" action="" enctype="multipart/form-data" class="form-horizontal"> {% csrf_token %} <div class="col-md-12"> <div class="box box-body"> diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html index 7e956ad711830c9c1e318edb5b474346910ff521..1dedffbc01f4a185c5cc4b9c94871f2ec3e8910e 100644 --- a/smash/web/templates/subjects/edit.html +++ b/smash/web/templates/subjects/edit.html @@ -41,7 +41,7 @@ <h3>Subject details</h3> </div> - <form method="post" action="" class="form-horizontal"> + <form method="post" action="" enctype="multipart/form-data" class="form-horizontal"> {% csrf_token %} <div class="box-body"> <div class="col-md-12"> diff --git a/smash/web/tests/forms/test_StudySubjectEditForm.py b/smash/web/tests/forms/test_StudySubjectEditForm.py index 1e7a5c0919c5b3ed775b23e1cf4bb1b3ab538f59..da51f0a6a8d77fbdf15b5b35a6805ee1a6fbfa77 100644 --- a/smash/web/tests/forms/test_StudySubjectEditForm.py +++ b/smash/web/tests/forms/test_StudySubjectEditForm.py @@ -29,6 +29,7 @@ class StudySubjectEditFormTests(LoggedInWithWorkerTestCase): def test_validation(self): edit_form = StudySubjectEditForm(self.sample_data) save_status = edit_form.is_valid() + logger.debug(edit_form.errors) self.assertTrue(save_status) def test_validation_with_empty_study(self): diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index ad3da907a16f158491a12f5f85a905274d1d3064..8261bd6335145cc090f610467b3a7c6523eaeaef 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -75,7 +75,7 @@ def create_voucher(study_subject=None): issue_date=get_today_midnight_date(), expiry_date=get_today_midnight_date(), voucher_type=create_voucher_type(), - status = VOUCHER_STATUS_NEW) + status=VOUCHER_STATUS_NEW) def create_empty_notification_parameters(): @@ -260,6 +260,8 @@ def format_form_field(value): return value.strftime('%Y-%m-%d') elif isinstance(value, datetime.datetime): return value.strftime('%Y-%m-%d %H:%M') + elif value is None: + return "" else: return value diff --git a/smash/web/tests/view/test_appointments.py b/smash/web/tests/view/test_appointments.py index add77509ac5daeefdd5e8e34d23311f7af083c0f..9560cfa02a577b900b541addbf304c98e0506747 100644 --- a/smash/web/tests/view/test_appointments.py +++ b/smash/web/tests/view/test_appointments.py @@ -1,6 +1,7 @@ import datetime import logging +from django.core.files.uploadedfile import SimpleUploadedFile from django.urls import reverse from web.forms import AppointmentEditForm, SubjectEditForm, StudySubjectEditForm @@ -171,17 +172,15 @@ class AppointmentsViewTests(LoggedInTestCase): def prepare_form(self, appointment, subject): form_appointment = AppointmentEditForm(user=self.user, instance=appointment, prefix="appointment") form_study_subject = StudySubjectEditForm(instance=subject, prefix="study-subject") - form_subject = SubjectEditForm(instance=subject.subject, prefix="study-subject") + form_subject = SubjectEditForm(instance=subject.subject, prefix="subject") form_data = {} for key, value in form_appointment.initial.items(): - if value is not None: - form_data['appointment-{}'.format(key)] = format_form_field(value) + form_data['appointment-{}'.format(key)] = format_form_field(value) for key, value in form_study_subject.initial.items(): - if value is not None: - form_data['study-subject-{}'.format(key)] = format_form_field(value) + form_data['study-subject-{}'.format(key)] = format_form_field(value) for key, value in form_subject.initial.items(): - if value is not None: - form_data['subject-{}'.format(key)] = format_form_field(value) + form_data['subject-{}'.format(key)] = format_form_field(value) + form_data["study-subject-referral_letter"] = SimpleUploadedFile("file.txt", b"file_content") return form_data def test_subject_flying_team_location(self): diff --git a/smash/web/tests/view/test_subjects.py b/smash/web/tests/view/test_subjects.py index cf9d49777f4ebd58988db0c48c3e8df2d6eb509e..68607410e129a50bfedfba771e17685e4d7ee3ba 100644 --- a/smash/web/tests/view/test_subjects.py +++ b/smash/web/tests/view/test_subjects.py @@ -1,6 +1,7 @@ import datetime import logging +from django.core.files.uploadedfile import SimpleUploadedFile from django.urls import reverse from web.forms import SubjectAddForm, SubjectEditForm, StudySubjectAddForm, StudySubjectEditForm @@ -9,7 +10,7 @@ from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, COUNTRY_AFGHANISTAN_ID, COUNTRY_OTHER_ID, MAIL_TEMPLATE_CONTEXT_SUBJECT from web.tests import LoggedInWithWorkerTestCase from web.tests.functions import create_study_subject, create_visit, create_appointment, get_test_location, \ - create_language, get_resource_path, get_test_study + create_language, get_resource_path, get_test_study, format_form_field from web.views.notifications import get_today_midnight_date logger = logging.getLogger(__name__) @@ -75,8 +76,11 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase): form_data['subject-dead'] = "True" form_data['study_subject-resigned'] = "True" form_data['study_subject-resign_reason'] = "doesn't want to participate" - response = self.client.post( - reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id}), data=form_data) + + url = reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id}) + response = self.client.post(url, data=form_data) + + logger.debug(response.content) self.assertEqual(response.status_code, 302) updated_study_subject = StudySubject.objects.filter(id=self.study_subject.id)[0] @@ -98,11 +102,10 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase): form_subject = SubjectEditForm(instance=self.study_subject.subject, prefix="subject") form_data = {} for key, value in form_study_subject.initial.items(): - if value is not None: - form_data['study_subject-{}'.format(key)] = value + form_data['study_subject-{}'.format(key)] = format_form_field(value) for key, value in form_subject.initial.items(): - if value is not None: - form_data['subject-{}'.format(key)] = value + form_data['subject-{}'.format(key)] = format_form_field(value) + form_data["study_subject-referral_letter"] = SimpleUploadedFile("file.txt", b"file_content") return form_data def create_add_form_data_for_study_subject(self):