diff --git a/smash/web/admin.py b/smash/web/admin.py index 5fdcc64ec02827b9b594f08b09513dfa6b27b86d..303aa56d09cf241230750c164e8abb6424dedea9 100644 --- a/smash/web/admin.py +++ b/smash/web/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from models import Subject, Item, Room, AppointmentType, Language, Location, Worker, FlyingTeam, Availability, Holiday, \ +from models import StudySubject, Item, Room, AppointmentType, Language, Location, Worker, FlyingTeam, Availability, Holiday, \ Visit, Appointment @@ -9,7 +9,7 @@ class LanguageAdmin(admin.ModelAdmin): # Register your models here. -admin.site.register(Subject) +admin.site.register(StudySubject) admin.site.register(Visit) admin.site.register(Item) admin.site.register(Room) diff --git a/smash/web/api_views/appointment.py b/smash/web/api_views/appointment.py index a51ddfc058c410f8f65bef590804cf9e793ef35e..14a96d62d4423f0343255cc896301db479169dc1 100644 --- a/smash/web/api_views/appointment.py +++ b/smash/web/api_views/appointment.py @@ -87,11 +87,11 @@ def serialize_appointment(appointment): nd_number = screening_number = phone_numbers = appointment_types = None if appointment.visit is not None: title = "Visit " + str(appointment.visit.visit_number) - subject = appointment.visit.subject - subject_string = subject.last_name + " " + subject.first_name - nd_number = subject.nd_number - screening_number = subject.screening_number - phone_numbers = ", ".join(filter(None, [subject.phone_number, subject.phone_number_2, subject.phone_number_3])) + study_subject = appointment.visit.subject + subject_string = study_subject.subject.last_name + " " + study_subject.subject.first_name + nd_number = study_subject.nd_number + screening_number = study_subject.screening_number + phone_numbers = ", ".join(filter(None, [study_subject.subject.phone_number, study_subject.subject.phone_number_2, study_subject.subject.phone_number_3])) appointment_types = ", ".join([unicode(type) for type in appointment.appointment_types.all()]) else: title = appointment.comment diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py index b84e207c373bdb98fbd3169817c69105df9b3549..6c864c10d9459a06130bc9df93da29d388d14e44 100644 --- a/smash/web/api_views/subject.py +++ b/smash/web/api_views/subject.py @@ -5,7 +5,7 @@ from django.db.models import Count, Case, When, Min from django.db.models import Q from django.http import JsonResponse -from web.models import Subject, Visit, Appointment +from web.models import Subject, StudySubject, Visit, Appointment from web.models.constants import SUBJECT_TYPE_CHOICES from web.views import e500_error from web.views.notifications import get_subjects_with_no_visit, get_subjects_with_reminder, get_today_midnight_date @@ -16,15 +16,19 @@ logger = logging.getLogger(__name__) @login_required def cities(request): - result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct() - return JsonResponse({ - "cities": [x[0] for x in result_subjects] - }) + try: + result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct() + return JsonResponse({ + "cities": [x[0] for x in result_subjects] + }) + except Exception as e: + logger.error(e, exc_info=True) + return e500_error(request) @login_required def referrals(request): - result_subjects = Subject.objects.filter(referral__isnull=False).values_list('referral').distinct() + result_subjects = StudySubject.objects.filter(referral__isnull=False).values_list('referral').distinct() return JsonResponse({ "referrals": [x[0] for x in result_subjects] }) @@ -33,7 +37,7 @@ def referrals(request): @login_required def get_subjects(request, type): if type == SUBJECT_LIST_GENERIC: - return Subject.objects.all() + return StudySubject.objects.all() elif type == SUBJECT_LIST_NO_VISIT: return get_subjects_with_no_visit(request.user) elif type == SUBJECT_LIST_REQUIRE_CONTACT: @@ -55,9 +59,9 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction): else: order_direction = "-" if order_column == "first_name": - result = subjects_to_be_ordered.order_by(order_direction + 'first_name') + result = subjects_to_be_ordered.order_by(order_direction + 'subject__first_name') elif order_column == "last_name": - result = subjects_to_be_ordered.order_by(order_direction + 'last_name') + result = subjects_to_be_ordered.order_by(order_direction + 'subject__last_name') elif order_column == "nd_number": result = subjects_to_be_ordered.order_by(order_direction + 'nd_number') elif order_column == "screening_number": @@ -65,7 +69,7 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction): elif order_column == "default_location": result = subjects_to_be_ordered.order_by(order_direction + 'default_location') elif order_column == "dead": - result = subjects_to_be_ordered.order_by(order_direction + 'dead') + result = subjects_to_be_ordered.order_by(order_direction + 'subject__dead') elif order_column == "resigned": result = subjects_to_be_ordered.order_by(order_direction + 'resigned') elif order_column == "information_sent": @@ -77,7 +81,7 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction): elif order_column == "id": result = subjects_to_be_ordered.order_by(order_direction + 'id') elif order_column == "date_born": - result = subjects_to_be_ordered.order_by(order_direction + 'date_born') + result = subjects_to_be_ordered.order_by(order_direction + 'subject__date_born') elif order_column == "visit_1": result = order_by_visit(subjects_to_be_ordered, order_direction, 1) elif order_column == "visit_2": @@ -166,15 +170,15 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): column = row[0] value = row[1] if column == "first_name": - result = result.filter(first_name__icontains=value) + result = result.filter(subject__first_name__icontains=value) elif column == "last_name": - result = result.filter(last_name__icontains=value) + result = result.filter(subject__last_name__icontains=value) elif column == "nd_number": result = result.filter(nd_number__icontains=value) elif column == "screening_number": result = result.filter(screening_number__icontains=value) elif column == "dead": - result = result.filter(dead=(value == "true")) + result = result.filter(subject__dead=(value == "true")) elif column == "resigned": result = result.filter(resigned=(value == "true")) elif column == "postponed": @@ -256,7 +260,8 @@ def subjects(request, type): "recordsFiltered": count_filtered, "data": data, }) - except: + except Exception as e: + logger.error(e, exc_info=True) return e500_error(request) @@ -282,11 +287,11 @@ def serialize_subject_visit(visit): pass -def serialize_subject(subject): +def serialize_subject(study_subject): location = "" - if subject.default_location is not None: - location = subject.default_location.name - visits = Visit.objects.filter(subject=subject).order_by('visit_number') + if study_subject.default_location is not None: + location = study_subject.default_location.name + visits = Visit.objects.filter(subject=study_subject).order_by('visit_number') serialized_visits = [] for visit in visits: if visit.datetime_begin < get_today_midnight_date(): @@ -315,18 +320,18 @@ def serialize_subject(subject): }) result = { - "first_name": subject.first_name, - "last_name": subject.last_name, - "date_born": subject.date_born, - "nd_number": subject.nd_number, - "screening_number": subject.screening_number, + "first_name": study_subject.subject.first_name, + "last_name": study_subject.subject.last_name, + "date_born": study_subject.subject.date_born, + "nd_number": study_subject.nd_number, + "screening_number": study_subject.screening_number, "default_location": location, - "dead": get_yes_no(subject.dead), - "resigned": get_yes_no(subject.resigned), - "postponed": get_yes_no(subject.postponed), - "information_sent": get_yes_no(subject.information_sent), - "type": subject.get_type_display(), - "id": subject.id, + "dead": get_yes_no(study_subject.subject.dead), + "resigned": get_yes_no(study_subject.resigned), + "postponed": get_yes_no(study_subject.postponed), + "information_sent": get_yes_no(study_subject.information_sent), + "type": study_subject.get_type_display(), + "id": study_subject.id, "visits": serialized_visits, } return result diff --git a/smash/web/forms.py b/smash/web/forms.py index 571fd786a5506d785f7aa1107a727ca23cbc88fd..ade762d5d0f7bb3bfdfd76840d90fb337bf1877c 100644 --- a/smash/web/forms.py +++ b/smash/web/forms.py @@ -1,11 +1,13 @@ import datetime +import logging from collections import OrderedDict from django import forms 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 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 @@ -35,10 +37,12 @@ TIMEPICKER_DATE_ATTRS = { START_YEAR_STATISTICS = 2015 APPOINTMENT_TYPES_FIELD_POSITION = 1 +logger = logging.getLogger(__name__) + 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']) + subjects_from_db = StudySubject.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") @@ -56,27 +60,22 @@ def validate_subject_resign_reason(self, cleaned_data): 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']) + 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") -class SubjectAddForm(ModelForm): - date_born = forms.DateField(label="Date of birth", - widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"), - required=False - ) - +class StudySubjectAddForm(ModelForm): datetime_contact_reminder = forms.DateTimeField(label="Contact on", widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS), required=False ) class Meta: - model = Subject + model = StudySubject fields = '__all__' - exclude = ['dead', 'resigned', 'resign_reason'] + exclude = ['resigned', 'resign_reason'] def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) @@ -98,15 +97,14 @@ class SubjectAddForm(ModelForm): return screening_number def clean(self): - cleaned_data = super(SubjectAddForm, self).clean() + cleaned_data = super(StudySubjectAddForm, 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) + 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_country(self, cleaned_data) validate_subject_nd_number(self, cleaned_data) validate_subject_mpower_number(self, cleaned_data) return cleaned_data @@ -128,7 +126,7 @@ class SubjectAddForm(ModelForm): def get_new_screening_number(screening_number_prefix): result_number = 0 - subjects = Subject.objects.filter(screening_number__contains=screening_number_prefix) + 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: @@ -143,46 +141,36 @@ def get_new_screening_number(screening_number_prefix): return screening_number_prefix + str(result_number + 1).zfill(3) -class SubjectDetailForm(ModelForm): +class StudySubjectDetailForm(ModelForm): class Meta: - model = Subject + model = StudySubject fields = '__all__' -class SubjectEditForm(ModelForm): +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_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) + 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_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) 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 + model = StudySubject fields = '__all__' @@ -343,7 +331,8 @@ class VisitDetailForm(ModelForm): class VisitAddForm(ModelForm): - subject = forms.ModelChoiceField(queryset=Subject.objects.order_by('last_name', 'first_name')) + 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) ) @@ -521,3 +510,48 @@ class HolidayAddForm(ModelForm): 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 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__' + + +class SubjectDetailForm(ModelForm): + class Meta: + model = Subject + fields = '__all__' diff --git a/smash/web/migrations/0063_auto_20171120_1429.py b/smash/web/migrations/0063_auto_20171120_1429.py new file mode 100644 index 0000000000000000000000000000000000000000..7b9c1de9d8c4e5eba269740c479e658a87de8433 --- /dev/null +++ b/smash/web/migrations/0063_auto_20171120_1429.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-20 14:29 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0062_subject_resign_reason'), + ] + + operations = [ + migrations.AlterField( + model_name='subject', + name='nd_number', + field=models.CharField(blank=True, max_length=25, validators=[django.core.validators.RegexValidator(b'^(ND[0-9]{4}|)$', message=b'ND number should look as follows: NDxxxx')], verbose_name=b'ND number'), + ), + ] diff --git a/smash/web/migrations/0064_auto_20171127_0954.py b/smash/web/migrations/0064_auto_20171127_0954.py new file mode 100644 index 0000000000000000000000000000000000000000..4fd29798f2f5fa6180904177aa22be5db2c72dcc --- /dev/null +++ b/smash/web/migrations/0064_auto_20171127_0954.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-27 09:54 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0063_auto_20171120_1429'), + ] + + operations = [ + migrations.RenameModel('Subject', 'StudySubject') + ] diff --git a/smash/web/migrations/0065_auto_20171127_0957.py b/smash/web/migrations/0065_auto_20171127_0957.py new file mode 100644 index 0000000000000000000000000000000000000000..79e6a2a186804e87b40fb729b72c03247b6561ea --- /dev/null +++ b/smash/web/migrations/0065_auto_20171127_0957.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-27 09:57 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0064_auto_20171127_0954'), + ] + + operations = [ + migrations.AlterField( + model_name='studysubject', + name='default_written_communication_language', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='studysubject_written_comunication', to='web.Language', verbose_name=b'Default language for document generation'), + ), + ] diff --git a/smash/web/migrations/0066_subject.py b/smash/web/migrations/0066_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..511f3560a02a48efb4d3a708ed1e538deab50795 --- /dev/null +++ b/smash/web/migrations/0066_subject.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-27 10:42 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0065_auto_20171127_0957'), + ] + + operations = [ + migrations.RunSQL('alter sequence web_subject_id_seq rename to web_studysubject_id_seq;'), + migrations.RunSQL('alter sequence web_subject_languages_id_seq rename to web_studysubject_languages_id_seq;'), + migrations.RunSQL('alter table web_studysubject rename constraint web_subject_flying_team_id_ef78de5d_fk_web_flyingteam_id to web_studysubject_flying_team_id_ef78de5d_fk_web_flyingteam_id;'), + migrations.RunSQL('alter table web_studysubject_languages rename constraint web_subject_languages_language_id_80eca4c1_fk_web_language_id to web_studysubject_languages_language_id_80eca4c1_fk_web_language_id;'), + migrations.RunSQL('alter table web_studysubject rename constraint web_subject_country_id_5d976875_fk_web_country_id to web_studysubject_country_id_5d976875_fk_web_country_id;'), + migrations.RunSQL('alter table web_studysubject rename constraint web_subject_default_location_id_da83b714_fk_web_location_id to web_studyubject_default_location_id_da83b714_fk_web_location_id;'), + migrations.RunSQL('alter table web_studysubject rename constraint web_subject_pkey to web_studysubject_pkey;'), + migrations.RunSQL('alter table web_studysubject_languages rename constraint web_subject_languages_pkey to web_studysubject_languages_pkey;'), + migrations.RunSQL('alter table web_studysubject_languages rename constraint web_subject_languages_subject_id_a5eb4f49_uniq to web_studysubject_languages_subject_id_a5eb4f49_uniq;'), + migrations.RunSQL('alter index if exists web_subject_15d1b6cd rename to web_studysubject_15d1b6cd;'), + migrations.RunSQL('alter index if exists web_subject_496f9482 rename to web_studysubject_496f9482;'), + migrations.RunSQL('alter index if exists web_subject_84b500ff rename to web_studysubject_84b500ff;'), + migrations.RunSQL('alter index if exists web_subject_9571fdf9 rename to web_studysubject_9571fdf9;'), + migrations.RunSQL('alter index if exists web_subject_languages_468679bd rename to web_studysubject_languages_468679bd;'), + migrations.RunSQL('alter index if exists web_subject_languages_ffaba1d1 rename to web_studysubject_languages_ffaba1d1;'), + migrations.RunSQL('alter index if exists web_subject_screening_number_75aa70c7_like rename to web_studysubject_screening_number_75aa70c7_like;') + ] diff --git a/smash/web/migrations/0067_subject.py b/smash/web/migrations/0067_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..6039e17520676f685812939e069109492ba20cf5 --- /dev/null +++ b/smash/web/migrations/0067_subject.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-27 13:29 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('web', '0066_subject'), + ] + + operations = [ + migrations.CreateModel( + name='Subject', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tmp_study_subject_id', models.IntegerField()), + ('sex', + models.CharField(choices=[(b'M', b'Male'), (b'F', b'Female')], max_length=1, verbose_name=b'Sex')), + ('first_name', models.CharField(max_length=50, verbose_name=b'First name')), + ('last_name', models.CharField(max_length=50, verbose_name=b'Last name')), + ('phone_number', models.CharField(blank=True, max_length=20, null=True, verbose_name=b'Phone number')), + ('phone_number_2', + models.CharField(blank=True, max_length=20, null=True, verbose_name=b'Phone number 2')), + ('phone_number_3', + models.CharField(blank=True, max_length=20, null=True, verbose_name=b'Phone number 3')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name=b'E-mail')), + ('date_born', models.DateField(blank=True, null=True, verbose_name=b'Date of birth (YYYY-MM-DD)')), + ('address', models.CharField(blank=True, max_length=255, verbose_name=b'Address')), + ('postal_code', models.CharField(blank=True, max_length=7, verbose_name=b'Postal code')), + ('city', models.CharField(blank=True, max_length=50, verbose_name=b'City')), + ('dead', models.BooleanField(default=False, verbose_name=b'Deceased')), + ('country', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='web.Country', + verbose_name=b'Country')), + ('default_written_communication_language', + models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='subjects_written_communication', to='web.Language', + verbose_name=b'Default language for document generation')), + ('languages', models.ManyToManyField(blank=True, to='web.Language', verbose_name=b'Known languages')), + ], + ), + migrations.RunSQL('insert into web_subject (tmp_study_subject_id, sex, first_name, last_name, phone_number, ' + + 'phone_number_2, phone_number_3, email, date_born, address, postal_code, city, dead, ' + + 'country_id, default_written_communication_language_id) ' + + 'select id, sex, first_name, last_name, phone_number, phone_number_2, phone_number_3, ' + + 'email, date_born, address, postal_code, city, dead, country_id, ' + 'default_written_communication_language_id from web_studysubject;'), + migrations.AddField( + model_name='studysubject', + name='subject', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Subject', + verbose_name=b'Subject'), + ), + migrations.RunSQL('update web_studysubject set subject_id = ' + + '(select id from web_subject where tmp_study_subject_id =web_studysubject.id);'), + migrations.RemoveField( + model_name='subject', + name='tmp_study_subject_id', + ), + migrations.AlterField( + model_name='studysubject', + name='subject', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Subject', + verbose_name=b'Subject'), + ), + ] diff --git a/smash/web/migrations/0068_remove_studysubject_dead.py b/smash/web/migrations/0068_remove_studysubject_dead.py new file mode 100644 index 0000000000000000000000000000000000000000..75f6b95408308d0413cff30232afe1947145333e --- /dev/null +++ b/smash/web/migrations/0068_remove_studysubject_dead.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-27 15:29 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0067_subject'), + ] + + operations = [ + migrations.RemoveField( + model_name='studysubject', + name='dead', + ), + ] diff --git a/smash/web/migrations/0069_remove_studysubject_sex.py b/smash/web/migrations/0069_remove_studysubject_sex.py new file mode 100644 index 0000000000000000000000000000000000000000..1f3c708b0a2e3d52db8ff7cf14d7bcad70fe1e5c --- /dev/null +++ b/smash/web/migrations/0069_remove_studysubject_sex.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-28 11:14 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0068_remove_studysubject_dead'), + ] + + operations = [ + migrations.RemoveField( + model_name='studysubject', + name='sex', + ), + ] diff --git a/smash/web/migrations/0070_auto_20171128_1124.py b/smash/web/migrations/0070_auto_20171128_1124.py new file mode 100644 index 0000000000000000000000000000000000000000..fe02c8a9cedff7df383393b69fcbd63178b9185c --- /dev/null +++ b/smash/web/migrations/0070_auto_20171128_1124.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-28 11:24 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0069_remove_studysubject_sex'), + ] + + operations = [ + migrations.RemoveField( + model_name='studysubject', + name='address', + ), + migrations.RemoveField( + model_name='studysubject', + name='city', + ), + migrations.RemoveField( + model_name='studysubject', + name='country', + ), + migrations.RemoveField( + model_name='studysubject', + name='date_born', + ), + migrations.RemoveField( + model_name='studysubject', + name='default_written_communication_language', + ), + migrations.RemoveField( + model_name='studysubject', + name='email', + ), + migrations.RemoveField( + model_name='studysubject', + name='first_name', + ), + migrations.RemoveField( + model_name='studysubject', + name='languages', + ), + migrations.RemoveField( + model_name='studysubject', + name='last_name', + ), + migrations.RemoveField( + model_name='studysubject', + name='phone_number', + ), + migrations.RemoveField( + model_name='studysubject', + name='phone_number_2', + ), + migrations.RemoveField( + model_name='studysubject', + name='phone_number_3', + ), + migrations.RemoveField( + model_name='studysubject', + name='postal_code', + ), + ] diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py index 28a4bfa0bbae63229028fc45c83657f1031f9370..2b7f7dba19788a6778d0430ed60436cc0c6c4e88 100644 --- a/smash/web/models/__init__.py +++ b/smash/web/models/__init__.py @@ -18,12 +18,13 @@ from holiday import Holiday from item import Item from language import Language from subject import Subject +from study_subject import StudySubject from contact_attempt import ContactAttempt from mail_template import MailTemplate from missing_subject import MissingSubject from inconsistent_subject import InconsistentSubject, InconsistentField -__all__ = [FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, +__all__ = [FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, StudySubject, Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate, AppointmentTypeLink, MissingSubject, InconsistentSubject, InconsistentField, Country] diff --git a/smash/web/models/appointment.py b/smash/web/models/appointment.py index f4a6c043a3dc13da9bed30f999ae19cc42fa6caa..9b5b3944c623d47a8e090bccab83fbfed6eb245b 100644 --- a/smash/web/models/appointment.py +++ b/smash/web/models/appointment.py @@ -125,7 +125,7 @@ class Appointment(models.Model): title = "N/A" return title else: - title = self.visit.subject.first_name + " " + self.visit.subject.last_name + " type: " + title = self.visit.subject.subject.first_name + " " + self.visit.subject.subject.last_name + " type: " for appointment_type in self.appointment_types.all(): title += appointment_type.code + ", " return title diff --git a/smash/web/models/contact_attempt.py b/smash/web/models/contact_attempt.py index b21d8d60de852e1588c785cbecfd885554390d68..e8a2608e2b78655add178a3d09de4570137a5545 100644 --- a/smash/web/models/contact_attempt.py +++ b/smash/web/models/contact_attempt.py @@ -7,7 +7,7 @@ __author__ = 'Valentin Grouès' class ContactAttempt(models.Model): - subject = models.ForeignKey("web.Subject", + subject = models.ForeignKey("web.StudySubject", verbose_name='Subject' ) worker = models.ForeignKey("web.Worker", null=True, diff --git a/smash/web/models/inconsistent_subject.py b/smash/web/models/inconsistent_subject.py index b9298c515b5d7dead50cecf5d02c94615be5d8e1..d50564e7e93b5f1aa4d98a152c15bb32bac9c8ed 100644 --- a/smash/web/models/inconsistent_subject.py +++ b/smash/web/models/inconsistent_subject.py @@ -44,7 +44,7 @@ class InconsistentSubject(models.Model): class Meta: app_label = 'web' - subject = models.ForeignKey("web.Subject", + subject = models.ForeignKey("web.StudySubject", verbose_name='Subject', null=True, blank=True diff --git a/smash/web/models/mail_template.py b/smash/web/models/mail_template.py index 9803905cdbd38f69cf579cb622b606563b979068..5d6e081bcf38a7f96834546549dc4f961e030407 100644 --- a/smash/web/models/mail_template.py +++ b/smash/web/models/mail_template.py @@ -9,7 +9,7 @@ from django.db import models from .constants import MAIL_TEMPLATE_CONTEXT_CHOICES, MAIL_TEMPLATE_CONTEXT_APPOINTMENT, \ MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT from ..docx_helper import process_file -from ..models import Appointment, Visit, Subject, Worker +from ..models import Appointment, Visit, StudySubject, Worker DATE_FORMAT_FULL = "%A %d %B %Y" @@ -124,16 +124,16 @@ class MailTemplate(models.Model): def apply(self, instance, user, stream): appointment = None visit = None - subject = None + study_subject = None if isinstance(instance, Appointment): appointment = instance visit = instance.visit - subject = visit.subject + study_subject = visit.subject elif isinstance(instance, Visit): visit = instance - subject = visit.subject - elif isinstance(instance, Subject): - subject = instance + study_subject = visit.subject + elif isinstance(instance, StudySubject): + study_subject = instance # set locale to get correct date format locale_name = self.language.locale if platform.system() == 'Windows': @@ -143,7 +143,7 @@ class MailTemplate(models.Model): self._add_generic_replacements(replacements, Worker.get_by_user(user)) self._add_appointment_replacements(replacements, appointment) self._add_visit_replacements(replacements, visit) - self._add_subject_replacements(replacements, subject) + self._add_subject_replacements(replacements, study_subject) process_file(self.template_file.path, stream, replacements) return stream @@ -194,33 +194,33 @@ class MailTemplate(models.Model): "##V_DATE_ENDS_SHORT##": visit.datetime_end.strftime(DATE_FORMAT_SHORT), }) - def _add_subject_replacements(self, replacements, subject): - if subject is not None: - if subject.date_born is not None: - date_born = subject.date_born.strftime(DATE_FORMAT_SHORT) + def _add_subject_replacements(self, replacements, study_subject): + if study_subject is not None: + if study_subject.subject.date_born is not None: + date_born = study_subject.subject.date_born.strftime(DATE_FORMAT_SHORT) else: date_born = None replacements.update({ - "##S_FULL_NAME##": unicode(subject), - "##S_FIRST_NAME##": subject.first_name, - "##S_LAST_NAME##": subject.last_name, - "##S_ADDRESS##": subject.address, - "##S_CITY##": subject.city, - "##S_COUNTRY##": unicode(subject.country), - "##S_DIAGNOSIS_YEAR##": subject.year_of_diagnosis, - "##S_DATE_ADDED##": subject.date_added.strftime(DATE_FORMAT_SHORT), + "##S_FULL_NAME##": unicode(study_subject), + "##S_FIRST_NAME##": study_subject.subject.first_name, + "##S_LAST_NAME##": study_subject.subject.last_name, + "##S_ADDRESS##": study_subject.subject.address, + "##S_CITY##": study_subject.subject.city, + "##S_COUNTRY##": unicode(study_subject.subject.country), + "##S_DIAGNOSIS_YEAR##": study_subject.year_of_diagnosis, + "##S_DATE_ADDED##": study_subject.date_added.strftime(DATE_FORMAT_SHORT), "##S_DATE_BORN##": date_born, - "##S_DIAGNOSIS##": subject.diagnosis, - "##S_EMAIL##": subject.email, - "##S_SEX##": subject.get_sex_display(), - "##S_MPOWER_ID##": subject.mpower_id, - "##S_ND_NUMBER##": subject.nd_number, - "##S_PHONE_NUMBER##": subject.phone_number, - "##S_PHONE_NUMBER_2##": subject.phone_number_2, - "##S_PHONE_NUMBER_3##": subject.phone_number_3, - "##S_POST_CODE##": subject.postal_code, - "##S_SCREENING_NUMBER##": subject.screening_number, - "##S_TYPE##": subject.get_type_display(), - '##S_MAIL_LANGUAGE##': str(subject.default_written_communication_language), - '##S_KNOWN_LANGUAGES##': ", ".join([l.name for l in subject.languages.all()]) + "##S_DIAGNOSIS##": study_subject.diagnosis, + "##S_EMAIL##": study_subject.subject.email, + "##S_SEX##": study_subject.subject.get_sex_display(), + "##S_MPOWER_ID##": study_subject.mpower_id, + "##S_ND_NUMBER##": study_subject.nd_number, + "##S_PHONE_NUMBER##": study_subject.subject.phone_number, + "##S_PHONE_NUMBER_2##": study_subject.subject.phone_number_2, + "##S_PHONE_NUMBER_3##": study_subject.subject.phone_number_3, + "##S_POST_CODE##": study_subject.subject.postal_code, + "##S_SCREENING_NUMBER##": study_subject.screening_number, + "##S_TYPE##": study_subject.get_type_display(), + '##S_MAIL_LANGUAGE##': str(study_subject.subject.default_written_communication_language), + '##S_KNOWN_LANGUAGES##': ", ".join([l.name for l in study_subject.subject.languages.all()]) }) diff --git a/smash/web/models/missing_subject.py b/smash/web/models/missing_subject.py index ff7353bbfec9910de716a47575fc90a9d0466782..6050dd7e290ee6e10b6edbf059b2d592cc4e3c9d 100644 --- a/smash/web/models/missing_subject.py +++ b/smash/web/models/missing_subject.py @@ -11,7 +11,7 @@ class MissingSubject(models.Model): verbose_name='Ignore missing subject' ) - subject = models.ForeignKey("web.Subject", + subject = models.ForeignKey("web.StudySubject", verbose_name='Subject', null=True, blank=True diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..7fdc2eaca43b9bfffe90bca16f298dc88056d1ec --- /dev/null +++ b/smash/web/models/study_subject.py @@ -0,0 +1,119 @@ +# 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 + + +class StudySubject(models.Model): + class Meta: + app_label = 'web' + + def finish_all_visits(self): + visits = Visit.objects.filter(subject=self, is_finished=False) + for visit in visits: + visit.is_finished = True + visit.save() + + def finish_all_appointments(self): + appointments = Appointment.objects.filter(visit__subject=self, status=Appointment.APPOINTMENT_STATUS_SCHEDULED) + for appointment in appointments: + appointment.status = Appointment.APPOINTMENT_STATUS_CANCELLED + appointment.save() + + def mark_as_resigned(self): + self.resigned = True + self.finish_all_visits() + self.finish_all_appointments() + + subject = models.ForeignKey("web.Subject", + verbose_name='Subject', + editable=False, + null=False, + ) + + postponed = models.BooleanField(choices=BOOL_CHOICES, + verbose_name='Postponed', + default=False + ) + datetime_contact_reminder = models.DateTimeField( + null=True, + blank=True, + verbose_name='Please make a contact on', + ) + type = models.CharField(max_length=1, + choices=SUBJECT_TYPE_CHOICES.items(), + verbose_name='Type' + ) + + default_location = models.ForeignKey(Location, + verbose_name='Default appointment location', + ) + + flying_team = models.ForeignKey("web.FlyingTeam", + verbose_name='Default flying team location (if applicable)', + null=True, blank=True + ) + + screening_number = models.CharField(max_length=50, + unique=True, + verbose_name='Screening number', blank=False, null=False + ) + nd_number = models.CharField(max_length=25, + blank=True, + verbose_name='ND number', + validators=[ + RegexValidator('^(ND[0-9]{4}|)$', + message="ND number should look as follows: NDxxxx")] + ) + mpower_id = models.CharField(max_length=20, + blank=True, + verbose_name='MPower ID' + ) + comments = models.TextField(max_length=2000, + blank=True, + verbose_name='Comments' + ) + date_added = models.DateField(verbose_name='Added on', + auto_now=True + ) + referral = models.CharField(max_length=128, + null=True, + blank=True, + verbose_name='Referred by' + ) + diagnosis = models.CharField(max_length=128, + null=True, + blank=True, + verbose_name='Diagnosis' + ) + year_of_diagnosis = models.IntegerField( + null=True, + blank=True, + verbose_name='Year of diagnosis (YYYY)' + ) + + information_sent = models.BooleanField( + verbose_name='Information sent', + default=False + ) + pd_in_family = models.NullBooleanField( + verbose_name='PD in family', + default=None, + ) + resigned = models.BooleanField( + verbose_name='Resigned', + default=False, + editable=True + ) + resign_reason = models.TextField(max_length=2000, + blank=True, + verbose_name='Resign reason' + ) + + def __str__(self): + return "%s %s" % (self.subject.first_name, self.subject.last_name) + + def __unicode__(self): + return "%s %s" % (self.subject.first_name, self.subject.last_name) diff --git a/smash/web/models/subject.py b/smash/web/models/subject.py index ab779e001586776b650fea2bb7f8da0e67e9162b..ae09e54185c35374522fb59b8efb790af5428c9b 100644 --- a/smash/web/models/subject.py +++ b/smash/web/models/subject.py @@ -1,79 +1,37 @@ # coding=utf-8 -from django.core.validators import RegexValidator from django.db import models -from constants import BOOL_CHOICES, SEX_CHOICES, SUBJECT_TYPE_CHOICES, COUNTRY_OTHER_ID -from web.models import Country -from . import Appointment, Language, Location, Visit +from constants import SEX_CHOICES, COUNTRY_OTHER_ID +from web.models import Country, Visit, Appointment +from . import Language class Subject(models.Model): class Meta: app_label = 'web' - def finish_all_visits(self): - visits = Visit.objects.filter(subject=self, is_finished=False) - for visit in visits: - visit.is_finished = True - visit.save() - - def finish_all_appointments(self): - appointments = Appointment.objects.filter(visit__subject=self, status=Appointment.APPOINTMENT_STATUS_SCHEDULED) - for appointment in appointments: - appointment.status = Appointment.APPOINTMENT_STATUS_CANCELLED - appointment.save() - - def mark_as_dead(self): - self.dead = True - self.finish_all_visits() - self.finish_all_appointments() - - def mark_as_resigned(self): - self.resigned = True - self.finish_all_visits() - self.finish_all_appointments() - sex = models.CharField(max_length=1, choices=SEX_CHOICES, verbose_name='Sex' ) - postponed = models.BooleanField(choices=BOOL_CHOICES, - verbose_name='Postponed', - default=False - ) - datetime_contact_reminder = models.DateTimeField( - null=True, - blank=True, - verbose_name='Please make a contact on', - ) - type = models.CharField(max_length=1, - choices=SUBJECT_TYPE_CHOICES.items(), - verbose_name='Type' - ) - - default_location = models.ForeignKey(Location, - verbose_name='Default appointment location', - ) - - flying_team = models.ForeignKey("web.FlyingTeam", - verbose_name='Default flying team location (if applicable)', - null=True, blank=True - ) first_name = models.CharField(max_length=50, verbose_name='First name' ) + last_name = models.CharField(max_length=50, verbose_name='Last name' ) + languages = models.ManyToManyField(Language, blank=True, verbose_name='Known languages' ) + default_written_communication_language = models.ForeignKey(Language, null=True, blank=True, - related_name="%(class)s_written_comunication", + related_name="subjects_written_communication", verbose_name='Default language for document generation' ) phone_number = models.CharField(max_length=20, @@ -81,103 +39,76 @@ class Subject(models.Model): blank=True, verbose_name='Phone number' ) + phone_number_2 = models.CharField(max_length=20, null=True, blank=True, verbose_name='Phone number 2' ) + phone_number_3 = models.CharField(max_length=20, null=True, blank=True, verbose_name='Phone number 3' ) + email = models.EmailField( null=True, blank=True, verbose_name='E-mail' ) + date_born = models.DateField( null=True, blank=True, verbose_name='Date of birth (YYYY-MM-DD)' ) + address = models.CharField(max_length=255, blank=True, verbose_name='Address' ) + postal_code = models.CharField(max_length=7, blank=True, verbose_name='Postal code' ) + city = models.CharField(max_length=50, blank=True, verbose_name='City' ) + country = models.ForeignKey(Country, null=False, blank=False, default=COUNTRY_OTHER_ID, verbose_name='Country' ) - screening_number = models.CharField(max_length=50, - unique=True, - verbose_name='Screening number', blank=False, null=False - ) - nd_number = models.CharField(max_length=25, - blank=True, - verbose_name='ND number', - validators=[ - RegexValidator('^(ND[0-9]{4}|)$', message="ND number should look as follows: NDxxxx")] - ) - mpower_id = models.CharField(max_length=20, - blank=True, - verbose_name='MPower ID' - ) - comments = models.TextField(max_length=2000, - blank=True, - verbose_name='Comments' - ) - date_added = models.DateField(verbose_name='Added on', - auto_now=True - ) - referral = models.CharField(max_length=128, - null=True, - blank=True, - verbose_name='Referred by' - ) - diagnosis = models.CharField(max_length=128, - null=True, - blank=True, - verbose_name='Diagnosis' - ) - year_of_diagnosis = models.IntegerField( - null=True, - blank=True, - verbose_name='Year of diagnosis (YYYY)' - ) - information_sent = models.BooleanField( - verbose_name='Information sent', - default=False - ) - pd_in_family = models.NullBooleanField( - verbose_name='PD in family', - default=None, - ) dead = models.BooleanField( verbose_name='Deceased', default=False, editable=True ) - resigned = models.BooleanField( - verbose_name='Resigned', - default=False, - editable=True - ) - resign_reason = models.TextField(max_length=2000, - blank=True, - verbose_name='Resign reason' - ) + + def mark_as_dead(self): + self.dead = True + self.finish_all_visits() + self.finish_all_appointments() + + def finish_all_visits(self): + visits = Visit.objects.filter(subject__subject=self, is_finished=False) + for visit in visits: + visit.is_finished = True + visit.save() + + def finish_all_appointments(self): + appointments = Appointment.objects.filter(visit__subject__subject=self, + status=Appointment.APPOINTMENT_STATUS_SCHEDULED) + for appointment in appointments: + appointment.status = Appointment.APPOINTMENT_STATUS_CANCELLED + appointment.save() def __str__(self): return "%s %s" % (self.first_name, self.last_name) diff --git a/smash/web/models/visit.py b/smash/web/models/visit.py index 1f491319e8f87585433ee7c1129f1b48c152ef33..61b29704181cff23b8091e5fb43c0e01232a3e88 100644 --- a/smash/web/models/visit.py +++ b/smash/web/models/visit.py @@ -12,7 +12,7 @@ class Visit(models.Model): class Meta: app_label = 'web' - subject = models.ForeignKey("web.Subject", on_delete=models.CASCADE, + subject = models.ForeignKey("web.StudySubject", on_delete=models.CASCADE, verbose_name='Subject' ) datetime_begin = models.DateTimeField( @@ -42,16 +42,16 @@ class Visit(models.Model): ) def __unicode__(self): - return "%s %s" % (self.subject.first_name, self.subject.last_name) + return "%s %s" % (self.subject.subject.first_name, self.subject.subject.last_name) def __str__(self): - return "%s %s" % (self.subject.first_name, self.subject.last_name) + return "%s %s" % (self.subject.subject.first_name, self.subject.subject.last_name) def mark_as_finished(self): self.is_finished = True self.save() - if (not self.subject.dead) and (not self.subject.resigned): + if (not self.subject.subject.dead) and (not self.subject.resigned): visit_started = Visit.objects.filter(subject=self.subject).filter(visit_number=1)[0].datetime_begin follow_up_number = Visit.objects.filter(subject=self.subject).count() + 1 diff --git a/smash/web/redcap_connector.py b/smash/web/redcap_connector.py index bca70fc0951a5894d2557f83a03f271eb216ce85..8a2828583aee2e92ff795c73ae3182fc505d5e9b 100644 --- a/smash/web/redcap_connector.py +++ b/smash/web/redcap_connector.py @@ -7,7 +7,7 @@ import pycurl import certifi from django_cron import CronJobBase, Schedule -from web.models import ConfigurationItem, Subject, Language +from web.models import ConfigurationItem, StudySubject, Language from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, \ REDCAP_BASE_URL_CONFIGURATION_TYPE from web.models.inconsistent_subject import InconsistentField, InconsistentSubject @@ -85,7 +85,7 @@ class RedcapConnector(object): for subject in red_cap_subjects: red_cap_subject_by_nd[subject.nd_number] = subject - smash_subjects = Subject.objects.exclude(nd_number='') + smash_subjects = StudySubject.objects.exclude(nd_number='') smash_subject_by_nd = {} for subject in smash_subjects: smash_subject_by_nd[subject.nd_number] = subject @@ -158,7 +158,7 @@ class RedcapConnector(object): for subject in red_cap_subjects: red_cap_subject_by_nd[subject.nd_number] = subject - smash_subjects = Subject.objects.exclude(nd_number='') + smash_subjects = StudySubject.objects.exclude(nd_number='') result = [] for subject in smash_subjects: @@ -173,14 +173,14 @@ class RedcapConnector(object): return result @staticmethod - def create_inconsistency_subject(red_cap_subject, subject, url): + def create_inconsistency_subject(red_cap_subject, study_subject, url): fields = [] - if subject.sex != red_cap_subject.sex: - field = InconsistentField.create("sex", subject.sex, red_cap_subject.sex) + if study_subject.subject.sex != red_cap_subject.sex: + field = InconsistentField.create("sex", study_subject.subject.sex, red_cap_subject.sex) fields.append(field) subject_date_born = "" - if subject.date_born is not None: - subject_date_born = subject.date_born.strftime('%Y-%m-%d') + if study_subject.subject.date_born is not None: + subject_date_born = study_subject.subject.date_born.strftime('%Y-%m-%d') redcap_subject_date_born = red_cap_subject.date_born if redcap_subject_date_born is None: redcap_subject_date_born = "" @@ -189,23 +189,23 @@ class RedcapConnector(object): if subject_date_born != redcap_subject_date_born: field = InconsistentField.create("date of birth", subject_date_born, redcap_subject_date_born) fields.append(field) - if subject.dead != red_cap_subject.dead: - field = InconsistentField.create("dead", str(subject.dead), str(red_cap_subject.dead)) + if study_subject.subject.dead != red_cap_subject.dead: + field = InconsistentField.create("dead", str(study_subject.subject.dead), str(red_cap_subject.dead)) fields.append(field) - if different_string(subject.mpower_id, red_cap_subject.mpower_id): - field = InconsistentField.create("mpower id", subject.mpower_id, red_cap_subject.mpower_id) + if different_string(study_subject.mpower_id, red_cap_subject.mpower_id): + field = InconsistentField.create("mpower id", study_subject.mpower_id, red_cap_subject.mpower_id) fields.append(field) missing_language = False if len(red_cap_subject.languages) < 4: - for language in subject.languages.all(): + for language in study_subject.subject.languages.all(): if language not in red_cap_subject.languages: missing_language = True for language in red_cap_subject.languages: - if language not in subject.languages.all(): + if language not in study_subject.subject.languages.all(): missing_language = True if missing_language: subject_languages = "" - for language in subject.languages.all(): + for language in study_subject.subject.languages.all(): subject_languages += language.name + ", " red_cap_subject_languages = "" for language in red_cap_subject.languages: @@ -214,7 +214,7 @@ class RedcapConnector(object): fields.append(field) result = None if len(fields) > 0: - result = InconsistentSubject.create(smash_subject=subject, url=url, fields=fields) + result = InconsistentSubject.create(smash_subject=study_subject, url=url, fields=fields) return result def create_redcap_link(self, pid, redcap_version, subject): diff --git a/smash/web/statistics.py b/smash/web/statistics.py index 63a710dca60acf2a2269e7a27d41ff9318ca2e92..f94df0a8a5c6a318c0b4f6ee6bb766732201d722 100644 --- a/smash/web/statistics.py +++ b/smash/web/statistics.py @@ -16,28 +16,28 @@ SELECT DISTINCT(rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin)) A """ QUERY_APPOINTMENTS_COUNT = """ -SELECT count(*) FROM web_appointment LEFT JOIN (SELECT id, subject_id, rank() +SELECT count(*) FROM web_appointment LEFT JOIN (SELECT id, subject_id as web_visit_subject_id, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a ON a.id = web_appointment.visit_id -LEFT JOIN web_subject ON web_subject.id = subject_id +LEFT JOIN web_studysubject ON web_studysubject.id = web_visit_subject_id WHERE a.rnk = %s AND EXTRACT(MONTH FROM web_appointment.datetime_when) = %s AND EXTRACT(YEAR FROM web_appointment.datetime_when) = %s """ QUERY_VISITS_ENDED_COUNT = """ -SELECT count(*) FROM (SELECT id, subject_id, datetime_begin, datetime_end, rank() +SELECT count(*) FROM (SELECT id, subject_id as web_visit_subject_id, datetime_begin, datetime_end, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a -LEFT JOIN web_subject ON web_subject.id = subject_id +LEFT JOIN web_studysubject ON web_studysubject.id = web_visit_subject_id WHERE a.rnk = %s AND EXTRACT(MONTH FROM a.datetime_end) = %s AND EXTRACT(YEAR FROM a.datetime_end) = %s """ QUERY_VISITS_STARTED_COUNT = """ -SELECT count(*) FROM (SELECT id, subject_id, datetime_begin, datetime_end, rank() +SELECT count(*) FROM (SELECT id, subject_id as web_visit_subject_id, datetime_begin, datetime_end, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a -LEFT JOIN web_subject ON web_subject.id = subject_id +LEFT JOIN web_studysubject ON web_studysubject.id = web_visit_subject_id WHERE a.rnk = %s AND EXTRACT(MONTH FROM a.datetime_begin) = %s AND EXTRACT(YEAR FROM a.datetime_begin) = %s @@ -45,10 +45,10 @@ EXTRACT(YEAR FROM a.datetime_begin) = %s QUERY_APPOINTMENTS = """ SELECT types.appointment_type_id, web_appointment.status, count(*) FROM web_appointment -LEFT JOIN (SELECT id, subject_id, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk +LEFT JOIN (SELECT id, subject_id as web_visit_subject_id, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a ON a.id = web_appointment.visit_id LEFT JOIN web_appointmenttypelink types ON types.appointment_id = web_appointment.id -LEFT JOIN web_subject ON web_subject.id = subject_id +LEFT JOIN web_studysubject ON web_studysubject.id = web_visit_subject_id WHERE a.rnk = %s AND EXTRACT(MONTH FROM web_appointment.datetime_when) = %s AND EXTRACT(YEAR FROM web_appointment.datetime_when) = %s @@ -136,7 +136,7 @@ class StatisticsManager(object): query = QUERY_APPOINTMENTS subject_type_clause = "" if subject_type is not None: - subject_type_clause = " AND web_subject.type = '{}'".format(subject_type) + subject_type_clause = " AND web_studysubject.type = '{}'".format(subject_type) query = query.format(subject_type_clause) with connection.cursor() as cursor: cursor.execute(query, [visit, month, year]) @@ -159,7 +159,7 @@ class StatisticsManager(object): def _get_count_from_filters_or_sql(model, filters, query, visit, month, year, subject_type): if visit: if subject_type is not None: - query += " AND web_subject.type = '{}'".format(subject_type) + query += " AND web_studysubject.type = '{}'".format(subject_type) with connection.cursor() as cursor: cursor.execute( query, @@ -208,7 +208,7 @@ class StatisticsManager(object): def _get_visits_ranks(subject_type=None): query = QUERY_VISITS_RANKS if subject_type is not None: - query += " LEFT JOIN web_subject ON web_subject.id = web_visit.subject_id WHERE web_subject.type = '{}'".format( + query += " LEFT JOIN web_studysubject ON web_studysubject.id = web_visit.subject_id WHERE web_studysubject.type = '{}'".format( subject_type) with connection.cursor() as cursor: cursor.execute( diff --git a/smash/web/templates/appointments/add.html b/smash/web/templates/appointments/add.html index 1cfc3c883b50b027f97ed385d08855e67f279f0b..b29fc125c36b8e753afa41b7067714f4132f7f2f 100644 --- a/smash/web/templates/appointments/add.html +++ b/smash/web/templates/appointments/add.html @@ -133,14 +133,15 @@ document.getElementById("id_datetime_when").value = dateString; }, - events: get_calendar_events_function("{% url 'web.api.appointments' full_list %}", false), + events: get_calendar_events_function("{% url 'web.api.appointments' full_list %}", false) }); }); - appointment_type_behaviour($("input[name='appointment_types']"), $("input[name='length']"), "{% url 'web.api.appointment_types' %}"); + var lengthInput = $("input[name='length']"); + appointment_type_behaviour($("input[name='appointment_types']"), lengthInput, "{% url 'web.api.appointment_types' %}"); appointment_flying_team_place_behaviour($("select[name='flying_team']"), $("select[name='location']")); - appointment_date_change_behaviour($("input[name='datetime_when']"), $("select[name='worker_assigned']"), $("input[name='length']")); + appointment_date_change_behaviour($("input[name='datetime_when']"), $("select[name='worker_assigned']"), lengthInput); </script> {% include "includes/datetimepicker.js.html" %} diff --git a/smash/web/templates/appointments/edit.html b/smash/web/templates/appointments/edit.html index d87475c2b2e8403a4e619da027ff9c7b53db6ace..1d7cfa2ab4d86d3105f38041af240c4202339785 100644 --- a/smash/web/templates/appointments/edit.html +++ b/smash/web/templates/appointments/edit.html @@ -42,7 +42,7 @@ <h3 class="box-title">Appointment's details</h3> </div> <div class="box-body"> - {% for field in form %} + {% for field in appointment_form %} <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}"> <label for="{# TODO #}" class="col-sm-4 control-label"> {{ field.label }} @@ -87,6 +87,30 @@ </div><!-- /.box-body --> </fieldset> + <fieldset> + <div class="box-header with-border"> + <h3 class="box-title">Subject's study details</h3> + </div> + + <div class="box-body"> + {% for field in study_subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label for="{# TODO #}" class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> {{ field.errors }} </span> + {% endif %} + </div> + {% endfor %} + </div><!-- /.box-body --> + + </fieldset> {% endif %} <div class="box-footer"> <div class="col-sm-3"> @@ -138,9 +162,10 @@ "autoWidth": false }); }); - appointment_type_behaviour($("input[name='appointment-appointment_types']"), $("input[name='appointment-length']"), "{% url 'web.api.appointment_types' %}"); + var appointmentLengthInput = $("input[name='appointment-length']"); + appointment_type_behaviour($("input[name='appointment-appointment_types']"), appointmentLengthInput, "{% url 'web.api.appointment_types' %}"); appointment_flying_team_place_behaviour($("select[name='appointment-flying_team']"), $("select[name='appointment-location']")); - appointment_date_change_behaviour($("input[name='appointment-datetime_when']"), $("select[name='appointment-worker_assigned']"), $("input[name='appointment-length']"), {{ appointment.id }}); + appointment_date_change_behaviour($("input[name='appointment-datetime_when']"), $("select[name='appointment-worker_assigned']"), appointmentLengthInput, {{ appointment.id }}); </script> diff --git a/smash/web/templates/export/index.html b/smash/web/templates/export/index.html index a4e8aef298bbe802d4997a9f54f325ca03b8d0c5..39276ce9354c7af97029bb0aa4665ddbd8be01ff 100644 --- a/smash/web/templates/export/index.html +++ b/smash/web/templates/export/index.html @@ -24,12 +24,12 @@ <li><a href="{% url 'web.views.export_to_excel' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS - Excel</a> </li> - <li><a href="{% url 'web.views.export_to_excel' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV - + <li><a href="{% url 'web.views.export_to_csv' 'subjects' %}"><i class="fa fa-file-text-o"></i> CSV - Text based</a></li> </ul> <h3>Appointments</h3> <ul> - <li><a href="{% url 'web.views.export_to_csv' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS - + <li><a href="{% url 'web.views.export_to_excel' 'appointments' %}"><i class="fa fa-file-excel-o"></i> XLS - Excel</a></li> <li><a href="{% url 'web.views.export_to_csv' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV - Text based</a></li> diff --git a/smash/web/templates/subjects/add.html b/smash/web/templates/subjects/add.html index 317c54e6953fe565d2e5e01216834c30228340a8..3bacf8021599c202596190da36dc00db059e1f88 100644 --- a/smash/web/templates/subjects/add.html +++ b/smash/web/templates/subjects/add.html @@ -23,37 +23,60 @@ {% block maincontent %} {% block content %} - <div class="box box-info"> + <div class="row"> <div class="box-header with-border"> <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default">Cancel</a> </div> - {% comment %} <div class="box-header with-border"> - <h3 class="box-title">Details of subject</h3> - </div>{% endcomment %} + </div> + <div class="row"> <form method="post" action="" class="form-horizontal"> {% csrf_token %} - - <div class="box-body"> - <div class="col-sm-6"> - {% for field in form %} - <div class="form-group {% if field.errors %}has-error{% endif %}"> - <label class="col-sm-4 control-label"> - {{ field.label }} - </label> - - <div class="col-sm-8"> - {{ field|add_class:'form-control' }} - </div> - - {% if field.errors %} - <span class="help-block"> - {{ field.errors }} - </span> - {% endif %} + <div class="col-md-12"> + <div class="box box-body"> + <div class="box-header with-border"> + <h3>Subject data</h3> + </div> + <div class="box-body"> + <div class="col-md-12"> + {% for field in subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block">{{ field.errors }}</span> + {% endif %} + </div> + {% endfor %} </div> - {% endfor %} + </div> + <div class="box-header with-border"> + <h3>Study data</h3> + </div> + <div class="box-body"> + {% for field in study_subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block">{{ field.errors }}</span> + {% endif %} + </div> + {% endfor %} + </div> </div> </div><!-- /.box-body --> @@ -79,17 +102,28 @@ <script src="{% static 'AdminLTE/plugins/awesomplete/awesomplete.min.js' %}"></script> <script> $(document).ready(function () { + + var cityInput = document.querySelector("#id_subject-city"); + var referralInput = document.querySelector("#id_study_subject-referral"); + var lastNameInput = $("input[name='subject-last_name']"); + var firstNameInput = $("input[name='subject-first_name']"); + + if (cityInput === null || referralInput === null || lastNameInput.length === 0 || firstNameInput.length === 0) { + alert("Internal error. Contact administrators."); + } + // If ever to debug and thinking why it doesn't work => look if there is 'null' in data from API $.get("{% url 'web.api.cities' %}", function (data) { - new Awesomplete(document.querySelector("#id_city")).list = data.cities; + new Awesomplete(cityInput).list = data.cities; }); $.get("{% url 'web.api.referrals' %}", function (data) { - new Awesomplete(document.querySelector("#id_referral")).list = data.referrals; + new Awesomplete(referralInput).list = data.referrals; }); + var checkDuplicates = function (e) { - var first_name = $("input[name='first_name']").val(); - var last_name = $("input[name='last_name']").val(); + var first_name = firstNameInput.val(); + var last_name = lastNameInput.val(); if (last_name !== "") { var url = "{% url 'web.api.subjects' 'GENERIC' %}"; url += "?columns[0][data]=first_name&columns[0][search][value]=" + first_name; @@ -102,19 +136,21 @@ } }); if (subjects.length > 0) { - $("#duplicate_warning").css("display", "block"); + var duplicateWarningDiv = $("#duplicate_warning"); + duplicateWarningDiv.css("display", "block"); var content = "There are possible duplicate(s) with the same name:<br/>"; $.each(subjects, function (index, subject) { content += subject.first_name + " " + subject.last_name + ", born: " + subjects.date_born + ", screening number: " + subject.screening_number + "<br/>" }); - $("#duplicate_warning").html(content); + duplicateWarningDiv.html(content); } }); } }; - $("input[name='last_name']").after("<div id='duplicate_warning' class='btn-warning' style='display:none'>message</div>"); - $("input[name='last_name']").on('keyup', checkDuplicates); - $("input[name='first_name']").on('keyup', checkDuplicates); + lastNameInput.after("<div id='duplicate_warning' class='btn-warning' style='display:none'>message</div>"); + + lastNameInput.on('keyup', checkDuplicates); + firstNameInput.on('keyup', checkDuplicates); }); diff --git a/smash/web/templates/subjects/delete.html b/smash/web/templates/subjects/delete.html deleted file mode 100644 index 372dce816701f74a8fc01ef7078452ed61004f20..0000000000000000000000000000000000000000 --- a/smash/web/templates/subjects/delete.html +++ /dev/null @@ -1,93 +0,0 @@ -{% extends "_base.html" %} -{% load static %} -{% load filters %} - -{% block styles %} - {{ block.super }} - <!-- DataTables --> - <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}"> -{% endblock styles %} - -{% block ui_active_tab %}'subjects'{% endblock ui_active_tab %} -{% block page_header %}Are you sure you want to remove information concerning this subject?{% endblock page_header %} -{% block page_description %}{% endblock page_description %} - -{% block title %}{{ block.super }} - Delete subject information{% endblock %} - -{% block breadcrumb %} - {% include "subjects/breadcrumb.html" %} -{% endblock breadcrumb %} - -{% block maincontent %} - - - {% block content %} - <div class="box box-info"> - <div class="box-header with-border"> - <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" onclick="history.back()">Go - back (without changes)</a> - </div> - - {% comment %} <div class="box-header with-border"> - <h3 class="box-title">Details of subject</h3> - </div>{% endcomment %} - - <form method="post" action="" class="form-horizontal"> - {% csrf_token %} - - <div class="box-body"> - {% for field in form %} - <div class="col-md-6 form-group"> - <label class="col-sm-4 control-label"> - {{ field.label }} - </label> - - <div class="col-sm-8"> - {{ field|disable|add_class:'form-control' }} - </div> - - {% if field.help_text %} - <span class="help-block"> - {{ field.help_text }} - </span> - {% endif %} - </div> - {% endfor %} - </div><!-- /.box-body --> - - <div class="box-footer"> - <div class="col-sm-6"> - <button type="submit" class="btn btn-block btn-danger">Delete</button> - </div> - <div class="col-sm-6"> - <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" - onclick="history.back()">Cancel</a> - </div> - </div><!-- /.box-footer --> - </form> - </div> - {% endblock %} - - - - -{% endblock maincontent %} - -{% block scripts %} - {{ block.super }} - - <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script> - <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script> - <script> - $(function () { - $('#table').DataTable({ - "paging": true, - "lengthChange": false, - "searching": true, - "ordering": true, - "info": true, - "autoWidth": false - }); - }); - </script> -{% endblock scripts %} diff --git a/smash/web/templates/subjects/details.html b/smash/web/templates/subjects/details.html deleted file mode 100644 index d3cd52aae7946204bc849ce185a5f1259cc76658..0000000000000000000000000000000000000000 --- a/smash/web/templates/subjects/details.html +++ /dev/null @@ -1,89 +0,0 @@ -{% extends "_base.html" %} -{% load static %} -{% load filters %} - -{% block styles %} - {{ block.super }} - <!-- DataTables --> - <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}"> -{% endblock styles %} - -{% block ui_active_tab %}'subjects'{% endblock ui_active_tab %} -{% block page_header %}Details of the subject{% endblock page_header %} -{% block page_description %}{% endblock page_description %} - -{% block title %}{{ block.super }} - Details of subject {% endblock %} - -{% block breadcrumb %} - {% include "subjects/breadcrumb.html" %} -{% endblock breadcrumb %} - -{% block maincontent %} - - {% block content %} - <div class="box box-info"> - <div class="box-header with-border"> - <a href="javascript:history.back(1)" class="btn btn-block btn-default">Back</a> - </div> - - {% comment %} <div class="box-header with-border"> - <h3 class="box-title">Details of subject</h3> - </div>{% endcomment %} - - <form class="form-horizontal"> - <div class="box-body"> - {% for field in form %} - <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> - <label for="{# TODO #}" class="col-sm-4 control-label"> - {{ field.label }} - </label> - - <div class="col-sm-8"> - {{ field|disable|add_class:'form-control' }} - </div> - - {% if field.errors %} - <span class="help-block"> - {{ field.errors }} - </span> - {% endif %} - </div> - {% endfor %} - </div><!-- /.box-body --> - - <a href="{% url 'web.views.subject_visit_details' sid %}" type="button" - class="btn btn-block btn-default">Subject's visits</a> - - - <div class="box-footer"> - <a href="javascript:history.back(1)" class="btn btn-block btn-default">Back</a> - </div><!-- /.box-footer --> - </form> - </div> - - </form> - {% endblock %} - - - - -{% endblock maincontent %} - -{% block scripts %} - {{ block.super }} - - <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script> - <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script> - <script> - $(function () { - $('#table').DataTable({ - "paging": true, - "lengthChange": false, - "searching": true, - "ordering": true, - "info": true, - "autoWidth": false - }); - }); - </script> -{% endblock scripts %} diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html index 609cfbe22dab89900bfeb0343eeacf2bb54dbe3c..7e956ad711830c9c1e318edb5b474346910ff521 100644 --- a/smash/web/templates/subjects/edit.html +++ b/smash/web/templates/subjects/edit.html @@ -26,12 +26,10 @@ {% block content %} <div class="row"> <p class="col-lg-3 pull-left"> - <a href="javascript:history.back(1)" class="btn btn-block btn-default" - ">Go - back (discard changes)</a> + <a href="javascript:history.back(1)" class="btn btn-block btn-default">Go back (discard changes)</a> </p> <p class="col-md-2 pull-right"> - <a href="{% url 'web.views.subject_visit_details' subject.id %}" type="button" + <a href="{% url 'web.views.subject_visit_details' study_subject.id %}" type="button" class="btn btn-block btn-default">Subject's visits</a> </p> </div> @@ -44,11 +42,34 @@ </div> <form method="post" action="" class="form-horizontal"> + {% csrf_token %} <div class="box-body"> <div class="col-md-12"> - {% csrf_token %} - {% for field in form %} + {% for field in subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label for="{# TODO #}" class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> {{ field.errors }} </span> + {% endif %} + </div> + {% endfor %} + + </div> + </div><!-- /.box-body --> + <div class="box-header with-border"> + <h3>Subject's study details</h3> + </div> + <div class="box-body"> + <div class="col-md-12"> + {% for field in study_subject_form %} <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> <label for="{# TODO #}" class="col-sm-4 control-label"> {{ field.label }} @@ -88,9 +109,9 @@ </div><!-- /.col-md-12 --> </div><!-- /.row --> - {% include 'includes/mail_templates_box.html' with instance_id=subject.id %} + {% include 'includes/mail_templates_box.html' with instance_id=study_subject.id %} - {% include 'includes/contact_attempts_box.html' with subject=subject contact_attempts=contact_attempts %} + {% include 'includes/contact_attempts_box.html' with subject=study_subject contact_attempts=contact_attempts %} <div class="modal modal-danger fade" id="confirm-dead-resigned-mark-dialog" tabindex="-1" role="dialog"> @@ -152,18 +173,28 @@ "info": true, "autoWidth": false }); - var dead_was_checked = $("#id_dead").is(":checked"); - var resigned_was_checked = $("#id_resigned").is(":checked"); - $("#id_resign_reason").prop('disabled', !resigned_was_checked); - $("#id_resigned").on("click", function(){ - var checked = $("#id_resigned").is(":checked"); - $("#id_resign_reason").prop('disabled', !checked); + var deadCheckbox = $("#id_subject-dead"); + var resignedCheckbox = $("#id_study_subject-resigned"); + var resignReasonInput = $("#id_study_subject-resign_reason"); + var screeningNumberInput = $("input[name='screening_number']"); + + if (deadCheckbox.length === 0 || resignedCheckbox.length === 0 || resignReasonInput.length === 0 || screeningNumberInput === null) { + alert("Internal error. Contact administrators"); + } + + var dead_was_checked = deadCheckbox.is(":checked"); + var resigned_was_checked = resignedCheckbox.is(":checked"); + + resignReasonInput.prop('disabled', !resigned_was_checked); + resignedCheckbox.on("click", function () { + var checked = resignedCheckbox.is(":checked"); + resignReasonInput.prop('disabled', !checked); }); $("form").submit(function () { if (confirmed) { return true; } - if ((!dead_was_checked && $("#id_dead").is(":checked")) || (!resigned_was_checked && $("#id_resigned").is(":checked"))) { + if ((!dead_was_checked && deadCheckbox.is(":checked")) || (!resigned_was_checked && resignedCheckbox.is(":checked"))) { confirmed = false; $("#confirm-dead-resigned-mark-dialog").modal("show"); return false; @@ -177,15 +208,16 @@ $("#confirm-screening-change").click(function () { $('#enable-screening-number-dialog').modal('hide'); - $("input[name='screening_number']").attr("readonly", false); - $("input[name='screening_number']")[0].focus(); + screeningNumberInput.attr("readonly", false); + screeningNumberInput[0].focus(); }); - $("input[name='screening_number']").attr("readonly", true); - $("input[name='screening_number']").after("<div><a class='btn btn-block btn-danger' onclick='show_screening_number_enable_dialog()'>modify<a/></div>"); + screeningNumberInput.attr("readonly", true); + screeningNumberInput.after("<div><a class='btn btn-block btn-danger' onclick='show_screening_number_enable_dialog()'>modify<a/></div>"); }); + function show_screening_number_enable_dialog() { $('#enable-screening-number-dialog').modal('show'); } diff --git a/smash/web/templates/visits/details.html b/smash/web/templates/visits/details.html index f6c632b961d953120b79982937a94a509a5cde74..ed73408d2e1030b164c7a6e38be41df5b83c9fb8 100644 --- a/smash/web/templates/visits/details.html +++ b/smash/web/templates/visits/details.html @@ -36,7 +36,7 @@ <form method="post" action="" class="form-horizontal"> {% csrf_token %} <div class="box-body"> - {% for field in vform %} + {% for field in visit_form %} {% if not field|is_checkbox %} <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}"> <label for="{# TODO #}" class="col-sm-4 control-label">{{ field.label }}</label> @@ -155,7 +155,32 @@ <form class="form-horizontal"> <div class="box-body"> - {% for field in sform %} + {% for field in subject_form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label for="{# TODO #}" class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|disable|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> + {{ field.errors }} + </span> + {% endif %} + </div> + {% endfor %} + </div><!-- /.box-body --> + + </form> + <div class="box-header with-border"> + <h3 class="box-title">Subject's study details</h3> + </div> + <form class="form-horizontal"> + <div class="box-body"> + {% for field in study_subject_form %} <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> <label for="{# TODO #}" class="col-sm-4 control-label"> {{ field.label }} @@ -174,11 +199,11 @@ {% endfor %} </div><!-- /.box-body --> - <div class="box-footer"> - <td><a href="{% url 'web.views.subject_edit' visit.subject.id %}" type="button" - class="btn btn-block btn-default">Edit subject</a></td> - </div><!-- /.box-footer --> </form> + <div class="box-footer"> + <td><a href="{% url 'web.views.subject_edit' visit.subject.id %}" type="button" + class="btn btn-block btn-default">Edit subject</a></td> + </div><!-- /.box-footer --> </div> {% include 'includes/mail_templates_box.html' with instance_id=visit.id %} diff --git a/smash/web/tests/api_views/test_appointment.py b/smash/web/tests/api_views/test_appointment.py index b6e11b47ab024922ac51394591356279f0166517..8e73b87519c8e8653fed2a95d3bb58c5651272e1 100644 --- a/smash/web/tests/api_views/test_appointment.py +++ b/smash/web/tests/api_views/test_appointment.py @@ -8,15 +8,15 @@ from django.test import TestCase from django.urls import reverse from web.models import AppointmentTypeLink -from web.tests.functions import create_subject, create_worker, create_visit, create_appointment, \ +from web.tests.functions import create_study_subject, create_worker, create_visit, create_appointment, \ create_appointment_type, create_get_suffix from web.views.appointment import APPOINTMENT_LIST_GENERIC, APPOINTMENT_LIST_APPROACHING, APPOINTMENT_LIST_UNFINISHED from web.views.notifications import get_today_midnight_date -class TestApi(TestCase): +class TestAppointmentApi(TestCase): def setUp(self): - self.subject = create_subject() + self.study_subject = create_study_subject() self.client = Client() username = 'piotr' password = 'top_secret' @@ -31,9 +31,9 @@ class TestApi(TestCase): def test_appointments_valid(self): name = "Piotrek" - self.subject.first_name = name - self.subject.save() - visit = create_visit(self.subject) + self.study_subject.subject.first_name = name + self.study_subject.subject.save() + visit = create_visit(self.study_subject) create_appointment(visit) appointment2 = create_appointment(visit, get_today_midnight_date()) appointment2.visit = None @@ -50,9 +50,9 @@ class TestApi(TestCase): def test_appointments_approaching(self): name = "Piotrek" - self.subject.first_name = name - self.subject.save() - visit = create_visit(self.subject) + self.study_subject.subject.first_name = name + self.study_subject.subject.save() + visit = create_visit(self.study_subject) create_appointment(visit, get_today_midnight_date() + datetime.timedelta(days=2)) url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_APPROACHING}) @@ -63,9 +63,9 @@ class TestApi(TestCase): def test_appointments_unfinished(self): name = "Piotrek" - self.subject.first_name = name - self.subject.save() - visit = create_visit(self.subject) + self.study_subject.subject.first_name = name + self.study_subject.subject.save() + visit = create_visit(self.study_subject) create_appointment(visit, get_today_midnight_date() + datetime.timedelta(days=-12)) url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_UNFINISHED}) @@ -76,9 +76,9 @@ class TestApi(TestCase): def test_get_calendar_appointments(self): name = "Peter" - self.subject.first_name = name - self.subject.save() - visit = create_visit(self.subject) + self.study_subject.subject.first_name = name + self.study_subject.subject.save() + visit = create_visit(self.study_subject) appointment = create_appointment(visit, get_today_midnight_date()) appointment.save() diff --git a/smash/web/tests/api_views/test_daily_planning.py b/smash/web/tests/api_views/test_daily_planning.py index 257e4d7abdd681e9a03c028189879b05160b4622..abaea166149c9dbf02967115040209f5fcaa9cb8 100644 --- a/smash/web/tests/api_views/test_daily_planning.py +++ b/smash/web/tests/api_views/test_daily_planning.py @@ -10,7 +10,7 @@ from django.urls import reverse from web.api_views.daily_planning import get_workers_for_daily_planning, get_generic_appointment_events from web.models import Worker, Availability, Holiday, AppointmentTypeLink from web.models.constants import TUESDAY_AS_DAY_OF_WEEK -from web.tests.functions import create_worker, create_subject, create_appointment, create_flying_team, create_visit, \ +from web.tests.functions import create_worker, create_study_subject, create_appointment, create_flying_team, create_visit, \ create_appointment_type, get_test_location @@ -38,7 +38,7 @@ class TestApi(TestCase): flying_team = create_flying_team() flying_team.place = "UTF name: ät" flying_team.save() - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit) diff --git a/smash/web/tests/api_views/test_subject.py b/smash/web/tests/api_views/test_subject.py index 7fafc2f84a3705ade0f09c9701f60412b022f67d..c7ea23613277ecb644b9403f1db1989dd09a960b 100644 --- a/smash/web/tests/api_views/test_subject.py +++ b/smash/web/tests/api_views/test_subject.py @@ -1,22 +1,25 @@ # coding=utf-8 -import json import datetime +import json +import logging from django.contrib.auth.models import User from django.test import Client from django.test import TestCase from django.urls import reverse -from web.views.notifications import get_today_midnight_date from web.api_views.subject import get_subjects_order, get_subjects_filtered, serialize_subject, SUBJECT_LIST_GENERIC, \ SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT -from web.models import Subject, Appointment, Visit -from web.tests.functions import create_subject, create_worker, create_get_suffix, create_visit, create_appointment +from web.models import StudySubject, Appointment +from web.tests.functions import create_study_subject, create_worker, create_get_suffix, create_visit, create_appointment +from web.views.notifications import get_today_midnight_date + +logger = logging.getLogger(__name__) class TestApi(TestCase): def setUp(self): - self.subject = create_subject() + self.study_subject = create_study_subject() self.client = Client() username = 'piotr' password = 'top_secret' @@ -35,8 +38,8 @@ class TestApi(TestCase): self.assertFalse(city_name in cities) - self.subject.city = city_name - self.subject.save() + self.study_subject.subject.city = city_name + self.study_subject.subject.save() response = self.client.get(reverse('web.api.cities')) cities = json.loads(response.content)['cities'] @@ -53,8 +56,8 @@ class TestApi(TestCase): self.assertFalse(referral_name in referrals) - self.subject.referral = referral_name - self.subject.save() + self.study_subject.referral = referral_name + self.study_subject.save() response = self.client.get(reverse('web.api.referrals')) referrals = json.loads(response.content)['referrals'] @@ -79,8 +82,8 @@ class TestApi(TestCase): def test_subjects_general_search(self): name = "Piotrek" - self.subject.first_name = name - self.subject.save() + self.study_subject.subject.first_name = name + self.study_subject.subject.save() params = { "columns[0][search][value]": "another_name", @@ -98,163 +101,163 @@ class TestApi(TestCase): self.assertTrue(name in response.content) def check_subject_filtered(self, filters, result): - subjects = get_subjects_filtered(Subject.objects.all(), filters) + subjects = get_subjects_filtered(StudySubject.objects.all(), filters) self.assertEqual(len(result), subjects.count()) for index in range(len(result)): self.assertEqual(result[index], subjects[index]) def check_subject_ordered(self, order, result): - subjects = get_subjects_order(Subject.objects.all(), order, "asc") + subjects = get_subjects_order(StudySubject.objects.all(), order, "asc") self.assertEqual(len(result), subjects.count()) for index in range(len(result)): self.assertEqual(result[index], subjects[index]) - subjects = get_subjects_order(Subject.objects.all(), order, "desc") + subjects = get_subjects_order(StudySubject.objects.all(), order, "desc") length = len(result) self.assertEqual(length, subjects.count()) for index in range(length): self.assertEqual(result[length - index - 1], subjects[index]) def test_subjects_sort_nd_number(self): - subject = self.subject + subject = self.study_subject subject.nd_number = "PPP" subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.nd_number = "QQQ" subject2.save() self.check_subject_ordered("nd_number", [subject, subject2]) def test_subjects_sort_id(self): - subject = self.subject + subject = self.study_subject - subject2 = create_subject(2) + subject2 = create_study_subject(2) self.check_subject_ordered("id", [subject, subject2]) def test_subjects_sort_date_born(self): - subject = self.subject - subject.date_born = get_today_midnight_date() - subject.save() + subject = self.study_subject + subject.subject.date_born = get_today_midnight_date() + subject.subject.save() - subject2 = create_subject(2) - subject2.date_born = get_today_midnight_date() + datetime.timedelta(days=1) - subject2.save() + subject2 = create_study_subject(2) + subject2.subject.date_born = get_today_midnight_date() + datetime.timedelta(days=1) + subject2.subject.save() self.check_subject_ordered("date_born", [subject, subject2]) def test_subjects_sort_default_location(self): - subject = self.subject + subject = self.study_subject self.check_subject_ordered("default_location", [subject]) def test_subjects_sort_screening_number(self): - subject = self.subject + subject = self.study_subject subject.screening_number = "PPP" subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.screening_number = "QQQ" subject2.save() self.check_subject_ordered("screening_number", [subject, subject2]) def test_subjects_sort_last_name(self): - subject = self.subject - subject.last_name = "XXX" - subject.save() + subject = self.study_subject + subject.subject.last_name = "XXX" + subject.subject.save() - subject2 = create_subject(2) - subject2.last_name = "YYY" - subject2.save() + subject2 = create_study_subject(2) + subject2.subject.last_name = "YYY" + subject2.subject.save() self.check_subject_ordered("last_name", [subject, subject2]) def test_subjects_sort_dead(self): - subject = self.subject - subject.dead = True - subject.save() + study_subject = self.study_subject + study_subject.subject.dead = True + study_subject.subject.save() - subject2 = create_subject(2) - subject2.dead = False - subject2.save() + study_subject2 = create_study_subject(2) + study_subject2.subject.dead = False + study_subject2.subject.save() - self.check_subject_ordered("dead", [subject2, subject]) + self.check_subject_ordered("dead", [study_subject2, study_subject]) def test_subjects_sort_resigned(self): - subject = self.subject + subject = self.study_subject subject.resigned = True subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.resigned = False subject2.save() self.check_subject_ordered("resigned", [subject2, subject]) def test_subjects_sort_postponed(self): - subject = self.subject + subject = self.study_subject subject.postponed = True subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.postponed = False subject2.save() self.check_subject_ordered("postponed", [subject2, subject]) def test_subjects_filter_dead(self): - subject = self.subject + subject = self.study_subject.subject subject.dead = True subject.save() - subject2 = create_subject(2) - subject2.dead = False - subject2.save() + study_subject2 = create_study_subject(2) + study_subject2.subject.dead = False + study_subject2.subject.save() - self.check_subject_filtered([["dead", "true"]], [subject]) - self.check_subject_filtered([["dead", "false"]], [subject2]) + self.check_subject_filtered([["dead", "true"]], [self.study_subject]) + self.check_subject_filtered([["dead", "false"]], [study_subject2]) def test_subjects_filter_nd_number(self): - subject = self.subject + subject = self.study_subject subject.nd_number = "PPP" subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.nd_number = "QQQ" subject2.save() self.check_subject_filtered([["nd_number", "P"]], [subject]) def test_subjects_filter_screening_number(self): - subject = self.subject + subject = self.study_subject subject.screening_number = "PPP" subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.screening_number = "QQQ" subject2.save() self.check_subject_filtered([["screening_number", "Q"]], [subject2]) def test_subjects_filter_last_name(self): - subject = self.subject - subject.last_name = "XXX" - subject.save() + subject = self.study_subject + subject.subject.last_name = "XXX" + subject.subject.save() - subject2 = create_subject(2) - subject2.last_name = "YYY" - subject2.save() + subject2 = create_study_subject(2) + subject2.subject.last_name = "YYY" + subject2.subject.save() self.check_subject_filtered([["last_name", "Q"]], []) def test_subjects_filter_resigned(self): - subject = self.subject + subject = self.study_subject subject.resigned = True subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.resigned = False subject2.save() @@ -262,11 +265,11 @@ class TestApi(TestCase): self.check_subject_filtered([["resigned", "false"]], [subject2]) def test_subjects_filter_postponed(self): - subject = self.subject + subject = self.study_subject subject.postponed = True subject.save() - subject2 = create_subject(2) + subject2 = create_study_subject(2) subject2.postponed = False subject2.save() @@ -274,28 +277,26 @@ class TestApi(TestCase): self.check_subject_filtered([["postponed", "false"]], [subject2]) def test_subjects_filter_default_location(self): - subject = self.subject + subject = self.study_subject self.check_subject_filtered([["default_location", str(subject.default_location.id)]], [subject]) self.check_subject_filtered([["default_location", "-1"]], []) def test_subjects_filter_unknown(self): - subject = self.subject + subject = self.study_subject self.check_subject_filtered([["some_unknown", "unknown data"]], [subject]) self.check_subject_filtered([["", ""]], [subject]) def test_serialize_subject(self): - subject = self.subject - subject.dead = True + study_subject = self.study_subject + study_subject.subject.dead = True - subject_json = serialize_subject(subject) + subject_json = serialize_subject(study_subject) self.assertEqual("YES", subject_json["dead"]) def test_subjects_filter_visit_1_DONE(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) appointment = create_appointment(visit) @@ -313,9 +314,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "UPCOMING"]], []) def test_subjects_filter_visit_1_MISSED(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) appointment = create_appointment(visit) @@ -333,9 +332,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "UPCOMING"]], []) def test_subjects_filter_visit_1_EXCEED(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=-365 * 2) @@ -352,9 +349,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "UPCOMING"]], []) def test_subjects_filter_visit_1_IN_PROGRESS(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) appointment = create_appointment(visit) @@ -371,9 +366,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "UPCOMING"]], []) def test_subjects_filter_visit_1_SHOULD_BE_IN_PROGRESS(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) @@ -387,9 +380,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "UPCOMING"]], []) def test_subjects_filter_visit_1_UPCOMING(self): - subject = self.subject - subject.dead = True - subject.save() + subject = self.study_subject visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=2) @@ -406,8 +397,7 @@ class TestApi(TestCase): self.check_subject_filtered([["visit_1", "SHOULD_BE_IN_PROGRESS"]], []) def test_subjects_filter_visit_1_visit_2_combined(self): - subject = self.subject - subject.save() + subject = self.study_subject visit = create_visit(subject) appointment = create_appointment(visit) diff --git a/smash/web/tests/api_views/test_worker.py b/smash/web/tests/api_views/test_worker.py index 33e4b71dcea12e2aec30eb85cc4b24ff6f382467..0b32a0b321347ea85467de611b981e1d551508b7 100644 --- a/smash/web/tests/api_views/test_worker.py +++ b/smash/web/tests/api_views/test_worker.py @@ -73,7 +73,7 @@ class TestApi(LoggedInWithWorkerTestCase): result = availabilities(request) entries = json.loads(result.content)['availabilities'] - count = 0; + count = 0 for entry in entries: count += len(entry["workers"]) self.assertTrue(count > 0) diff --git a/smash/web/tests/forms/test_SubjectAddForm.py b/smash/web/tests/forms/test_StudySubjectAddForm.py similarity index 66% rename from smash/web/tests/forms/test_SubjectAddForm.py rename to smash/web/tests/forms/test_StudySubjectAddForm.py index 907cfe39d93200d6bfedff92d7f18a4463d085fa..a417a5bb3f11f562dbf2121b02c4074d3f5f45ff 100644 --- a/smash/web/tests/forms/test_SubjectAddForm.py +++ b/smash/web/tests/forms/test_StudySubjectAddForm.py @@ -1,25 +1,28 @@ -from web.forms import SubjectAddForm, get_new_screening_number -from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, COUNTRY_AFGHANISTAN_ID +import logging + +from web.forms import StudySubjectAddForm, get_new_screening_number +from web.models.constants import SUBJECT_TYPE_CHOICES_CONTROL from web.tests import LoggedInWithWorkerTestCase -from web.tests.functions import create_subject +from web.tests.functions import create_study_subject, create_subject + +logger = logging.getLogger(__name__) -class SubjectAddFormTests(LoggedInWithWorkerTestCase): +class StudySubjectAddFormTests(LoggedInWithWorkerTestCase): def setUp(self): - super(SubjectAddFormTests, self).setUp() + super(StudySubjectAddFormTests, self).setUp() location = self.worker.locations.all()[0] - self.sample_data = {'first_name': 'name', - 'last_name': 'name', - 'sex': SEX_CHOICES_MALE, - 'type': SUBJECT_TYPE_CHOICES_CONTROL, - 'default_location': location.id, - 'screening_number': "123", - 'country': COUNTRY_AFGHANISTAN_ID, - } + self.subject = create_subject() + self.sample_data = { + 'type': SUBJECT_TYPE_CHOICES_CONTROL, + 'default_location': location.id, + 'screening_number': "123", + 'subject': self.subject.id + } def test_validation(self): - form = SubjectAddForm(data=self.sample_data, user=self.user) + form = StudySubjectAddForm(data=self.sample_data, user=self.user) form.is_valid() self.assertTrue(form.is_valid()) @@ -27,13 +30,14 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): form_data = self.sample_data form_data['screening_number'] = "123" - form = SubjectAddForm(data=form_data, user=self.user) + form = StudySubjectAddForm(data=form_data, user=self.user) form.is_valid() + form.instance.subject_id = self.subject.id self.assertTrue(form.is_valid()) self.assertIsNone(form.fields['year_of_diagnosis'].initial) form.save() - form2 = SubjectAddForm(data=form_data, user=self.user) + form2 = StudySubjectAddForm(data=form_data, user=self.user) validation_status = form2.is_valid() self.assertFalse(validation_status) self.assertTrue("screening_number" in form2.errors) @@ -42,13 +46,14 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): form_data = self.sample_data form_data['nd_number'] = "ND0123" - form = SubjectAddForm(data=form_data, user=self.user) + form = StudySubjectAddForm(data=form_data, user=self.user) form.is_valid() self.assertTrue(form.is_valid()) + form.instance.subject_id = self.subject.id form.save() form_data['screening_number'] = "2" - form2 = SubjectAddForm(data=form_data, user=self.user) + form2 = StudySubjectAddForm(data=form_data, user=self.user) validation_status = form2.is_valid() self.assertFalse(validation_status) self.assertTrue("nd_number" in form2.errors) @@ -57,20 +62,21 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): form_data = self.sample_data form_data['mpower_id'] = "123" - form = SubjectAddForm(data=form_data, user=self.user) + form = StudySubjectAddForm(data=form_data, user=self.user) form.is_valid() self.assertTrue(form.is_valid()) + form.instance.subject_id = self.subject.id form.save() form_data['screening_number'] = "2" - form2 = SubjectAddForm(data=form_data, user=self.user) + form2 = StudySubjectAddForm(data=form_data, user=self.user) validation_status = form2.is_valid() self.assertFalse(validation_status) self.assertTrue("mpower_id" in form2.errors) def test_get_new_screening_number(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = prefix + "300" subject.save() @@ -79,7 +85,7 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): def test_get_new_screening_number_2(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = "L-1111; " + prefix + "300" subject.save() @@ -88,7 +94,7 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): def test_get_new_screening_number_3(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = "P-1112; " + prefix + "300" + "; L-1111" subject.save() @@ -97,7 +103,7 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): def test_get_new_screening_number_4(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = "P-1112; " subject.save() @@ -106,19 +112,19 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): def test_get_new_screening_number_5(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = prefix + "100" subject.save() - subject = create_subject() + subject = create_study_subject() subject.screening_number = prefix + "200" subject.save() - subject = create_subject() + subject = create_study_subject() subject.screening_number = "Y-300" subject.save() - subject = create_subject() + subject = create_study_subject() subject.screening_number = prefix + "20" subject.save() @@ -127,7 +133,7 @@ class SubjectAddFormTests(LoggedInWithWorkerTestCase): def test_get_new_screening_number_6(self): prefix = "X-" - subject = create_subject() + subject = create_study_subject() subject.screening_number = "X-" subject.save() diff --git a/smash/web/tests/forms/test_StudySubjectEditForm.py b/smash/web/tests/forms/test_StudySubjectEditForm.py new file mode 100644 index 0000000000000000000000000000000000000000..5f838cff2724de1c31ae353a5e828d426d3bb002 --- /dev/null +++ b/smash/web/tests/forms/test_StudySubjectEditForm.py @@ -0,0 +1,60 @@ +import logging + +from web.forms import StudySubjectEditForm +from web.models import StudySubject +from web.tests import LoggedInWithWorkerTestCase +from web.tests.functions import create_study_subject + +logger = logging.getLogger(__name__) + + +class StudySubjectEditFormTests(LoggedInWithWorkerTestCase): + def setUp(self): + super(StudySubjectEditFormTests, self).setUp() + self.study_subject = create_study_subject() + + location = self.worker.locations.all()[0] + self.sample_data = { + 'type': self.study_subject.type, + 'default_location': location.id, + 'screening_number': self.study_subject.screening_number, + 'nd_number': self.study_subject.nd_number, + 'subject': self.study_subject.subject.id, + 'id': self.study_subject.id + } + + def tearDown(self): + StudySubject.objects.all().delete() + + def test_validation(self): + edit_form = StudySubjectEditForm(self.sample_data) + save_status = edit_form.is_valid() + self.assertTrue(save_status) + + def test_invalid_nd_number_edit(self): + study_subject2 = create_study_subject(124) + study_subject2.nd_number = "ND0124" + study_subject2.screening_number = "124" + study_subject2.save() + + self.sample_data['nd_number'] = "ND0124" + edit_form = StudySubjectEditForm(self.sample_data) + + save_status = edit_form.is_valid() + self.assertTrue("nd_number" in edit_form.errors) + self.assertFalse(save_status) + + def test_invalid_mpower_id_edit(self): + self.study_subject.mpower_id = "mpower_002" + self.study_subject.nd_number = "ND0002" + self.study_subject.screening_number = "002" + self.study_subject.save() + + self.sample_data['mpower_id'] = "mpower_002" + self.sample_data['nd_number'] = "ND0001" + self.sample_data['screening_number'] = "001" + edit_form = StudySubjectEditForm(self.sample_data) + + save_status = edit_form.is_valid() + self.assertTrue("mpower_id" in edit_form.errors) + self.assertFalse(save_status) diff --git a/smash/web/tests/forms/test_SubjectEditForm.py b/smash/web/tests/forms/test_SubjectEditForm.py deleted file mode 100644 index 3de9b95dd970d2aeeb0b029ec32af74c5413b420..0000000000000000000000000000000000000000 --- a/smash/web/tests/forms/test_SubjectEditForm.py +++ /dev/null @@ -1,69 +0,0 @@ -from web.forms import SubjectAddForm -from web.forms import SubjectEditForm -from web.models import Subject -from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, COUNTRY_AFGHANISTAN_ID -from web.tests import LoggedInWithWorkerTestCase - - -class SubjectEditFormTests(LoggedInWithWorkerTestCase): - def setUp(self): - super(SubjectEditFormTests, self).setUp() - - location = self.worker.locations.all()[0] - self.sample_data = {'first_name': 'name', - 'last_name': 'name', - 'sex': SEX_CHOICES_MALE, - 'type': SUBJECT_TYPE_CHOICES_CONTROL, - 'default_location': location.id, - 'country': COUNTRY_AFGHANISTAN_ID, - 'screening_number': '123', - 'nd_number': 'ND0123' - } - - def tearDown(self): - Subject.objects.all().delete() - - def test_validation(self): - add_form = SubjectAddForm(data=self.sample_data, user=self.user) - subject = add_form.save() - self.sample_data['id'] = subject.id - - edit_form = SubjectEditForm(self.sample_data) - save_status = edit_form.is_valid() - self.assertTrue(save_status) - - def test_invalid_nd_number_edit(self): - add_form = SubjectAddForm(data=self.sample_data, user=self.user) - add_form.save() - - self.sample_data['nd_number'] = "ND0124" - self.sample_data['screening_number'] = "124" - add_form = SubjectAddForm(data=self.sample_data, user=self.user) - subject = add_form.save() - - self.sample_data['id'] = subject.id - self.sample_data['nd_number'] = "ND0123" - edit_form = SubjectEditForm(self.sample_data) - - save_status = edit_form.is_valid() - self.assertFalse(save_status) - - def test_invalid_mpower_id_edit(self): - self.sample_data['screening_number'] = "001" - add_form = SubjectAddForm(data=self.sample_data, user=self.user) - subject = add_form.save() - - self.sample_data['mpower_id'] = "mpower_002" - self.sample_data['nd_number'] = "ND0002" - self.sample_data['screening_number'] = "002" - add_form = SubjectAddForm(data=self.sample_data, user=self.user) - add_form.save() - - self.sample_data['id'] = subject.id - self.sample_data['mpower_id'] = "mpower_002" - self.sample_data['nd_number'] = "ND0001" - self.sample_data['screening_number'] = "001" - edit_form = SubjectEditForm(self.sample_data) - - save_status = edit_form.is_valid() - self.assertFalse(save_status) diff --git a/smash/web/tests/forms/test_VisitAddForm.py b/smash/web/tests/forms/test_VisitAddForm.py index b6a0d817ad2b577e83bcbf9a03fbfd71b3baf6f7..14fbed837708331b331bdaf415d859ce9a13ca7b 100644 --- a/smash/web/tests/forms/test_VisitAddForm.py +++ b/smash/web/tests/forms/test_VisitAddForm.py @@ -3,14 +3,14 @@ from __future__ import print_function from django.test import TestCase from web.forms import VisitAddForm -from web.tests.functions import create_subject +from web.tests.functions import create_study_subject from web.tests.functions import get_test_location class VisitAddFormTests(TestCase): def setUp(self): get_test_location() - self.subject = create_subject() + self.subject = create_study_subject() self.sample_data = {'datetime_begin': "2017-01-01", 'datetime_end': "2017-02-02", diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index aa6ee1114a859f7df40d03b71f71c5821f22a10f..adb1eaf8511656ec1b92688405d4979d893ccb53 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -3,8 +3,8 @@ import os from django.contrib.auth.models import User -from web.models import Location, AppointmentType, Subject, Worker, Visit, Appointment, ConfigurationItem, Language, \ - ContactAttempt, FlyingTeam, Availability +from web.models import Location, AppointmentType, StudySubject, Worker, Visit, Appointment, ConfigurationItem, Language, \ + ContactAttempt, FlyingTeam, Availability, Subject from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, REDCAP_BASE_URL_CONFIGURATION_TYPE, \ SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, CONTACT_TYPES_PHONE, \ MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID @@ -41,7 +41,7 @@ def create_appointment_type(): def create_contact_attempt(subject=None, worker=None): if subject is None: - subject = create_subject() + subject = create_study_subject() if worker is None: worker = create_worker() @@ -54,15 +54,23 @@ def create_contact_attempt(subject=None, worker=None): ) -def create_subject(subject_id=1): +def create_subject(): return Subject.objects.create( first_name="Piotr", last_name="Gawron", - default_location=get_test_location(), sex=SEX_CHOICES_MALE, + country_id=COUNTRY_AFGHANISTAN_ID + ) + + +def create_study_subject(subject_id=1, subject=None): + if subject is None: + subject = create_subject() + return StudySubject.objects.create( + default_location=get_test_location(), type=SUBJECT_TYPE_CHOICES_CONTROL, screening_number="piotr's number" + str(subject_id), - country_id=COUNTRY_AFGHANISTAN_ID + subject=subject ) @@ -112,7 +120,7 @@ def create_availability(worker=None): def create_visit(subject=None): if subject is None: - subject = create_subject() + subject = create_study_subject() return Visit.objects.create(datetime_begin=get_today_midnight_date() + datetime.timedelta(days=-31), datetime_end=get_today_midnight_date() + datetime.timedelta(days=31), subject=subject, diff --git a/smash/web/tests/models/test_appointment.py b/smash/web/tests/models/test_appointment.py index a781e69c9ec36a99bd92e3cbf5616d16ee8d2b4d..3568ad558b96ab0c2b9e38a54d4d0a1d9ee69d53 100644 --- a/smash/web/tests/models/test_appointment.py +++ b/smash/web/tests/models/test_appointment.py @@ -1,7 +1,11 @@ +import logging + from django.test import TestCase from web.tests.functions import create_appointment +logger = logging.getLogger(__name__) + class AppointmentModelTests(TestCase): def test_title_for_appointment_without_visit(self): @@ -12,3 +16,7 @@ class AppointmentModelTests(TestCase): appointment.save() self.assertEqual(comment, appointment.title()) + + def test_title_for_appointment_with_visit(self): + appointment = create_appointment() + self.assertIsNotNone(appointment.title()) diff --git a/smash/web/tests/models/test_inconsistent_subject.py b/smash/web/tests/models/test_inconsistent_subject.py index 926314158302ac7bb18ed4900a7c2ba1fa0d5216..1ee33c4e38a2e1ef0706fd3f5b2864758413251d 100644 --- a/smash/web/tests/models/test_inconsistent_subject.py +++ b/smash/web/tests/models/test_inconsistent_subject.py @@ -1,12 +1,12 @@ from django.test import TestCase from web.models.inconsistent_subject import InconsistentSubject, InconsistentField -from web.tests.functions import create_subject +from web.tests.functions import create_study_subject class InconsistentSubjectTests(TestCase): def test_create_with_empty_data(self): - subject = create_subject() + subject = create_study_subject() inconsistent_subject = InconsistentSubject.create(subject) self.assertIsNotNone(str(inconsistent_subject)) self.assertIsNotNone(unicode(inconsistent_subject)) diff --git a/smash/web/tests/models/test_mail_template.py b/smash/web/tests/models/test_mail_template.py index d718ba2e5ddf4a02f283ae4008d6297b831ec5b5..49c84a13962049a563cd007c6a2b819575e33361 100644 --- a/smash/web/tests/models/test_mail_template.py +++ b/smash/web/tests/models/test_mail_template.py @@ -7,7 +7,8 @@ from web.models import MailTemplate from web.models.constants import MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLATE_CONTEXT_VISIT, \ MAIL_TEMPLATE_CONTEXT_SUBJECT from web.models.mail_template import DATE_FORMAT_SHORT -from web.tests.functions import create_language, get_resource_path, create_appointment, create_user, create_subject, \ +from web.tests.functions import create_language, get_resource_path, create_appointment, create_user, \ + create_study_subject, \ create_visit @@ -80,7 +81,7 @@ class MailTemplateModelTests(TestCase): def test_apply_subject(self): template_name_french = "test_fr" - subject = create_subject() + subject = create_study_subject() subject_template_french = MailTemplate(name=template_name_french, language=self.french_language, context=MAIL_TEMPLATE_CONTEXT_SUBJECT, template_file=self.template_file) @@ -89,7 +90,7 @@ class MailTemplateModelTests(TestCase): doc = Document(stream) worker_name = str(self.user.worker) - self.check_doc_contains(doc, [worker_name, str(subject), str(subject.country), subject.nd_number, + self.check_doc_contains(doc, [worker_name, str(subject), str(subject.subject.country), subject.nd_number, subject.get_type_display()]) def test_apply_visit(self): @@ -103,7 +104,8 @@ class MailTemplateModelTests(TestCase): doc = Document(stream) worker_name = str(self.user.worker) - self.check_doc_contains(doc, [worker_name, str(visit.subject), str(visit.subject.country), visit.subject.nd_number, + self.check_doc_contains(doc, [worker_name, str(visit.subject), str(visit.subject.subject.country), + visit.subject.nd_number, visit.subject.get_type_display(), visit.datetime_begin.strftime(DATE_FORMAT_SHORT), visit.datetime_end.strftime(DATE_FORMAT_SHORT)]) diff --git a/smash/web/tests/models/test_missing_subject.py b/smash/web/tests/models/test_missing_subject.py index 37a7f92459ec0ad09ae3ec06203f370712eb95d7..712969a9b125ca8b4e7a6c80ed735aac3f601fa0 100644 --- a/smash/web/tests/models/test_missing_subject.py +++ b/smash/web/tests/models/test_missing_subject.py @@ -1,12 +1,12 @@ from django.test import TestCase from web.models.missing_subject import MissingSubject -from web.tests.functions import create_subject, create_red_cap_subject +from web.tests.functions import create_study_subject, create_red_cap_subject class MissingSubjectTests(TestCase): def test_create_with_empty_redcap_subject(self): - subject = create_subject() + subject = create_study_subject() missing_subject = MissingSubject.create(red_cap_subject=None, smash_subject=subject) self.assertIsNotNone(str(missing_subject)) self.assertIsNotNone(unicode(missing_subject)) diff --git a/smash/web/tests/models/test_study_subject.py b/smash/web/tests/models/test_study_subject.py new file mode 100644 index 0000000000000000000000000000000000000000..cebd56b769b72493aac87487444c0594b856bbcd --- /dev/null +++ b/smash/web/tests/models/test_study_subject.py @@ -0,0 +1,21 @@ +from django.test import TestCase + +from web.models import Appointment +from web.models import Visit +from web.tests.functions import create_study_subject, create_appointment +from web.tests.functions import create_visit + + +class SubjectModelTests(TestCase): + def test_mark_as_resigned(self): + subject = create_study_subject() + visit = create_visit(subject) + appointment = create_appointment(visit) + + subject.mark_as_resigned() + appointment_status = Appointment.objects.filter(id=appointment.id)[0].status + visit_finished = Visit.objects.filter(id=visit.id)[0].is_finished + + self.assertTrue(subject.resigned) + self.assertTrue(visit_finished) + self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) diff --git a/smash/web/tests/models/test_subject.py b/smash/web/tests/models/test_subject.py index 6266ec03e6eb3eeab698a0732d37f42425527ce5..8bfdafdd9443d24be50449052c2337cb33f79997 100644 --- a/smash/web/tests/models/test_subject.py +++ b/smash/web/tests/models/test_subject.py @@ -1,17 +1,16 @@ -from django.contrib.auth.models import User -from django.test import Client from django.test import TestCase -from web.tests.functions import create_subject, create_appointment, create_worker -from web.tests.functions import create_visit from web.models import Appointment from web.models import Visit +from web.tests.functions import create_study_subject, create_appointment +from web.tests.functions import create_visit class SubjectModelTests(TestCase): def test_mark_as_dead(self): - subject = create_subject() - visit = create_visit(subject) + study_subject = create_study_subject() + subject = study_subject.subject + visit = create_visit(study_subject) appointment = create_appointment(visit) subject.mark_as_dead() @@ -21,16 +20,3 @@ class SubjectModelTests(TestCase): self.assertTrue(subject.dead) self.assertTrue(visit_finished) self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) - - def test_mark_as_resigned(self): - subject = create_subject() - visit = create_visit(subject) - appointment = create_appointment(visit) - - subject.mark_as_resigned() - appointment_status = Appointment.objects.filter(id=appointment.id)[0].status - visit_finished = Visit.objects.filter(id=visit.id)[0].is_finished - - self.assertTrue(subject.resigned) - self.assertTrue(visit_finished) - self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) \ No newline at end of file diff --git a/smash/web/tests/models/test_visit.py b/smash/web/tests/models/test_visit.py index f4a231344474b3eb0b946ea9cb0e8b5dbc6f65a2..a247d83886c073d3d40d4318949b98817dc7924a 100644 --- a/smash/web/tests/models/test_visit.py +++ b/smash/web/tests/models/test_visit.py @@ -2,15 +2,14 @@ import datetime from django.test import TestCase -from web.models.constants import SUBJECT_TYPE_CHOICES_PATIENT from web.models import Visit -from web.tests.functions import create_subject, create_visit -from web.views.notifications import get_today_midnight_date +from web.models.constants import SUBJECT_TYPE_CHOICES_PATIENT +from web.tests.functions import create_study_subject, create_visit class VisitModelTests(TestCase): def test_mark_as_finished(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.mark_as_finished() @@ -19,18 +18,18 @@ class VisitModelTests(TestCase): self.assertEquals(2, visit_count) def test_mark_as_finished_2(self): - subject = create_subject() - visit = create_visit(subject) - subject.dead = True - subject.save() + study_subject = create_study_subject() + visit = create_visit(study_subject) + study_subject.subject.dead = True + study_subject.subject.save() visit.mark_as_finished() - visit_count = Visit.objects.filter(subject=subject).count() + visit_count = Visit.objects.filter(subject=study_subject).count() self.assertEquals(1, visit_count) def test_mark_as_finished_3(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) subject.resigned = True subject.save() @@ -41,7 +40,7 @@ class VisitModelTests(TestCase): self.assertEquals(1, visit_count) def test_mark_as_finished_for_follow_up_visit(self): - subject = create_subject() + subject = create_study_subject() subject.type = SUBJECT_TYPE_CHOICES_PATIENT subject.save() visit = create_visit(subject) @@ -64,9 +63,9 @@ class VisitModelTests(TestCase): self.assertTrue(visit.datetime_begin + datetime.timedelta(days=365 * 2 - 1) < new_follow_up.datetime_begin) def test_visit_to_string(self): - visit = create_visit(create_subject()) + visit = create_visit(create_study_subject()) self.assertIsNotNone(str(visit)) def test_visit_to_unicode(self): - visit = create_visit(create_subject()) + visit = create_visit(create_study_subject()) self.assertIsNotNone(unicode(visit)) diff --git a/smash/web/tests/test_RedcapConnector.py b/smash/web/tests/test_RedcapConnector.py index de3a1378a67218bfd393cfc4cd8efffd5f03ed02..bde702f4d61a714bbc250727f356c5d20a36c3ed 100644 --- a/smash/web/tests/test_RedcapConnector.py +++ b/smash/web/tests/test_RedcapConnector.py @@ -4,7 +4,7 @@ import logging from django.test import TestCase -from functions import create_subject, prepare_test_redcap_connection +from functions import create_study_subject, prepare_test_redcap_connection from web.models import Language from web.models.inconsistent_subject import InconsistentSubject from web.models.missing_subject import MissingSubject @@ -39,7 +39,7 @@ class TestRedcapConnector(TestCase): def test_get_invalid_connections_for_new_local(self): prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() subject.nd_number = "123" subject.save() @@ -49,7 +49,7 @@ class TestRedcapConnector(TestCase): def test_get_inconsistent_data(self): prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() # noinspection SpellCheckingInspection subject.nd_number = 'NDtest_external' subject.save() @@ -72,43 +72,43 @@ class TestRedcapConnector(TestCase): def test_create_inconsistent_data_for_date_born(self): prepare_test_redcap_connection() - subject = create_subject() + study_subject = create_study_subject() - redcap_subject = self.create_redcap_subject_from_smash_subject(subject) - subject.date_born = get_today_midnight_date() + redcap_subject = self.create_redcap_subject_from_smash_subject(study_subject) + study_subject.subject.date_born = get_today_midnight_date() - self.check_single_inconsistency(redcap_subject, subject) + self.check_single_inconsistency(redcap_subject, study_subject) - def check_single_inconsistency(self, redcap_subject, subject): + def check_single_inconsistency(self, redcap_subject, study_subject): redcap_connection = RedcapConnector() - result = redcap_connection.create_inconsistency_subject(redcap_subject, subject, "") + result = redcap_connection.create_inconsistency_subject(redcap_subject, study_subject, "") self.assertIsNotNone(result) self.assertEqual(1, len(result.fields), "Invalid number of fields. Found: " + str(result.fields)) def test_create_inconsistent_data_for_dead(self): prepare_test_redcap_connection() - subject = create_subject() + study_subject = create_study_subject() - redcap_subject = self.create_redcap_subject_from_smash_subject(subject) - subject.dead = not subject.dead + redcap_subject = self.create_redcap_subject_from_smash_subject(study_subject) + study_subject.subject.dead = not study_subject.subject.dead - self.check_single_inconsistency(redcap_subject, subject) + self.check_single_inconsistency(redcap_subject, study_subject) def test_create_inconsistent_data_for_language(self): language = Language.objects.create(name="xx") language.save() prepare_test_redcap_connection() - subject = create_subject() + study_subject = create_study_subject() - redcap_subject = self.create_redcap_subject_from_smash_subject(subject) - subject.languages.add(language) + redcap_subject = self.create_redcap_subject_from_smash_subject(study_subject) + study_subject.subject.languages.add(language) - self.check_single_inconsistency(redcap_subject, subject) + self.check_single_inconsistency(redcap_subject, study_subject) def test_create_inconsistent_data_for_language2(self): language = Language.objects.create(name="xx") prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() redcap_subject = self.create_redcap_subject_from_smash_subject(subject) redcap_subject.add_language(language) @@ -117,7 +117,7 @@ class TestRedcapConnector(TestCase): def test_create_inconsistent_data_for_mpower_id(self): prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() redcap_subject = self.create_redcap_subject_from_smash_subject(subject) subject.mpower_id = "105" @@ -126,7 +126,7 @@ class TestRedcapConnector(TestCase): def test_create_inconsistent_data_for_mpower_id_with_whitespace(self): prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() subject.mpower_id = "105" redcap_subject = self.create_redcap_subject_from_smash_subject(subject) @@ -137,15 +137,15 @@ class TestRedcapConnector(TestCase): self.assertIsNone(result) @staticmethod - def create_redcap_subject_from_smash_subject(subject): + def create_redcap_subject_from_smash_subject(study_subject): redcap_subject = RedcapSubject() - for language in subject.languages.all(): + for language in study_subject.subject.languages.all(): redcap_subject.add_language(language) - redcap_subject.mpower_id = subject.mpower_id - redcap_subject.dead = subject.dead - redcap_subject.date_born = subject.date_born - redcap_subject.nd_number = subject.nd_number - redcap_subject.sex = subject.sex + redcap_subject.mpower_id = study_subject.mpower_id + redcap_subject.dead = study_subject.subject.dead + redcap_subject.date_born = study_subject.subject.date_born + redcap_subject.nd_number = study_subject.nd_number + redcap_subject.sex = study_subject.subject.sex return redcap_subject def test_get_language(self): @@ -203,7 +203,7 @@ class TestRedcapConnector(TestCase): def test_refresh_inconsistent_data(self): prepare_test_redcap_connection() - subject = create_subject() + subject = create_study_subject() # noinspection SpellCheckingInspection subject.nd_number = 'NDtest_external' subject.save() diff --git a/smash/web/tests/view/test_appointments.py b/smash/web/tests/view/test_appointments.py index 239fb666b8b039fe5a5c0bc3783fe65105ec23db..97f26eb5c7abae165eb7f89d2766c299f9245e35 100644 --- a/smash/web/tests/view/test_appointments.py +++ b/smash/web/tests/view/test_appointments.py @@ -3,12 +3,13 @@ import logging from django.urls import reverse -from web.tests.functions import create_subject, create_visit, create_appointment, create_worker, create_flying_team, \ +from web.forms import AppointmentEditForm, StudySubjectEditForm, SubjectEditForm +from web.models import Appointment, StudySubject +from web.tests import LoggedInTestCase +from web.tests.functions import create_study_subject, create_visit, create_appointment, create_worker, \ + create_flying_team, \ format_form_field -from web.forms import AppointmentEditForm, SubjectEditForm -from web.models import Appointment, Subject from web.views.notifications import get_today_midnight_date -from web.tests import LoggedInTestCase logger = logging.getLogger(__name__) @@ -23,21 +24,13 @@ class AppointmentsViewTests(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_appointments_edit(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit, when=datetime.datetime.now()) new_comment = 'new comment' new_status = appointment.APPOINTMENT_STATUS_NO_SHOW new_last_name = "new last name" - form_appointment = AppointmentEditForm(user=self.user, instance=appointment, prefix="appointment") - form_subject = SubjectEditForm(instance=subject, prefix="subject") - form_data = {} - for key, value in form_appointment.initial.items(): - if value is not None: - form_data['appointment-{}'.format(key)] = value - for key, value in form_subject.initial.items(): - if value is not None: - form_data['subject-{}'.format(key)] = value + form_data = self.prepare_form(appointment, subject) form_data["appointment-comment"] = new_comment form_data["appointment-status"] = new_status form_data["subject-last_name"] = new_last_name @@ -45,10 +38,10 @@ class AppointmentsViewTests(LoggedInTestCase): reverse('web.views.appointment_edit', kwargs={'id': appointment.id}), data=form_data) self.assertEqual(response.status_code, 302) updated_appointment = Appointment.objects.filter(id=appointment.id)[0] - updated_subject = Subject.objects.filter(id=subject.id)[0] + updated_subject = StudySubject.objects.filter(id=subject.id)[0] self.assertEqual(new_comment, updated_appointment.comment) self.assertEqual(new_status, updated_appointment.status) - self.assertEqual(new_last_name, updated_subject.last_name) + self.assertEqual(new_last_name, updated_subject.subject.last_name) def test_appointments_edit_without_visit(self): appointment = create_appointment() @@ -59,6 +52,24 @@ class AppointmentsViewTests(LoggedInTestCase): reverse('web.views.appointment_edit', kwargs={'id': appointment.id})) self.assertEqual(response.status_code, 200) + def test_render_appointments_edit(self): + appointment = create_appointment() + appointment.comment = "APPOINTMENT_COMMENT_DATA" + appointment.save() + appointment.visit.subject.comments = "STUDY_SUBJECT_COMMENT_DATA" + appointment.visit.subject.save() + appointment.visit.subject.subject.first_name = "SUBJECT_FIRST_NAME_DATA" + appointment.visit.subject.subject.save() + + response = self.client.get( + reverse('web.views.appointment_edit', kwargs={'id': appointment.id})) + self.assertEqual(response.status_code, 200) + self.assertTrue(appointment.comment in response.content, msg="Appointment data not visible in rendered page") + self.assertTrue(appointment.visit.subject.comments in response.content, + msg="Subject study data not visible in rendered page") + self.assertTrue(appointment.visit.subject.subject.first_name in response.content, + msg="Subject data not visible in rendered page") + def test_save_appointments_edit_without_visit(self): appointment = create_appointment() appointment.visit = None @@ -91,7 +102,7 @@ class AppointmentsViewTests(LoggedInTestCase): self.assertEqual(Appointment.APPOINTMENT_STATUS_FINISHED, appointment_result.status) def test_save_appointments_edit_with_invalid_nd_number(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit, get_today_midnight_date()) @@ -103,40 +114,44 @@ class AppointmentsViewTests(LoggedInTestCase): self.assertEqual(response.status_code, 200) - updated_subject = Subject.objects.get(id=subject.id) + updated_subject = StudySubject.objects.get(id=subject.id) self.assertFalse(updated_subject.information_sent) def test_save_appointments_edit_with_valid_nd_number(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit, get_today_midnight_date()) form_data = self.prepare_form(appointment, subject) form_data["appointment-status"] = Appointment.APPOINTMENT_STATUS_FINISHED - form_data["subject-nd_number"] = "ND9999" + form_data["study-subject-nd_number"] = "ND9999" response = self.client.post( reverse('web.views.appointment_edit', kwargs={'id': appointment.id}), data=form_data) self.assertEqual(response.status_code, 302) - updated_subject = Subject.objects.get(id=subject.id) + updated_subject = StudySubject.objects.get(id=subject.id) self.assertTrue(updated_subject.information_sent) def prepare_form(self, appointment, subject): form_appointment = AppointmentEditForm(user=self.user, instance=appointment, prefix="appointment") - form_subject = SubjectEditForm(instance=subject, prefix="subject") + form_study_subject = StudySubjectEditForm(instance=subject, prefix="study-subject") + form_subject = SubjectEditForm(instance=subject.subject, prefix="study-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) + for key, value in form_study_subject.initial.items(): + if value is not None: + 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) return form_data def test_subject_flying_team_location(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit, get_today_midnight_date()) @@ -144,11 +159,11 @@ class AppointmentsViewTests(LoggedInTestCase): form_data["appointment-status"] = Appointment.APPOINTMENT_STATUS_FINISHED form_data["appointment-flying_team"] = create_flying_team().id form_data['appointment-status'] = Appointment.APPOINTMENT_STATUS_FINISHED - form_data["subject-nd_number"] = "ND9999" + form_data["study-subject-nd_number"] = "ND9999" self.client.post(reverse('web.views.appointment_edit', kwargs={'id': appointment.id}), data=form_data) - updated_subject = Subject.objects.get(id=subject.id) + updated_subject = StudySubject.objects.get(id=subject.id) self.assertIsNotNone(updated_subject.flying_team) def test_delete_appointment(self): diff --git a/smash/web/tests/view/test_configuration.py b/smash/web/tests/view/test_configuration.py index 23a147796de28f012dcf257e1dac12313160bcb3..024f5e24d59c91245253108f4cab248f32929f45 100644 --- a/smash/web/tests/view/test_configuration.py +++ b/smash/web/tests/view/test_configuration.py @@ -1,5 +1,3 @@ -import datetime - from django.urls import reverse from web.tests import LoggedInTestCase @@ -9,4 +7,3 @@ class ConfigurationViewTests(LoggedInTestCase): def test_visit_details_request(self): response = self.client.get(reverse('web.views.configuration')) self.assertEqual(response.status_code, 200) - diff --git a/smash/web/tests/view/test_contact_attempt.py b/smash/web/tests/view/test_contact_attempt.py index 9bb40d7191ef127995ec1a01f2a9618c91547971..a240ce9721866421cfde0c5bfd463b159e119594 100644 --- a/smash/web/tests/view/test_contact_attempt.py +++ b/smash/web/tests/view/test_contact_attempt.py @@ -7,18 +7,18 @@ from web.forms import ContactAttemptEditForm from web.models import ContactAttempt from web.models.constants import CONTACT_TYPES_EMAIL from web.tests import LoggedInWithWorkerTestCase -from web.tests.functions import create_subject, create_contact_attempt, format_form_field +from web.tests.functions import create_study_subject, create_contact_attempt, format_form_field class ContactAttemptViewTests(LoggedInWithWorkerTestCase): def test_contact_attempt_add_get(self): - subject = create_subject() + subject = create_study_subject() response = self.client.get(reverse('web.views.contact_add', kwargs={'subject_id': subject.id})) self.assertContains(response, 'selected">{}'.format(self.worker), 1) self.assertContains(response, 'selected">{}'.format(subject), 1) def test_contact_attempt_add_post_valid(self): - subject = create_subject() + subject = create_study_subject() self.assertEqual(0, ContactAttempt.objects.filter(subject=subject).count()) now = datetime.datetime.now() now_aware = timezone.make_aware(now, timezone.get_default_timezone()) @@ -43,7 +43,7 @@ class ContactAttemptViewTests(LoggedInWithWorkerTestCase): self.assertContains(response, comment, 1) def test_contact_attempt_add_post_invalid(self): - subject = create_subject() + subject = create_study_subject() self.assertEqual(0, ContactAttempt.objects.filter(subject=subject).count()) contact_type = CONTACT_TYPES_EMAIL comment = "this is a comment" diff --git a/smash/web/tests/view/test_export.py b/smash/web/tests/view/test_export.py index 91e1462730506a790a59ad8885ce431c60210cdb..d1394128104a6ece78d90d9b2a75ce46213687b0 100644 --- a/smash/web/tests/view/test_export.py +++ b/smash/web/tests/view/test_export.py @@ -3,13 +3,13 @@ from django.urls import reverse from web.models import Appointment from web.tests import LoggedInTestCase -from web.tests.functions import create_subject, create_appointment, create_visit +from web.tests.functions import create_study_subject, create_appointment, create_visit from web.views.export import subject_to_row_for_fields, DROP_OUT_FIELD class TestExportView(LoggedInTestCase): def test_export_subjects_to_csv(self): - create_subject() + create_study_subject() response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "subjects"})) self.assertEqual(response.status_code, 200) @@ -19,7 +19,7 @@ class TestExportView(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_export_subjects_to_excel(self): - create_subject() + create_study_subject() response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "subjects"})) self.assertEqual(response.status_code, 200) @@ -29,7 +29,7 @@ class TestExportView(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_subject_to_row_for_fields_when_not_resigned(self): - subject = create_subject() + subject = create_study_subject() subject.resigned = False subject.save() @@ -37,7 +37,7 @@ class TestExportView(LoggedInTestCase): self.assertFalse(result[0]) def test_subject_to_row_for_fields_when_resigned(self): - subject = create_subject() + subject = create_study_subject() subject.resigned = True subject.save() @@ -45,7 +45,7 @@ class TestExportView(LoggedInTestCase): self.assertFalse(result[0]) def test_subject_to_row_for_fields_when_dropped_out(self): - subject = create_subject() + subject = create_study_subject() subject.resigned = True subject.save() diff --git a/smash/web/tests/view/test_notifications.py b/smash/web/tests/view/test_notifications.py index c2586124a76f713af2746ad99f1cf5063123c773..031a225c754c4a41bb50bc81a3f8fe93f1c2b3fd 100644 --- a/smash/web/tests/view/test_notifications.py +++ b/smash/web/tests/view/test_notifications.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import AnonymousUser from web.models import Appointment, Location, AppointmentTypeLink from web.tests import LoggedInTestCase from web.tests.functions import create_appointment, create_location, create_worker, create_appointment_type -from web.tests.functions import create_subject +from web.tests.functions import create_study_subject from web.tests.functions import create_visit from web.views.notifications import \ get_approaching_visits_for_mail_contact, \ @@ -29,7 +29,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_exceeded_visit_notifications_count(self): original_notification = get_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_end = "2011-01-01" visit.save() @@ -41,7 +41,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_exceeded_visit_notifications_count_2(self): original_notification = get_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_end = "2011-01-01" visit.is_finished = True @@ -53,7 +53,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_visits_without_appointments_count(self): original_notification = get_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() create_visit(subject) notification = get_visits_without_appointments_count(self.user) @@ -97,7 +97,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_visits_without_appointments_count_2(self): appointment_type = create_appointment_type() original_notification = get_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.appointment_types.add(appointment_type) visit.save() @@ -107,7 +107,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_visits_without_appointments_count_3(self): original_notification = get_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit) @@ -118,7 +118,7 @@ class NotificationViewTests(LoggedInTestCase): self.assertEquals(original_notification.count + 1, notification.count) def test_get_unfinished_visits_order(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=-10) @@ -141,7 +141,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_without_appointments_count(self): original_notification = get_approaching_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=2) visit.save() @@ -151,7 +151,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_without_appointments_count_2(self): original_notification = get_approaching_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=2) visit.save() @@ -162,7 +162,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_without_appointments_count_3(self): original_notification = get_approaching_visits_without_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=2) visit.save() @@ -175,7 +175,7 @@ class NotificationViewTests(LoggedInTestCase): self.assertEquals(original_notification.count + 1, notification.count) def test_get_approaching_visits_without_appointments_order(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=10) @@ -198,14 +198,14 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subject_with_no_visit_notifications_count(self): original_notification = get_subject_with_no_visit_notifications_count(self.user) - create_subject() + create_study_subject() notification = get_subject_with_no_visit_notifications_count(self.user) self.assertEquals(original_notification.count + 1, notification.count) def test_get_subject_with_no_visit_notifications_count_2(self): original_notification = get_subject_with_no_visit_notifications_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.is_finished = True @@ -216,7 +216,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subject_with_no_visit_notifications_count_3(self): original_notification = get_subject_with_no_visit_notifications_count(self.user) - subject = create_subject() + subject = create_study_subject() create_visit(subject) @@ -225,16 +225,16 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subject_with_no_visit_notifications_count_4(self): original_notification = get_subject_with_no_visit_notifications_count(self.user) - subject = create_subject() - subject.dead = True - subject.save() + study_subject = create_study_subject() + study_subject.subject.dead = True + study_subject.subject.save() notification = get_subject_with_no_visit_notifications_count(self.user) self.assertEquals(original_notification.count, notification.count) def test_get_subject_with_no_visit_notifications_count_5(self): original_notification = get_subject_with_no_visit_notifications_count(self.user) - subject = create_subject() + subject = create_study_subject() subject.resigned = True subject.save() @@ -243,7 +243,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_unfinished_appointments_count(self): original_notification = get_unfinished_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit) appointment.datetime_when = "2011-01-01" @@ -255,7 +255,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_unfinished_appointments_count_2(self): original_notification = get_unfinished_appointments_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit) appointment.datetime_when = "2011-01-01" @@ -266,7 +266,7 @@ class NotificationViewTests(LoggedInTestCase): self.assertEquals(original_notification.count, notification.count) def test_get_unfinished_appointments_order(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) appointment = create_appointment(visit, get_today_midnight_date() + datetime.timedelta(days=-10)) @@ -290,7 +290,7 @@ class NotificationViewTests(LoggedInTestCase): create_location("l1") create_location("l2") original_notification = get_subject_with_no_visit_notifications_count(self.user) - create_subject() + create_study_subject() notification = get_subject_with_no_visit_notifications_count(self.user) self.assertEquals(original_notification.count + 1, notification.count) @@ -302,7 +302,7 @@ class NotificationViewTests(LoggedInTestCase): original_notification = get_subject_with_no_visit_notifications_count(worker) - subject = create_subject() + subject = create_study_subject() subject.default_location = create_location("l1") subject.save() @@ -317,7 +317,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_for_mail_contact_count(self): original_notification = get_approaching_visits_for_mail_contact_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=100) visit.save() @@ -327,7 +327,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_for_mail_contact_count_2(self): original_notification = get_approaching_visits_for_mail_contact_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=100) visit.save() @@ -338,7 +338,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_for_mail_contact_count_3(self): original_notification = get_approaching_visits_for_mail_contact_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=100) visit.save() @@ -352,7 +352,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_approaching_visits_for_mail_contact_count_4(self): original_notification = get_approaching_visits_for_mail_contact_count(self.user) - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=100) visit.post_mail_sent = True @@ -362,7 +362,7 @@ class NotificationViewTests(LoggedInTestCase): self.assertEquals(original_notification.count, notification.count) def test_get_approaching_visits_for_mail_contact_order(self): - subject = create_subject() + subject = create_study_subject() visit = create_visit(subject) visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=110) @@ -386,7 +386,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subjects_with_reminder_count(self): original_without_visit_notification = get_subject_with_no_visit_notifications_count(self.user) original_notification = get_subjects_with_reminder_count(self.user) - subject = create_subject() + subject = create_study_subject() subject.datetime_contact_reminder = get_today_midnight_date() + datetime.timedelta(days=-1) subject.save() @@ -399,7 +399,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subjects_with_reminder_count_2(self): original_without_visit_notification = get_subject_with_no_visit_notifications_count(self.user) original_notification = get_subjects_with_reminder_count(self.user) - subject = create_subject() + subject = create_study_subject() subject.datetime_contact_reminder = get_today_midnight_date() + datetime.timedelta(hours=1) subject.save() @@ -412,7 +412,7 @@ class NotificationViewTests(LoggedInTestCase): def test_get_subjects_with_reminder_count_3(self): original_without_visit_notification = get_subject_with_no_visit_notifications_count(self.user) original_notification = get_subjects_with_reminder_count(self.user) - subject = create_subject() + subject = create_study_subject() subject.datetime_contact_reminder = get_today_midnight_date() + datetime.timedelta(days=2) subject.save() diff --git a/smash/web/tests/view/test_subjects.py b/smash/web/tests/view/test_subjects.py index 834ba8d09d950625bd64d32465753c74ce3b2e9f..4617ccab3367d799f1aaefad73d5bf39f60d8072 100644 --- a/smash/web/tests/view/test_subjects.py +++ b/smash/web/tests/view/test_subjects.py @@ -1,183 +1,177 @@ import datetime +import logging from django.urls import reverse -from web.forms import SubjectAddForm, SubjectEditForm -from web.models import Subject +from web.forms import StudySubjectAddForm, StudySubjectEditForm, SubjectEditForm, SubjectAddForm +from web.models import StudySubject from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, SUBJECT_TYPE_CHOICES_PATIENT, \ COUNTRY_AFGHANISTAN_ID, COUNTRY_OTHER_ID from web.tests import LoggedInWithWorkerTestCase -from web.tests.functions import create_subject, create_visit, create_appointment, get_test_location +from web.tests.functions import create_study_subject, create_visit, create_appointment, get_test_location from web.views.notifications import get_today_midnight_date +logger = logging.getLogger(__name__) + class SubjectsViewTests(LoggedInWithWorkerTestCase): def setUp(self): super(SubjectsViewTests, self).setUp() + self.study_subject = create_study_subject() - def test_subjects_add(self): + def test_render_subjects_add(self): self.worker.save() response = self.client.get(reverse('web.views.subject_add')) self.assertEqual(response.status_code, 200) def test_render_subject_edit(self): - subject = create_subject() - - response = self.client.get(reverse('web.views.subject_edit', kwargs={'id': subject.id})) + response = self.client.get(reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id})) self.assertEqual(response.status_code, 200) def test_render_subject_visit_details(self): - subject = create_subject() - visit = create_visit(subject) + visit = create_visit(self.study_subject) create_appointment(visit) - response = self.client.get(reverse('web.views.subject_visit_details', kwargs={'id': subject.id})) + response = self.client.get(reverse('web.views.subject_visit_details', kwargs={'id': self.study_subject.id})) self.assertEqual(response.status_code, 200) self.assertFalse("Add visit" in response.content) def test_render_subject_visit_details_without_visit(self): - subject = create_subject() - - response = self.client.get(reverse('web.views.subject_visit_details', kwargs={'id': subject.id})) + response = self.client.get(reverse('web.views.subject_visit_details', kwargs={'id': self.study_subject.id})) self.assertEqual(response.status_code, 200) self.assertTrue("Add visit" in response.content) def test_save_subject_edit_when_resigned_without_reason(self): - subject = create_subject() - form_subject = SubjectEditForm(instance=subject) - form_data = {} - for key, value in form_subject.initial.items(): - if value is not None: - form_data[key] = value + form_data = self.create_edit_form_data_for_study_subject() - form_data['dead'] = "True" - form_data['resigned'] = "True" + form_data['subject-dead'] = "True" + form_data['study_subject-resigned'] = "True" response = self.client.post( - reverse('web.views.subject_edit', kwargs={'id': subject.id}), data=form_data) + reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id}), data=form_data) self.assertEqual(response.status_code, 200) self.assertTrue("Resign reason cannot be empty" in response.content) def test_save_subject_edit(self): - subject = create_subject() - form_subject = SubjectEditForm(instance=subject) - form_data = {} - for key, value in form_subject.initial.items(): - if value is not None: - form_data[key] = value + form_data = self.create_edit_form_data_for_study_subject() - form_data['dead'] = "True" - form_data['resigned'] = "True" - form_data['resign_reason'] = "doesn't want to participate" + 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': subject.id}), data=form_data) + reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id}), data=form_data) self.assertEqual(response.status_code, 302) - updated_subject = Subject.objects.filter(id=subject.id)[0] - self.assertTrue(updated_subject.dead) - self.assertTrue(updated_subject.resigned) + updated_study_subject = StudySubject.objects.filter(id=self.study_subject.id)[0] + self.assertTrue(updated_study_subject.subject.dead) + self.assertTrue(updated_study_subject.resigned) - def test_subjects_add_2(self): - - form = SubjectAddForm(user=self.user) + def create_edit_form_data_for_study_subject(self): + form_study_subject = StudySubjectEditForm(instance=self.study_subject, prefix="study_subject") + form_subject = SubjectEditForm(instance=self.study_subject.subject, prefix="subject") form_data = {} - for key, value in form.initial.items(): + for key, value in form_study_subject.initial.items(): + if value is not None: + form_data['study_subject-{}'.format(key)] = value + for key, value in form_subject.initial.items(): if value is not None: - form_data[key] = value + form_data['subject-{}'.format(key)] = value + return form_data + def create_add_form_data_for_study_subject(self): + form_study_subject = StudySubjectAddForm(prefix="study_subject", user=self.user) + form_subject = SubjectAddForm(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 + for key, value in form_subject.initial.items(): + if value is not None: + form_data['subject-{}'.format(key)] = value self.add_valid_form_data_for_subject_add(form_data) - form_data["type"] = SUBJECT_TYPE_CHOICES_CONTROL - form_data["default_location"] = get_test_location().id + return form_data + + def test_subjects_add_2(self): + form_data = self.create_add_form_data_for_study_subject() + + form_data["study_subject-type"] = SUBJECT_TYPE_CHOICES_CONTROL + form_data["study_subject-default_location"] = get_test_location().id response = self.client.post(reverse('web.views.subject_add'), data=form_data) self.assertEqual(response.status_code, 302) response = self.client.get(response.url) self.assertContains(response, "Subject created") - subject = Subject.objects.all()[0] + subject = StudySubject.objects.all().order_by("-id")[0] self.assertEqual("L-001", subject.screening_number, "prefix should start by L" + " as default location prefix is not defined and subject type is control") - @staticmethod - def add_valid_form_data_for_subject_add(form_data): - form_data["country"] = COUNTRY_AFGHANISTAN_ID - form_data["first_name"] = "John" - form_data["last_name"] = "Doe" - form_data["sex"] = SEX_CHOICES_MALE - form_data["type"] = SUBJECT_TYPE_CHOICES_PATIENT + def add_valid_form_data_for_subject_add(self, form_data): + form_data["subject-country"] = COUNTRY_AFGHANISTAN_ID + form_data["subject-first_name"] = "John" + form_data["subject-last_name"] = "Doe" + form_data["subject-sex"] = SEX_CHOICES_MALE + form_data["study_subject-type"] = SUBJECT_TYPE_CHOICES_PATIENT + form_data["study_subject-subject"] = self.study_subject.id - def test_subjects_add_patient(self): + # TODO remove after refactoring + form_data["study_subject-country"] = COUNTRY_AFGHANISTAN_ID + form_data["study_subject-first_name"] = "John" + form_data["study_subject-last_name"] = "Doe" - form = SubjectAddForm(user=self.user) - form_data = {} - for key, value in form.initial.items(): - if value is not None: - form_data[key] = value + def test_subjects_add_patient(self): + form_data = self.create_add_form_data_for_study_subject() - self.add_valid_form_data_for_subject_add(form_data) - form_data["default_location"] = get_test_location().id + form_data["study_subject-default_location"] = get_test_location().id response = self.client.post(reverse('web.views.subject_add'), data=form_data) self.assertEqual(response.status_code, 302) response = self.client.get(response.url) self.assertContains(response, "Subject created") - subject = Subject.objects.all()[0] + subject = StudySubject.objects.all().order_by("-id")[0] self.assertEqual("P-001", subject.screening_number, "prefix should start by P" + " as default location prefix is not defined and subject type is patient") def test_subjects_add_invalid(self): + form_data = self.create_add_form_data_for_study_subject() + form_data["study_subject-type"] = SUBJECT_TYPE_CHOICES_CONTROL + form_data["study_subject-default_location"] = get_test_location().id - form = SubjectAddForm(user=self.user) - form_data = {} - for key, value in form.initial.items(): - if value is not None: - form_data[key] = value - - self.add_valid_form_data_for_subject_add(form_data) - form_data["country"] = COUNTRY_OTHER_ID + form_data["subject-country"] = COUNTRY_OTHER_ID response = self.client.post(reverse('web.views.subject_add'), data=form_data) self.assertTrue("Invalid data" in response.content) def test_subjects_add_with_prefixed_location(self): - - form = SubjectAddForm(user=self.user) - form_data = {} - for key, value in form.initial.items(): - if value is not None: - form_data[key] = value + form_data = self.create_add_form_data_for_study_subject() + form_data["study_subject-type"] = SUBJECT_TYPE_CHOICES_CONTROL self.add_valid_form_data_for_subject_add(form_data) location = get_test_location() location.prefix = 'X' location.save() - form_data["default_location"] = location.id + form_data["study_subject-default_location"] = get_test_location().id response = self.client.post(reverse('web.views.subject_add'), data=form_data) self.assertEqual(response.status_code, 302) response = self.client.get(response.url) self.assertContains(response, "Subject created") - subject = Subject.objects.all()[0] + subject = StudySubject.objects.all().order_by("-id")[0] self.assertEqual("X-001", subject.screening_number, "prefix should start by X as default location prefix is equal to 'X'") def test_render_subjects(self): - create_subject() - response = self.client.get(reverse('web.views.subjects')) self.assertEqual(response.status_code, 200) def test_render_subjects_with_no_visit(self): - create_subject() - response = self.client.get(reverse('web.views.subject_no_visits')) self.assertEqual(response.status_code, 200) def test_render_subjects_require_contact(self): - subject = create_subject() - subject.datetime_contact_reminder = get_today_midnight_date() + datetime.timedelta(days=-1) + self.study_subject.datetime_contact_reminder = get_today_midnight_date() + datetime.timedelta(days=-1) response = self.client.get(reverse('web.views.subject_require_contact')) self.assertEqual(response.status_code, 200) diff --git a/smash/web/tests/view/test_visit.py b/smash/web/tests/view/test_visit.py index 217475e6822e106079f7fc52550b706bcddd3a55..8925d6d8fa8b0be13fba8098c288027c02b071b9 100644 --- a/smash/web/tests/view/test_visit.py +++ b/smash/web/tests/view/test_visit.py @@ -1,21 +1,30 @@ import datetime +import logging from django.urls import reverse from web.forms import VisitDetailForm, VisitAddForm from web.models import Visit from web.tests import LoggedInTestCase -from web.tests.functions import create_subject, create_visit, create_appointment, create_appointment_type +from web.tests.functions import create_study_subject, create_visit, create_appointment, create_appointment_type from web.views.notifications import get_today_midnight_date +logger = logging.getLogger(__name__) + class VisitViewTests(LoggedInTestCase): def test_visit_details_request(self): visit = create_visit() create_appointment(visit) + visit.subject.subject.address = "SUBJECT_ADDRESS_DATA" + visit.subject.subject.save() + visit.subject.comments = "STUDY_SUBJECT_COMMENTS_DATA" + visit.subject.save() - response = self.client.get(reverse('web.views.visit_details', args=[visit.id])) + response = self.client.get(reverse('web.views.visit_details', kwargs={'id': visit.id})) self.assertEqual(response.status_code, 200) + self.assertTrue(visit.subject.comments in response.content, msg="No study subject data in rendered response") + self.assertTrue(visit.subject.subject.address in response.content, msg="No subject data in rendered response") def test_save_visit_details(self): visit = create_visit() @@ -36,14 +45,14 @@ class VisitViewTests(LoggedInTestCase): self.assertNotContains(response, "error") def test_render_add_visit(self): - subject = create_subject() + study_subject = create_study_subject() - response = self.client.get(reverse('web.views.visit_add', kwargs={'subject_id': subject.id})) + response = self.client.get(reverse('web.views.visit_add', kwargs={'subject_id': study_subject.id})) self.assertEqual(response.status_code, 200) def test_save_add_visit(self): visit_count = Visit.objects.all().count() - subject = create_subject() + subject = create_study_subject() form = VisitAddForm() form_data = {} diff --git a/smash/web/views/appointment.py b/smash/web/views/appointment.py index 2ec6de2acb48e7232dfe72f5afe13d2d3bf59745..e7f08ea311fe1581c2c1363606c0842f11f572a2 100644 --- a/smash/web/views/appointment.py +++ b/smash/web/views/appointment.py @@ -7,8 +7,9 @@ from django.core.exceptions import ValidationError from django.shortcuts import get_object_or_404, redirect from . import wrap_response -from ..forms import AppointmentDetailForm, AppointmentAddForm, AppointmentEditForm, SubjectEditForm -from ..models import Appointment, Subject, MailTemplate +from ..forms import AppointmentDetailForm, AppointmentAddForm, AppointmentEditForm, StudySubjectEditForm, \ + SubjectEditForm +from ..models import Appointment, StudySubject, MailTemplate APPOINTMENT_LIST_GENERIC = "GENERIC" APPOINTMENT_LIST_UNFINISHED = "UNFINISHED" @@ -58,6 +59,7 @@ def appointment_add(request, visit_id=None): def appointment_edit(request, id): the_appointment = get_object_or_404(Appointment, id=id) + study_subject_form = None subject_form = None contact_attempts = None @@ -69,7 +71,12 @@ def appointment_edit(request, id): prefix="appointment") is_valid_form = True if the_appointment.visit is not None: - subject_form = SubjectEditForm(request.POST, instance=the_appointment.visit.subject, prefix="subject") + study_subject_form = StudySubjectEditForm(request.POST, instance=the_appointment.visit.subject, + prefix="study-subject") + if not study_subject_form.is_valid(): + is_valid_form = False + subject_form = SubjectEditForm(request.POST, instance=the_appointment.visit.subject.subject, + prefix="subject") if not subject_form.is_valid(): is_valid_form = False @@ -78,18 +85,19 @@ def appointment_edit(request, id): if the_appointment.visit is not None: if appointment_form.cleaned_data["status"] == Appointment.APPOINTMENT_STATUS_FINISHED: - if re.match('ND[0-9][0-9][0-9][0-9]', subject_form.cleaned_data["nd_number"]) is None: - subject_form.add_error('nd_number', ValidationError("invalid ND number")) + if re.match('ND[0-9][0-9][0-9][0-9]', study_subject_form.cleaned_data["nd_number"]) is None: + study_subject_form.add_error('nd_number', ValidationError("invalid ND number")) is_valid_form = False if is_valid_form: appointment_form.save() - if subject_form is not None: + if study_subject_form is not None: + study_subject_form.save() subject_form.save() the_appointment = get_object_or_404(Appointment, id=id) if the_appointment.status == Appointment.APPOINTMENT_STATUS_FINISHED and the_appointment.visit is not None: - subject = Subject.objects.get(id=the_appointment.visit.subject.id) + subject = StudySubject.objects.get(id=the_appointment.visit.subject.id) subject.information_sent = True if the_appointment.flying_team is not None and subject.flying_team is None: subject.flying_team = the_appointment.flying_team @@ -109,17 +117,20 @@ def appointment_edit(request, id): appointment_form = AppointmentEditForm(instance=the_appointment, user=request.user, prefix="appointment") if the_appointment.visit is not None: - subject_form = SubjectEditForm(instance=the_appointment.visit.subject, prefix="subject") + study_subject_form = StudySubjectEditForm(instance=the_appointment.visit.subject, prefix="study-subject") + subject_form = SubjectEditForm(instance=the_appointment.visit.subject.subject, + prefix="subject") contact_attempts = the_appointment.visit.subject.contactattempt_set.order_by('-datetime_when').all() languages = [] if the_appointment.visit is not None: - subject = the_appointment.visit.subject + subject = the_appointment.visit.subject.subject if subject.default_written_communication_language: languages.append(subject.default_written_communication_language) languages.extend(subject.languages.all()) return wrap_response(request, 'appointments/edit.html', { - 'form': appointment_form, + 'appointment_form': appointment_form, + 'study_subject_form': study_subject_form, 'subject_form': subject_form, 'id': id, 'appointment': the_appointment, diff --git a/smash/web/views/contact_attempt.py b/smash/web/views/contact_attempt.py index 6246cd03afc6fdea92f4e5f0bd9daea80cd8ed81..6694bc41836623bef0041fe6c7861ee2e4a4c7bc 100644 --- a/smash/web/views/contact_attempt.py +++ b/smash/web/views/contact_attempt.py @@ -2,11 +2,11 @@ from django.shortcuts import redirect, get_object_or_404 from . import wrap_response from ..forms import ContactAttemptForm, ContactAttemptEditForm -from ..models import Subject, ContactAttempt +from ..models import StudySubject, ContactAttempt def contact_add(request, subject_id): - subject = get_object_or_404(Subject, id=subject_id) + subject = get_object_or_404(StudySubject, id=subject_id) if request.method == 'POST': form = ContactAttemptForm(request.POST, user=request.user, subject=subject) form.instance.subject_id = subject_id diff --git a/smash/web/views/export.py b/smash/web/views/export.py index 5279b6b8f302574f0ffaf4f78cdb774256458f21..b5b755e61ebd600e8043c9a50259fb8802815912 100644 --- a/smash/web/views/export.py +++ b/smash/web/views/export.py @@ -7,7 +7,7 @@ from django.http import HttpResponse from notifications import get_today_midnight_date from . import e500_error, wrap_response -from ..models import Subject, Appointment +from ..models import Subject, StudySubject, Appointment @login_required @@ -65,28 +65,31 @@ def get_subjects_as_array(): result.append(field_names) - subjects = Subject.objects.order_by('-last_name') + subjects = StudySubject.objects.order_by('-subject__last_name') for subject in subjects: row = subject_to_row_for_fields(subject, subject_fields) result.append([unicode(s).replace("\n", ";").replace("\r", ";") for s in row]) return result -def subject_to_row_for_fields(subject, subject_fields): +def subject_to_row_for_fields(study_subject, subject_fields): row = [] for field in subject_fields: if field == DROP_OUT_FIELD: - if not subject.resigned: + if not study_subject.resigned: cell = False else: - finished_appointments = Appointment.objects.filter(visit__subject=subject).filter( + finished_appointments = Appointment.objects.filter(visit__subject=study_subject).filter( status=Appointment.APPOINTMENT_STATUS_FINISHED).count() if finished_appointments > 0: cell = True else: cell = False else: - cell = getattr(subject, field.name) + if hasattr(study_subject, field.name): + cell = getattr(study_subject, field.name) + elif hasattr(study_subject.subject, field.name): + cell = getattr(study_subject.subject, field.name) if cell is None: cell = "" row.append(cell) @@ -98,6 +101,9 @@ def get_default_subject_fields(): for field in Subject._meta.fields: if field.name != "ID": subject_fields.append(field) + for field in StudySubject._meta.fields: + if field.name != "ID" and field.name != "subject": + subject_fields.append(field) subject_fields.append(DROP_OUT_FIELD) return subject_fields @@ -121,8 +127,8 @@ def get_appointments_as_array(): for appointment in appointments: if appointment.visit is not None: - row = [appointment.visit.subject.nd_number, appointment.visit.subject.last_name, - appointment.visit.subject.first_name, appointment.visit.visit_number] + row = [appointment.visit.subject.nd_number, appointment.visit.subject.subject.last_name, + appointment.visit.subject.subject.first_name, appointment.visit.visit_number] else: row = ["---", "---", "---", "---"] for field in appointments_fields: diff --git a/smash/web/views/mails.py b/smash/web/views/mails.py index abe5d7f79cf1d12b087d2da1c5b66a9314f4ee1b..8ca773008e269e26c2c9a03010ff25ad5f8df05a 100644 --- a/smash/web/views/mails.py +++ b/smash/web/views/mails.py @@ -13,14 +13,14 @@ from django.views.generic import ListView from django.views.generic import UpdateView from . import wrap_response, WrappedView -from ..models import Subject, Visit, Appointment, MailTemplate +from ..models import StudySubject, Visit, Appointment, MailTemplate from ..models.constants import MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT, \ MAIL_TEMPLATE_CONTEXT_APPOINTMENT MIMETYPE_DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' CONTEXT_TYPES_MAPPING = { - MAIL_TEMPLATE_CONTEXT_SUBJECT: Subject, + MAIL_TEMPLATE_CONTEXT_SUBJECT: StudySubject, MAIL_TEMPLATE_CONTEXT_VISIT: Visit, MAIL_TEMPLATE_CONTEXT_APPOINTMENT: Appointment } diff --git a/smash/web/views/notifications.py b/smash/web/views/notifications.py index 42cbc3e41952d551f53f4d08b99c84a4ba52ecaf..3829d3ebebc09360004a2730fad67c52dd0df520 100644 --- a/smash/web/views/notifications.py +++ b/smash/web/views/notifications.py @@ -5,7 +5,7 @@ from django.utils import timezone from django.contrib.auth.models import User, AnonymousUser from django.db.models import Count, Case, When -from ..models import Worker, Subject, Visit, Appointment, Location, MissingSubject, InconsistentSubject +from ..models import Worker, StudySubject, Visit, Appointment, Location, MissingSubject, InconsistentSubject class NotificationCount(object): @@ -132,8 +132,8 @@ def get_notifications(the_user): def get_subjects_with_no_visit(user): - result = Subject.objects.annotate(my_count=Count(Case(When(visit__is_finished=False, then=1)))).filter( - dead=False, + result = StudySubject.objects.annotate(my_count=Count(Case(When(visit__is_finished=False, then=1)))).filter( + subject__dead=False, resigned=False, my_count=0, default_location__in=get_filter_locations(user), @@ -146,8 +146,8 @@ def get_subjects_with_no_visit(user): def get_subjects_with_reminder(user): tomorrow = datetime.datetime.now() + datetime.timedelta(hours=1) - result = Subject.objects.filter( - dead=False, + result = StudySubject.objects.filter( + subject__dead=False, resigned=False, default_location__in=get_filter_locations(user), datetime_contact_reminder__lt=tomorrow, diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py index dfc0c2bc0cb57991c3f49210e49a38787e3c2136..53ceafe61a78afb0d1e260df5cfb4e8248e547b5 100644 --- a/smash/web/views/subject.py +++ b/smash/web/views/subject.py @@ -1,15 +1,19 @@ # coding=utf-8 +import logging + from django.contrib import messages from django.shortcuts import redirect, get_object_or_404 from . import wrap_response -from ..forms import SubjectAddForm, SubjectEditForm, VisitDetailForm -from ..models import Subject, MailTemplate, Worker +from ..forms import StudySubjectAddForm, StudySubjectEditForm, VisitDetailForm, SubjectEditForm, SubjectAddForm +from ..models import StudySubject, MailTemplate, Worker SUBJECT_LIST_GENERIC = "GENERIC" SUBJECT_LIST_NO_VISIT = "NO_VISIT" SUBJECT_LIST_REQUIRE_CONTACT = "REQUIRE_CONTACT" +logger = logging.getLogger(__name__) + def subjects(request): context = { @@ -21,18 +25,24 @@ def subjects(request): def subject_add(request): if request.method == 'POST': - form = SubjectAddForm(request.POST, request.FILES, user=request.user) - if form.is_valid(): - form.save() + study_subject_form = StudySubjectAddForm(request.POST, request.FILES, prefix="study_subject", user=request.user) + + subject_form = SubjectAddForm(request.POST, request.FILES, prefix="subject") + if study_subject_form.is_valid() and subject_form.is_valid(): + subject = subject_form.save() + study_subject_form.instance.subject_id = subject.id + study_subject_form.save() messages.add_message(request, messages.SUCCESS, 'Subject created') - return redirect('web.views.subject_edit', id=form.instance.id) + return redirect('web.views.subject_edit', id=study_subject_form.instance.id) else: messages.add_message(request, messages.ERROR, 'Invalid data. Please fix data and try again.') else: - form = SubjectAddForm(user=request.user) + study_subject_form = StudySubjectAddForm(user=request.user, prefix="study_subject") + subject_form = SubjectAddForm(prefix="subject") - return wrap_response(request, 'subjects/add.html', {'form': form}) + return wrap_response(request, 'subjects/add.html', + {'study_subject_form': study_subject_form, 'subject_form': subject_form}) def subject_no_visits(request): @@ -50,45 +60,53 @@ def subject_require_contact(request): def subject_edit(request, id): - the_subject = get_object_or_404(Subject, id=id) - contact_attempts = the_subject.contactattempt_set.order_by('-datetime_when').all() - was_dead = the_subject.dead - was_resigned = the_subject.resigned + study_subject = get_object_or_404(StudySubject, id=id) + contact_attempts = study_subject.contactattempt_set.order_by('-datetime_when').all() + was_dead = study_subject.subject.dead + was_resigned = study_subject.resigned if request.method == 'POST': - form = SubjectEditForm(request.POST, request.FILES, instance=the_subject, was_dead=was_dead, - was_resigned=was_resigned) - if form.is_valid(): - form.save() + study_subject_form = StudySubjectEditForm(request.POST, request.FILES, instance=study_subject, + was_resigned=was_resigned, prefix="study_subject" + ) + subject_form = SubjectEditForm(request.POST, request.FILES, instance=study_subject.subject, + was_dead=was_dead, prefix="subject" + ) + if study_subject_form.is_valid() and subject_form.is_valid(): + study_subject_form.save() + subject_form.save() # check if subject was marked as dead or resigned - if form.cleaned_data['dead'] and not was_dead: - the_subject.mark_as_dead() - if form.cleaned_data['resigned'] and not was_resigned: - the_subject.mark_as_resigned() + if subject_form.cleaned_data['dead'] and not was_dead: + study_subject.subject.mark_as_dead() + if study_subject_form.cleaned_data['resigned'] and not was_resigned: + study_subject.mark_as_resigned() messages.success(request, "Modifications saved") if '_continue' in request.POST: - return redirect('web.views.subject_edit', id=the_subject.id) + return redirect('web.views.subject_edit', id=study_subject.id) return redirect('web.views.subjects') else: messages.add_message(request, messages.ERROR, 'Invalid data. Please fix data and try again.') else: - form = SubjectEditForm(instance=the_subject, was_dead=was_dead, was_resigned=was_resigned) + study_subject_form = StudySubjectEditForm(instance=study_subject, was_resigned=was_resigned, + prefix="study_subject") + subject_form = SubjectEditForm(instance=study_subject.subject, was_dead=was_dead, prefix="subject") languages = [] - if the_subject.default_written_communication_language: - languages.append(the_subject.default_written_communication_language) - languages.extend(the_subject.languages.all()) + if study_subject.subject.default_written_communication_language: + languages.append(study_subject.subject.default_written_communication_language) + languages.extend(study_subject.subject.languages.all()) return wrap_response(request, 'subjects/edit.html', { - 'form': form, - 'subject': the_subject, + 'study_subject_form': study_subject_form, + 'subject_form': subject_form, + 'study_subject': study_subject, 'contact_attempts': contact_attempts, 'mail_templates': MailTemplate.get_subject_mail_templates(languages) }) def subject_visit_details(request, id): - subject_to_be_viewed = get_object_or_404(Subject, id=id) - visits = subject_to_be_viewed.visit_set.order_by("-visit_number").all() + study_subject_to_be_viewed = get_object_or_404(StudySubject, id=id) + visits = study_subject_to_be_viewed.visit_set.order_by("-visit_number").all() visits_data = [] allow_add_visit = True for visit in visits: diff --git a/smash/web/views/visit.py b/smash/web/views/visit.py index b4f11304a30f1d2417424c1779f98ff5682bac45..2dd081106633856fa109d72fdb9db7f7ee57a2d6 100644 --- a/smash/web/views/visit.py +++ b/smash/web/views/visit.py @@ -5,8 +5,8 @@ from notifications import get_active_visits_with_missing_appointments, get_unfin get_approaching_visits_without_appointments, get_approaching_visits_for_mail_contact, get_exceeded_visits, \ waiting_for_appointment from . import wrap_response -from ..forms import VisitDetailForm, SubjectDetailForm, VisitAddForm -from ..models import Visit, Appointment, Subject, MailTemplate +from ..forms import VisitDetailForm, StudySubjectDetailForm, VisitAddForm, SubjectDetailForm +from ..models import Visit, Appointment, StudySubject, MailTemplate def visits(request): @@ -51,7 +51,7 @@ def visit_details(request, id): visit_finished = displayed_visit.is_finished visit_id = displayed_visit.id - displayed_subject = displayed_visit.subject + study_subject = displayed_visit.subject list_of_appointments = displayed_visit.appointment_set.all() can_finish = not waiting_for_appointment(displayed_visit) @@ -60,15 +60,17 @@ def visit_details(request, id): if appointment.status == Appointment.APPOINTMENT_STATUS_SCHEDULED: can_finish = False - subject_form = SubjectDetailForm(instance=displayed_subject) + study_subject_form = StudySubjectDetailForm(instance=study_subject) + subject_form = SubjectDetailForm(instance=study_subject.subject) languages = [] - if displayed_subject.default_written_communication_language: - languages.append(displayed_subject.default_written_communication_language) - languages.extend(displayed_subject.languages.all()) + if study_subject.subject.default_written_communication_language: + languages.append(study_subject.subject.default_written_communication_language) + languages.extend(study_subject.subject.languages.all()) return wrap_response(request, 'visits/details.html', { - 'vform': visit_form, - 'sform': subject_form, + 'visit_form': visit_form, + 'study_subject_form': study_subject_form, + 'subject_form': subject_form, 'loApp': list_of_appointments, 'visFinished': visit_finished, 'canFinish': can_finish, @@ -92,7 +94,7 @@ def visit_add(request, subject_id=-1): visit = form.save() return redirect('web.views.visit_details', visit.id) else: - subjects = Subject.objects.filter(id=subject_id) + subjects = StudySubject.objects.filter(id=subject_id) subject = None if len(subjects) > 0: subject = subjects[0]