diff --git a/smash/web/api_views/daily_planning.py b/smash/web/api_views/daily_planning.py index cca2057d53c58f1e41114d90968e1026ac759a51..ad3c0a94387f1c64266194de086ae60a805bb9c5 100644 --- a/smash/web/api_views/daily_planning.py +++ b/smash/web/api_views/daily_planning.py @@ -8,6 +8,7 @@ from django.http import JsonResponse from django.shortcuts import get_object_or_404 from web.models import Appointment, AppointmentTypeLink, Worker, Availability, Holiday +from web.models.worker_study_role import WORKER_STAFF from web.views import e500_error from web.views.notifications import get_filter_locations @@ -300,9 +301,9 @@ def events(request, date): def get_workers_for_daily_planning(request): - result = Worker.objects.filter(locations__in=get_filter_locations(request.user)). \ - filter(user__is_active=True).exclude( - role=Worker.ROLE_CHOICES_SECRETARY).distinct() + result = Worker.get_workers_by_worker_type(WORKER_STAFF).filter( + locations__in=get_filter_locations(request.user)).filter( + user__is_active=True).distinct() return result diff --git a/smash/web/api_views/worker.py b/smash/web/api_views/worker.py index d9edbcc1dab1a455c7c29f53165b8b278d86802a..e3ca26040d94afab46ef7be02094923a841258ee 100644 --- a/smash/web/api_views/worker.py +++ b/smash/web/api_views/worker.py @@ -4,6 +4,7 @@ from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.utils import timezone +from web.models.constants import GLOBAL_STUDY_ID from web.api_views.daily_planning import get_workers_for_daily_planning, get_availabilities from ..models import Worker @@ -29,10 +30,11 @@ def workers_for_daily_planning(request): workers = get_workers_for_daily_planning(request) workers_list_for_json = [] for worker in workers: + role = unicode(worker.roles.filter(study_id=GLOBAL_STUDY_ID)[0].role) worker_dict_for_json = { 'id': worker.id, - 'title': u"{} ({})".format(unicode(worker), worker.get_role_display()[:1].upper()), - 'role': worker.get_role_display() + 'title': u"{} ({})".format(unicode(worker), role[:1].upper()), + 'role': role } workers_list_for_json.append(worker_dict_for_json) return JsonResponse(workers_list_for_json, safe=False) diff --git a/smash/web/forms/__init__.py b/smash/web/forms/__init__.py index 23d44c9490bb74edddc5ec5ad5d436dffd829ae2..99186b1cc4900b521e0d35da9727beff58ee5c20 100644 --- a/smash/web/forms/__init__.py +++ b/smash/web/forms/__init__.py @@ -1,13 +1,15 @@ -from forms import WorkerAddForm, \ - WorkerEditForm, AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm, VisitDetailForm, VisitAddForm, \ - ContactAttemptForm, ContactAttemptEditForm, KitRequestForm, StatisticsForm, AvailabilityAddForm, \ +from worker_form import WorkerForm +from forms import VisitDetailForm, \ + VisitAddForm, KitRequestForm, StatisticsForm, AvailabilityAddForm, \ AvailabilityEditForm, HolidayAddForm +from contact_attempt_forms import ContactAttemptAddForm, ContactAttemptEditForm +from appointment_form import AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm from study_subject_forms import StudySubjectAddForm, StudySubjectDetailForm, StudySubjectEditForm from subject_forms import SubjectAddForm, SubjectEditForm, SubjectDetailForm from voucher_forms import VoucherTypeForm, VoucherTypePriceForm, VoucherForm -__all__ = [StudySubjectAddForm, StudySubjectDetailForm, StudySubjectEditForm, WorkerAddForm, WorkerEditForm, +__all__ = [StudySubjectAddForm, StudySubjectDetailForm, StudySubjectEditForm, WorkerForm, AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm, VisitDetailForm, VisitAddForm, - ContactAttemptForm, ContactAttemptEditForm, KitRequestForm, StatisticsForm, AvailabilityAddForm, + ContactAttemptAddForm, ContactAttemptEditForm, KitRequestForm, StatisticsForm, AvailabilityAddForm, AvailabilityEditForm, HolidayAddForm, SubjectAddForm, SubjectEditForm, SubjectDetailForm, VoucherTypeForm, VoucherTypePriceForm, VoucherForm] diff --git a/smash/web/forms/appointment_form.py b/smash/web/forms/appointment_form.py new file mode 100644 index 0000000000000000000000000000000000000000..83b40597bc5d3ad21a7b5927e5a5c73e59004ff1 --- /dev/null +++ b/smash/web/forms/appointment_form.py @@ -0,0 +1,134 @@ +import logging +from collections import OrderedDict + +from django import forms +from django.forms import ModelForm + +from web.models.worker_study_role import WORKER_STAFF +from web.forms.forms import DATETIMEPICKER_DATE_ATTRS, APPOINTMENT_TYPES_FIELD_POSITION +from web.models import Appointment, Worker, AppointmentTypeLink, AppointmentType +from web.views.notifications import get_filter_locations + +logger = logging.getLogger(__name__) + + +class AppointmentForm(ModelForm): + datetime_when = forms.DateTimeField(label='Appointment on', + widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) + ) + + def __init__(self, *args, **kwargs): + super(AppointmentForm, self).__init__(*args, **kwargs) + self.fields['worker_assigned'].queryset = Worker.get_workers_by_worker_type(WORKER_STAFF).filter( + locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name') + + +class AppointmentDetailForm(AppointmentForm): + class Meta: + model = Appointment + fields = '__all__' + + +class AppointmentEditForm(AppointmentForm): + class Meta: + model = Appointment + fields = '__all__' + exclude = ['appointment_types'] + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + if user is None: + raise TypeError("User not defined") + self.user = Worker.get_by_user(user) + if self.user is None: + raise TypeError("Worker not defined for: " + user.username) + super(AppointmentEditForm, self).__init__(*args, **kwargs) + if 'instance' in kwargs: + initial_appointment_types = AppointmentTypeLink.objects.filter(appointment=kwargs['instance']).values_list( + 'appointment_type', flat=True) + else: + initial_appointment_types = [] + fields = OrderedDict() + for i, tuple in enumerate(self.fields.items()): + key, value = tuple + fields[key] = value + if i == APPOINTMENT_TYPES_FIELD_POSITION: + fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False, + widget=forms.CheckboxSelectMultiple, + queryset=AppointmentType.objects.all(), + initial=initial_appointment_types) + self.fields = fields + self.fields['location'].queryset = get_filter_locations(self.user) + + def clean_location(self): + location = self.cleaned_data['location'] + if self.user.locations.filter(id=location.id).count() == 0: + self.add_error('location', "You cannot create appointment for this location") + else: + return location + + def save(self, commit=True): + appointment = super(AppointmentEditForm, self).save(commit) + # if appointment date change, remove appointment_type links + if 'datetime_when' in self.changed_data: + AppointmentTypeLink.objects.filter(appointment=appointment).delete() + appointment_type_links = [] + else: + appointment_type_links = AppointmentTypeLink.objects.filter(appointment=appointment).all() + appointment_types_from_links = [] + appointment_types = self.cleaned_data['appointment_types'] + for appointment_type_link in appointment_type_links: + if appointment_type_link.appointment_type not in appointment_types: + # we delete appointment links for appointments types that have been removed + appointment_type_link.delete() + else: + appointment_types_from_links.append(appointment_type_link.appointment_type) + for appointment_type in appointment_types: + if appointment_type not in appointment_types_from_links: + # we create new appointment links for appointments types that have been added + appointment_type_link = AppointmentTypeLink(appointment=appointment, + appointment_type=appointment_type) + appointment_type_link.save() + return appointment + + +class AppointmentAddForm(AppointmentForm): + class Meta: + model = Appointment + exclude = ['status', 'appointment_types'] + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + if user is None: + raise TypeError("User not defined") + self.user = Worker.get_by_user(user) + if self.user is None: + raise TypeError("Worker not defined for: " + user.username) + + super(AppointmentAddForm, self).__init__(*args, **kwargs) + fields = OrderedDict() + for i, tuple in enumerate(self.fields.items()): + key, value = tuple + fields[key] = value + if i == APPOINTMENT_TYPES_FIELD_POSITION: + fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False, + widget=forms.CheckboxSelectMultiple, + queryset=AppointmentType.objects.all(), + ) + self.fields = fields + self.fields['location'].queryset = get_filter_locations(self.user) + + def clean_location(self): + location = self.cleaned_data['location'] + if self.user.locations.filter(id=location.id).count() == 0: + self.add_error('location', "You cannot create appointment for this location") + else: + return location + + def save(self, commit=True): + appointment = super(AppointmentAddForm, self).save(commit) + appointment_types = self.cleaned_data['appointment_types'] + for appointment_type in appointment_types: + appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type) + appointment_type_link.save() + return appointment diff --git a/smash/web/forms/contact_attempt_forms.py b/smash/web/forms/contact_attempt_forms.py new file mode 100644 index 0000000000000000000000000000000000000000..39bb260e180536b76cf7b8c5e6ca4c59ddc12868 --- /dev/null +++ b/smash/web/forms/contact_attempt_forms.py @@ -0,0 +1,45 @@ +from django import forms +from django.forms import ModelForm + +from web.forms.forms import DATETIMEPICKER_DATE_ATTRS +from web.models import ContactAttempt, Worker +from web.models.worker_study_role import WORKER_STAFF + + +class ContactAttemptForm(ModelForm): + datetime_when = forms.DateTimeField(label='When? (YYYY-MM-DD HH:MM)', + widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) + ) + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + if user is None: + raise TypeError("User not defined") + self.user = Worker.get_by_user(user) + if self.user is None: + raise TypeError("Worker not defined for: " + user.username) + super(ContactAttemptForm, self).__init__(*args, **kwargs) + self.fields['subject'].disabled = True + self.fields['worker'].queryset = Worker.get_workers_by_worker_type(WORKER_STAFF).distinct().order_by( + 'first_name', 'last_name') + + +class ContactAttemptAddForm(ContactAttemptForm): + class Meta: + model = ContactAttempt + fields = '__all__' + + def __init__(self, *args, **kwargs): + subject = kwargs.pop('subject', None) + super(ContactAttemptAddForm, self).__init__(*args, **kwargs) + self.fields['subject'].initial = subject.id + self.fields['worker'].initial = self.user + + +class ContactAttemptEditForm(ContactAttemptForm): + class Meta: + model = ContactAttempt + fields = '__all__' + + def __init__(self, *args, **kwargs): + super(ContactAttemptEditForm, self).__init__(*args, **kwargs) diff --git a/smash/web/forms/forms.py b/smash/web/forms/forms.py index 1aa33b920dd8735547cc2b8c786b81ef79e369d6..7fc95986747cdbaf859f928cde493ebd9f53a661 100644 --- a/smash/web/forms/forms.py +++ b/smash/web/forms/forms.py @@ -1,6 +1,5 @@ import datetime import logging -from collections import OrderedDict from django import forms from django.forms import ModelForm, Form @@ -10,7 +9,6 @@ from web.models import Appointment, AppointmentType, AppointmentTypeLink, \ Availability, ContactAttempt, FlyingTeam, Holiday, StudySubject, \ Worker, Visit, VoucherType, VoucherTypePrice from web.models.constants import SUBJECT_TYPE_CHOICES -from web.views.notifications import get_filter_locations """ Possible redundancy, but if need arises, contents of forms can be easily customized @@ -50,145 +48,6 @@ def get_worker_from_args(kwargs): return result -class WorkerAddForm(ModelForm): - class Meta: - model = Worker - exclude = ['appointments'] - - -class WorkerEditForm(ModelForm): - class Meta: - model = Worker - fields = '__all__' - - -class AppointmentDetailForm(ModelForm): - class Meta: - model = Appointment - fields = '__all__' - - datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)', - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) - ) - - -class AppointmentEditForm(ModelForm): - class Meta: - model = Appointment - fields = '__all__' - exclude = ['appointment_types'] - - datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)', - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) - ) - - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - if user is None: - raise TypeError("User not defined") - self.user = Worker.get_by_user(user) - if self.user is None: - raise TypeError("Worker not defined for: " + user.username) - super(ModelForm, self).__init__(*args, **kwargs) - if 'instance' in kwargs: - initial_appointment_types = AppointmentTypeLink.objects.filter(appointment=kwargs['instance']).values_list( - 'appointment_type', flat=True) - else: - initial_appointment_types = [] - fields = OrderedDict() - for i, tuple in enumerate(self.fields.items()): - key, value = tuple - fields[key] = value - if i == APPOINTMENT_TYPES_FIELD_POSITION: - fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False, - widget=forms.CheckboxSelectMultiple, - queryset=AppointmentType.objects.all(), - initial=initial_appointment_types) - self.fields = fields - self.fields['worker_assigned'].queryset = Worker.objects.filter( - locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name') - self.fields['location'].queryset = get_filter_locations(self.user) - - def clean_location(self): - location = self.cleaned_data['location'] - if self.user.locations.filter(id=location.id).count() == 0: - self.add_error('location', "You cannot create appointment for this location") - else: - return location - - def save(self, commit=True): - appointment = super(AppointmentEditForm, self).save(commit) - # if appointment date change, remove appointment_type links - if 'datetime_when' in self.changed_data: - AppointmentTypeLink.objects.filter(appointment=appointment).delete() - appointment_type_links = [] - else: - appointment_type_links = AppointmentTypeLink.objects.filter(appointment=appointment).all() - appointment_types_from_links = [] - appointment_types = self.cleaned_data['appointment_types'] - for appointment_type_link in appointment_type_links: - if appointment_type_link.appointment_type not in appointment_types: - # we delete appointment links for appointments types that have been removed - appointment_type_link.delete() - else: - appointment_types_from_links.append(appointment_type_link.appointment_type) - for appointment_type in appointment_types: - if appointment_type not in appointment_types_from_links: - # we create new appointment links for appointments types that have been added - appointment_type_link = AppointmentTypeLink(appointment=appointment, - appointment_type=appointment_type) - appointment_type_link.save() - return appointment - - -class AppointmentAddForm(ModelForm): - class Meta: - model = Appointment - exclude = ['status', 'appointment_types'] - - datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)', - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) - ) - - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - if user is None: - raise TypeError("User not defined") - self.user = Worker.get_by_user(user) - if self.user is None: - raise TypeError("Worker not defined for: " + user.username) - - super(ModelForm, self).__init__(*args, **kwargs) - fields = OrderedDict() - for i, tuple in enumerate(self.fields.items()): - key, value = tuple - fields[key] = value - if i == APPOINTMENT_TYPES_FIELD_POSITION: - fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False, - widget=forms.CheckboxSelectMultiple, - queryset=AppointmentType.objects.all(), - ) - self.fields = fields - self.fields['worker_assigned'].queryset = Worker.objects.filter( - locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name') - self.fields['location'].queryset = get_filter_locations(self.user) - - def clean_location(self): - location = self.cleaned_data['location'] - if self.user.locations.filter(id=location.id).count() == 0: - self.add_error('location', "You cannot create appointment for this location") - else: - return location - - def save(self, commit=True): - appointment = super(AppointmentAddForm, self).save(commit) - appointment_types = self.cleaned_data['appointment_types'] - for appointment_type in appointment_types: - appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type) - appointment_type_link.save() - return appointment - - class VisitDetailForm(ModelForm): datetime_begin = forms.DateField(label="Visit begins on", widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d") @@ -231,49 +90,6 @@ class VisitAddForm(ModelForm): self.add_error('datetime_end', "End date must be after start date") -class ContactAttemptForm(ModelForm): - datetime_when = forms.DateTimeField(label='When? (YYYY-MM-DD HH:MM)', - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) - ) - - class Meta: - model = ContactAttempt - fields = '__all__' - - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - if user is None: - raise TypeError("User not defined") - self.user = Worker.get_by_user(user) - if self.user is None: - raise TypeError("Worker not defined for: " + user.username) - subject = kwargs.pop('subject', None) - super(ContactAttemptForm, self).__init__(*args, **kwargs) - self.fields['subject'].initial = subject.id - self.fields['subject'].disabled = True - self.fields['worker'].initial = self.user - - -class ContactAttemptEditForm(ModelForm): - datetime_when = forms.DateTimeField(label='When? (YYYY-MM-DD HH:MM)', - widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) - ) - - class Meta: - model = ContactAttempt - fields = '__all__' - - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - if user is None: - raise TypeError("User not defined") - self.user = Worker.get_by_user(user) - if self.user is None: - raise TypeError("Worker not defined for: " + user.username) - super(ContactAttemptEditForm, self).__init__(*args, **kwargs) - self.fields['subject'].disabled = True - - class KitRequestForm(Form): start_date = forms.DateField(label="From date", widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"), @@ -398,5 +214,3 @@ class HolidayAddForm(ModelForm): availabilities = worker.availability_set.all() for availability in availabilities: validate_availability_conflict(self, self.cleaned_data, availability) - - diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py index 94fa1fcfe15551d3d9510337db6ad1828158ca24..7101e6bbeec368de1979c6f901b033b9f96cd8b4 100644 --- a/smash/web/forms/study_subject_forms.py +++ b/smash/web/forms/study_subject_forms.py @@ -5,8 +5,9 @@ from django import forms from django.forms import ModelForm from web.forms.forms import DATETIMEPICKER_DATE_ATTRS, get_worker_from_args -from web.models import StudySubject, Study, StudyColumns, VoucherType +from web.models import StudySubject, Study, StudyColumns, VoucherType, Worker from web.models.constants import SCREENING_NUMBER_PREFIXES_FOR_TYPE +from web.models.worker_study_role import WORKER_HEALTH_PARTNER from web.widgets.secure_file_widget import SecuredFileWidget logger = logging.getLogger(__name__) @@ -27,6 +28,7 @@ class StudySubjectForm(ModelForm): def __init__(self, *args, **kwargs): super(StudySubjectForm, self).__init__(*args, **kwargs) + self.fields['health_partner'].queryset = Worker.get_workers_by_worker_type(WORKER_HEALTH_PARTNER) class StudySubjectAddForm(StudySubjectForm): diff --git a/smash/web/forms/voucher_forms.py b/smash/web/forms/voucher_forms.py index 99bb89736762a3c49a1e859d95feb0389f033fa9..946486db177dd364d547e97afba6b33fafbc9bc2 100644 --- a/smash/web/forms/voucher_forms.py +++ b/smash/web/forms/voucher_forms.py @@ -7,8 +7,9 @@ from django.utils import timezone from web.algorithm import VerhoeffAlgorithm from web.forms.forms import DATEPICKER_DATE_ATTRS -from web.models import VoucherType, VoucherTypePrice, Voucher +from web.models import VoucherType, VoucherTypePrice, Voucher, Worker from web.models.constants import VOUCHER_STATUS_NEW, VOUCHER_STATUS_USED +from web.models.worker_study_role import WORKER_VOUCHER_PARTNER logger = logging.getLogger(__name__) @@ -42,6 +43,7 @@ class VoucherForm(ModelForm): super(VoucherForm, self).__init__(*args, **kwargs) self.fields['voucher_type'].queryset = voucher_types + self.fields['usage_partner'].queryset = Worker.get_workers_by_worker_type(WORKER_VOUCHER_PARTNER) self.fields['number'].widget.attrs['readonly'] = True self.fields['number'].required = False diff --git a/smash/web/forms/worker_form.py b/smash/web/forms/worker_form.py new file mode 100644 index 0000000000000000000000000000000000000000..5fd4da53b15d4806e64230ce855a2ea6d6e93373 --- /dev/null +++ b/smash/web/forms/worker_form.py @@ -0,0 +1,45 @@ +import logging + +from django import forms +from django.forms import ModelForm + +from web.models import Worker, WorkerStudyRole +from web.models.constants import GLOBAL_STUDY_ID +from web.models.worker import role_choices_by_worker_type, worker_type_by_worker +from web.models.worker_study_role import WORKER_STAFF + +logger = logging.getLogger(__name__) + + +class WorkerForm(ModelForm): + class Meta: + model = Worker + exclude = ['appointments'] + + def __init__(self, *args, **kwargs): + worker_type = kwargs.pop('worker_type', WORKER_STAFF) + super(WorkerForm, self).__init__(*args, **kwargs) + instance = getattr(self, 'instance', None) + + initial_role = None + if instance is not None: + worker_type = worker_type_by_worker(instance) + roles = WorkerStudyRole.objects.filter(worker=instance, study_id=GLOBAL_STUDY_ID) + + if roles.count() > 0: + initial_role = roles[0].role + + choices = role_choices_by_worker_type(worker_type) + self.fields['role'] = forms.ChoiceField(label='Role', choices=choices) + self.fields['role'].initial = initial_role + + if worker_type == WORKER_STAFF: + del self.fields['voucher_types'] + + def save(self, commit=True): + instance = super(WorkerForm, self).save(commit) + roles = WorkerStudyRole.objects.filter(worker=instance, study_id=GLOBAL_STUDY_ID) + if roles.count() > 0: + roles.update(role=self.cleaned_data['role']) + else: + WorkerStudyRole.objects.create(worker=instance, study_id=GLOBAL_STUDY_ID, role=self.cleaned_data['role']) diff --git a/smash/web/migrations/0098_workerstudyrole.py b/smash/web/migrations/0098_workerstudyrole.py new file mode 100644 index 0000000000000000000000000000000000000000..247c4ad401d6407649a19c6b4936f5ed3f4d210c --- /dev/null +++ b/smash/web/migrations/0098_workerstudyrole.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-12 14:15 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0097_auto_20171211_1616'), + ] + + operations = [ + migrations.CreateModel( + name='WorkerStudyRole', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField(choices=[(b'DOCTOR', b'Doctor'), (b'NURSE', b'Nurse'), (b'PSYCHOLOGIST', b'Psychologist'), (b'TECHNICIAN', b'Technician'), (b'SECRETARY', b'Secretary')], max_length=20, verbose_name=b'Role')), + ('study', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Study')), + ('worker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roles', to='web.Worker')), + ], + ), + migrations.RunSQL('insert into web_workerstudyrole (study_id, worker_id, role) '+ + 'select 1, id, role from web_worker;'), + migrations.RemoveField( + model_name='worker', + name='role', + ), + + ] diff --git a/smash/web/migrations/0099_auto_20171213_1115.py b/smash/web/migrations/0099_auto_20171213_1115.py new file mode 100644 index 0000000000000000000000000000000000000000..36f7df053335eaaf12b53d29195e6160dcc2d171 --- /dev/null +++ b/smash/web/migrations/0099_auto_20171213_1115.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-13 11:15 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0098_workerstudyrole'), + ] + + operations = [ + migrations.AlterField( + model_name='worker', + name='languages', + field=models.ManyToManyField(blank=True, to='web.Language', verbose_name=b'Known languages'), + ), + migrations.AlterField( + model_name='worker', + name='locations', + field=models.ManyToManyField(blank=True, to='web.Location', verbose_name=b'Locations'), + ), + migrations.AlterField( + model_name='worker', + name='phone_number', + field=models.CharField(blank=True, max_length=20, verbose_name=b'Phone number'), + ), + migrations.AlterField( + model_name='worker', + name='specialization', + field=models.CharField(blank=True, max_length=20, verbose_name=b'Specialization'), + ), + migrations.AlterField( + model_name='worker', + name='unit', + field=models.CharField(blank=True, max_length=50, verbose_name=b'Unit'), + ), + ] diff --git a/smash/web/migrations/0100_auto_20171213_1140.py b/smash/web/migrations/0100_auto_20171213_1140.py new file mode 100644 index 0000000000000000000000000000000000000000..6bacef3d9db5c8a66ca87363c89cf0c37ffc13a1 --- /dev/null +++ b/smash/web/migrations/0100_auto_20171213_1140.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-13 11:40 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0099_auto_20171213_1115'), + ] + + operations = [ + migrations.AddField( + model_name='worker', + name='address', + field=models.CharField(blank=True, max_length=255, verbose_name=b'Address'), + ), + migrations.AddField( + model_name='worker', + name='city', + field=models.CharField(blank=True, max_length=50, verbose_name=b'City'), + ), + migrations.AddField( + model_name='worker', + name='country', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='web.Country', verbose_name=b'Country'), + ), + migrations.AddField( + model_name='worker', + name='fax_number', + field=models.CharField(blank=True, max_length=20, verbose_name=b'Fax number'), + ), + migrations.AddField( + model_name='worker', + name='phone_number_2', + field=models.CharField(blank=True, max_length=20, verbose_name=b'Phone number 2'), + ), + migrations.AddField( + model_name='worker', + name='postal_code', + field=models.CharField(blank=True, max_length=7, verbose_name=b'Postal code'), + ), + migrations.AddField( + model_name='worker', + name='voucher_types', + field=models.ManyToManyField(blank=True, to='web.VoucherType', verbose_name=b'Voucher types'), + ), + ] diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py index 77a30db094dfd4b977b2ca164a0070b39ea1e8cc..9edb9cb0982da082f3fd3011ce76607ae0970748 100644 --- a/smash/web/models/__init__.py +++ b/smash/web/models/__init__.py @@ -19,6 +19,7 @@ from voucher_type_price import VoucherTypePrice from room import Room from visit import Visit from worker import Worker +from worker_study_role import WorkerStudyRole from appointment import Appointment from appointment_type import AppointmentType from availability import Availability @@ -39,5 +40,5 @@ from inconsistent_subject import InconsistentSubject, InconsistentField __all__ = [Study, FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, StudySubject, StudySubjectList, SubjectColumns, StudyNotificationParameters, AppointmentList, AppointmentColumns, Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate, - AppointmentTypeLink, VoucherType, VoucherTypePrice, Voucher, + AppointmentTypeLink, VoucherType, VoucherTypePrice, Voucher, WorkerStudyRole, MissingSubject, InconsistentSubject, InconsistentField, Country, StudyColumns, VisitColumns, StudyVisitList] diff --git a/smash/web/models/visit.py b/smash/web/models/visit.py index 61b29704181cff23b8091e5fb43c0e01232a3e88..2922ea8bb66ba4e71fd213edd5ff660be87f9304 100644 --- a/smash/web/models/visit.py +++ b/smash/web/models/visit.py @@ -5,7 +5,7 @@ from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver -from constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES_CONTROL +from web.models.constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES_CONTROL class Visit(models.Model): diff --git a/smash/web/models/worker.py b/smash/web/models/worker.py index 8923532e3351205d48ed2e7b8fe0419b670d2efe..df02e89f21972de2dedb04832cff24c3b113773e 100644 --- a/smash/web/models/worker.py +++ b/smash/web/models/worker.py @@ -5,18 +5,61 @@ import logging from django.contrib.auth.models import User, AnonymousUser from django.db import models +from web.models.constants import GLOBAL_STUDY_ID, COUNTRY_OTHER_ID +from web.models.worker_study_role import STUDY_ROLE_CHOICES, HEALTH_PARTNER_ROLE_CHOICES, \ + VOUCHER_PARTNER_ROLE_CHOICES, WORKER_STAFF, WORKER_HEALTH_PARTNER, WORKER_VOUCHER_PARTNER + logger = logging.getLogger(__name__) +def roles_by_worker_type(worker_type): + role_choices = role_choices_by_worker_type(worker_type) + roles = [] + + for role_type, role_name in role_choices: + roles.append(role_type) + return roles + + +def role_choices_by_worker_type(worker_type): + if worker_type == WORKER_STAFF: + return STUDY_ROLE_CHOICES + elif worker_type == WORKER_HEALTH_PARTNER: + return HEALTH_PARTNER_ROLE_CHOICES + elif worker_type == WORKER_VOUCHER_PARTNER: + return VOUCHER_PARTNER_ROLE_CHOICES + else: + raise TypeError("Unknown worker type") + + +def worker_type_by_worker(worker): + roles = worker.roles.filter(study=GLOBAL_STUDY_ID) + if roles.count() == 0: + return WORKER_STAFF + role = roles[0].role + for role_type, role_name in STUDY_ROLE_CHOICES: + if role_type == role: + return WORKER_STAFF + for role_type, role_name in HEALTH_PARTNER_ROLE_CHOICES: + if role_type == role: + return WORKER_HEALTH_PARTNER + for role_type, role_name in VOUCHER_PARTNER_ROLE_CHOICES: + if role_type == role: + return WORKER_VOUCHER_PARTNER + raise TypeError("Unknown worker role") + + class Worker(models.Model): class Meta: app_label = 'web' languages = models.ManyToManyField("web.Language", - verbose_name='Known languages' + verbose_name='Known languages', + blank=True ) locations = models.ManyToManyField("web.Location", - verbose_name='Locations' + verbose_name='Locations', + blank=True ) appointments = models.ManyToManyField('web.Appointment', blank=True, verbose_name='Appointments' @@ -31,29 +74,55 @@ class Worker(models.Model): verbose_name='Last name' ) phone_number = models.CharField(max_length=20, - verbose_name='Phone number' + verbose_name='Phone number', + blank=True ) + phone_number_2 = models.CharField(max_length=20, + verbose_name='Phone number 2', + blank=True + ) + fax_number = models.CharField(max_length=20, + verbose_name='Fax number', + blank=True + ) + 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('web.Country', + null=False, + blank=False, + default=COUNTRY_OTHER_ID, + verbose_name='Country' + ) + + voucher_types = models.ManyToManyField("web.VoucherType", + verbose_name='Voucher types', + blank=True + ) + unit = models.CharField(max_length=50, - verbose_name='Unit' + verbose_name='Unit', + blank=True ) email = models.EmailField( verbose_name='E-mail' ) - ROLE_CHOICES_SECRETARY = "SECRETARY" - ROLE_CHOICES_DOCTOR = "DOCTOR" - ROLE_CHOICES = ( - (ROLE_CHOICES_DOCTOR, 'Doctor'), - ('NURSE', 'Nurse'), - ('PSYCHOLOGIST', 'Psychologist'), - ('TECHNICIAN', 'Technician'), - (ROLE_CHOICES_SECRETARY, 'Secretary') - ) - role = models.CharField(max_length=20, choices=ROLE_CHOICES, - verbose_name='Role' - ) specialization = models.CharField(max_length=20, - verbose_name='Specialization' + verbose_name='Specialization', + blank=True ) def is_on_leave(self): @@ -95,16 +164,23 @@ class Worker(models.Model): else: return None + @staticmethod + def get_workers_by_worker_type(worker_type, study_id=GLOBAL_STUDY_ID): + return Worker.objects.filter(roles__study_id=study_id, + roles__role__in=roles_by_worker_type(worker_type)) + @staticmethod def get_details(the_user): - person = Worker.objects.filter(user=the_user) + persons = Worker.objects.filter(user=the_user) - if len(person) == 0: + if len(persons) == 0: return the_user.get_full_name(), '<No worker information>' else: - # For get_*_display, see: - # https://docs.djangoproject.com/en/1.10/topics/db/models/#field-options - return unicode(person[0]), person[0].get_role_display() + person = persons[0] + role = "N/A" + if person.roles.filter(study=GLOBAL_STUDY_ID).count() > 0: + role = person.roles.filter(study=GLOBAL_STUDY_ID)[0].get_role_display() + return unicode(person), role def __str__(self): return "%s %s" % (self.first_name, self.last_name) diff --git a/smash/web/models/worker_study_role.py b/smash/web/models/worker_study_role.py new file mode 100644 index 0000000000000000000000000000000000000000..c6e6654e70b3bd7c7c671ac18347928f733b0aa4 --- /dev/null +++ b/smash/web/models/worker_study_role.py @@ -0,0 +1,52 @@ +# coding=utf-8 +import logging + +from django.db import models + +logger = logging.getLogger(__name__) + +ROLE_CHOICES_SECRETARY = "SECRETARY" +ROLE_CHOICES_DOCTOR = "DOCTOR" +ROLE_CHOICES_NURSE = "NURSE" +ROLE_CHOICES_PSYCHOLOGIST = "PSYCHOLOGIST" +ROLE_CHOICES_TECHNICIAN = "TECHNICIAN" + +ROLE_CHOICES_HEALTH_PARTNER = "HEALTH_PARTNER" +ROLE_CHOICES_VOUCHER_PARTNER = "VOUCHER_PARTNER" + +STUDY_ROLE_CHOICES = ( + (ROLE_CHOICES_DOCTOR, 'Doctor'), + (ROLE_CHOICES_NURSE, 'Nurse'), + (ROLE_CHOICES_PSYCHOLOGIST, 'Psychologist'), + (ROLE_CHOICES_TECHNICIAN, 'Technician'), + (ROLE_CHOICES_SECRETARY, 'Secretary') +) + +HEALTH_PARTNER_ROLE_CHOICES = ( + (ROLE_CHOICES_HEALTH_PARTNER, "Health Partner"), +) + +VOUCHER_PARTNER_ROLE_CHOICES = ( + (ROLE_CHOICES_VOUCHER_PARTNER, "Voucher Partner"), +) + +ROLE_CHOICES = STUDY_ROLE_CHOICES + HEALTH_PARTNER_ROLE_CHOICES + VOUCHER_PARTNER_ROLE_CHOICES + +WORKER_STAFF = "STAFF" +WORKER_HEALTH_PARTNER = "HEALTH_PARTNER" +WORKER_VOUCHER_PARTNER = "VOUCHER_PARTNER" + + +class WorkerStudyRole(models.Model): + class Meta: + app_label = 'web' + + worker = models.ForeignKey("web.Worker", + related_name="roles" + ) + + study = models.ForeignKey("web.Study") + + role = models.CharField(max_length=20, choices=STUDY_ROLE_CHOICES, + verbose_name='Role' + ) diff --git a/smash/web/templates/doctors/add.html b/smash/web/templates/doctors/add.html index eb17a92775c2ae3206b8545d674c9b2614361274..45259a6242701e7754fcf98eea71b6609fa3da19 100644 --- a/smash/web/templates/doctors/add.html +++ b/smash/web/templates/doctors/add.html @@ -22,7 +22,7 @@ {% block content %} <div class="box box-info"> <div class="box-header with-border"> - <a href="{% url 'web.views.doctors' %}" class="btn btn-block btn-default">Go back (without changes)</a> + <a href="{% url 'web.views.workers' %}" class="btn btn-block btn-default">Go back (without changes)</a> </div> <form method="post" action="" class="form-horizontal"> @@ -55,7 +55,7 @@ <button type="submit" class="btn btn-block btn-success">Add</button> </div> <div class="col-sm-6"> - <a href="{% url 'web.views.doctors' %}" class="btn btn-block btn-default">Cancel</a> + <a href="{% url 'web.views.workers' %}" class="btn btn-block btn-default">Cancel</a> </div> </div><!-- /.box-footer --> </form> diff --git a/smash/web/templates/doctors/add_availability.html b/smash/web/templates/doctors/add_availability.html index 3277b6f0db6c2f21bf7216cbaaff85a5cc5089e6..de6dce582dd2ea09ae2faf06b1d29bf4c21d0e03 100644 --- a/smash/web/templates/doctors/add_availability.html +++ b/smash/web/templates/doctors/add_availability.html @@ -22,7 +22,7 @@ {% block content %} <div class="box box-info"> <div class="box-header with-border"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without changes)</a> </div> @@ -56,7 +56,7 @@ <button type="submit" class="btn btn-block btn-success">Add</button> </div> <div class="col-sm-6"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Cancel</a> </div> </div><!-- /.box-footer --> diff --git a/smash/web/templates/doctors/add_holiday.html b/smash/web/templates/doctors/add_holiday.html index 797bab7d47ad70f20606fd0c71805e53f6e2e0b5..59e2975087f29fa7adf00f386bf03623c09966f5 100644 --- a/smash/web/templates/doctors/add_holiday.html +++ b/smash/web/templates/doctors/add_holiday.html @@ -23,7 +23,7 @@ {% block content %} <div class="box box-info"> <div class="box-header with-border"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without changes)</a> </div> @@ -57,7 +57,7 @@ <button type="submit" class="btn btn-block btn-success">Add</button> </div> <div class="col-sm-6"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Cancel</a> </div> </div><!-- /.box-footer --> diff --git a/smash/web/templates/doctors/breadcrumb.html b/smash/web/templates/doctors/breadcrumb.html index 76b5b72095b33b9a7c470f19bb7568d72b7df9b4..6d111dd545adc0d422fe4dfae996671af3c8f657 100644 --- a/smash/web/templates/doctors/breadcrumb.html +++ b/smash/web/templates/doctors/breadcrumb.html @@ -1,2 +1,2 @@ <li><a href="{% url 'web.views.appointments' %}"><i class="fa fa-dashboard"></i> Dashboard</a></li> -<li class="active"><a href="{% url 'web.views.doctors' %}">Workers</a></li> \ No newline at end of file +<li class="active"><a href="{% url 'web.views.workers' %}">Workers</a></li> \ No newline at end of file diff --git a/smash/web/templates/doctors/edit.html b/smash/web/templates/doctors/edit.html index e41beaed77e99201e1f6fcbb5c865c13a0f83812..148cda2c28482fd8327a800c334d58728abf9cb6 100644 --- a/smash/web/templates/doctors/edit.html +++ b/smash/web/templates/doctors/edit.html @@ -23,7 +23,7 @@ {% block content %} <div class="box box-info"> <div class="box-header with-border"> - <a href="{% url 'web.views.doctors' %}" class="btn btn-block btn-default" onclick="history.back()">Go + <a href="{% url 'web.views.workers' %}" class="btn btn-block btn-default" onclick="history.back()">Go back (without changes)</a> </div> @@ -55,7 +55,7 @@ <button type="submit" class="btn btn-block btn-success">Save</button> </div> <div class="col-sm-6"> - <a href="{% url 'web.views.doctors' %}" class="btn btn-block btn-default" + <a href="{% url 'web.views.workers' %}" class="btn btn-block btn-default" onclick="history.back()">Cancel</a> </div> </div><!-- /.box-footer --> @@ -90,11 +90,11 @@ <td>{{ availability.available_from }}</td> <td>{{ availability.available_till }}</td> <td> - <a href="{% url 'web.views.doctor_availability_edit' availability.id %}" type="button" + <a href="{% url 'web.views.worker_availability_edit' availability.id %}" type="button" class="btn btn-block btn-default">Edit</a> </td> <td> - <a href="{% url 'web.views.doctor_availability_delete' availability.id %}" type="button" + <a href="{% url 'web.views.worker_availability_delete' availability.id %}" type="button" class="btn btn-block btn-default">Delete</a> </td> </tr> @@ -105,7 +105,7 @@ <div class="box-footer"> <div class="col-sm-6"> - <a href="{% url 'views.doctor.doctor_availability_add' doctor_id %}" type="button" + <a href="{% url 'web.views.worker_availability_add' doctor_id %}" type="button" class="btn btn-block btn-success">Add availability</a> </div> </div><!-- /.box-footer --> @@ -132,7 +132,7 @@ <td>{{ holiday.datetime_end }}</td> <td>{{ holiday.info }}</td> <td> - <a href="{% url 'web.views.doctor_holiday_delete' holiday.id %}" type="button" + <a href="{% url 'web.views.worker_holiday_delete' holiday.id %}" type="button" class="btn btn-block btn-default">Delete</a> </td> </tr> @@ -143,7 +143,7 @@ <div class="box-footer"> <div class="col-sm-6"> - <a href="{% url 'views.doctor.doctor_holiday_add' doctor_id %}" type="button" + <a href="{% url 'web.views.worker_holiday_add' doctor_id %}" type="button" class="btn btn-block btn-success">Add holiday</a> </div> </div><!-- /.box-footer --> diff --git a/smash/web/templates/doctors/edit_availability.html b/smash/web/templates/doctors/edit_availability.html index 37de10ae4cc064b45b0173ef53d7a80e43296227..4daf5fffd8475f40a0908e700dfb717547c36a24 100644 --- a/smash/web/templates/doctors/edit_availability.html +++ b/smash/web/templates/doctors/edit_availability.html @@ -23,7 +23,7 @@ {% block content %} <div class="box box-info"> <div class="box-header with-border"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Go back (without changes)</a> </div> @@ -57,7 +57,7 @@ <button type="submit" class="btn btn-block btn-success">Save</button> </div> <div class="col-sm-6"> - <a href="{% url 'web.views.doctor_edit' doctor_id %}" + <a href="{% url 'web.views.worker_edit' doctor_id %}" class="btn btn-block btn-default">Cancel</a> </div> </div><!-- /.box-footer --> diff --git a/smash/web/templates/doctors/index.html b/smash/web/templates/doctors/index.html index 4dc34ec01d556867dd728b5f51d553f360eceb0c..67741a4b3b3fbf193e98e6c77c5a44db981ba3f9 100644 --- a/smash/web/templates/doctors/index.html +++ b/smash/web/templates/doctors/index.html @@ -18,7 +18,7 @@ {% block maincontent %} <div> - <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> + <a href="{% url 'web.views.worker_add' worker_type %}" class="btn btn-app"> <i class="fa fa-plus"></i> Add new worker</a> </div> @@ -52,7 +52,7 @@ {% endautoescape %} </td> <td>{{ worker.unit }}</td> - <td><a href="{% url 'web.views.doctor_edit' worker.id %}" type="button" + <td><a href="{% url 'web.views.worker_edit' worker.id %}" type="button" class="btn btn-block btn-default">Details</a></td> <td> {% if worker.is_on_leave %} @@ -63,7 +63,7 @@ </td> <td> {% if worker.is_active %} - <a href="{% url 'web.views.doctor_disable' worker.id %}" class="btn btn-block btn-warning">DISABLE</a> + <a href="{% url 'web.views.worker_disable' worker.id %}" class="btn btn-block btn-warning">DISABLE</a> {% else %} YES {% endif %} diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html index 05498fc5bf407bdd307a8e554214a598a5105953..235006d25e2c473796090189adc1e11426fcbf59 100644 --- a/smash/web/templates/sidebar.html +++ b/smash/web/templates/sidebar.html @@ -24,7 +24,7 @@ </li> <li data-desc="workers"> - <a href="{% url 'web.views.doctors' %}"> + <a href="{% url 'web.views.workers' %}"> <i class="fa fa-user-md"></i> <span>Workers</span> </a> diff --git a/smash/web/tests/api_views/test_daily_planning.py b/smash/web/tests/api_views/test_daily_planning.py index 0d48c8d15ed527d6704f168a77cdd0b47f94dfad..5f2cce562debb2cccda3bb876673b78cdb1431ac 100644 --- a/smash/web/tests/api_views/test_daily_planning.py +++ b/smash/web/tests/api_views/test_daily_planning.py @@ -1,30 +1,23 @@ # coding=utf-8 import datetime import json +import logging from django.contrib.auth.models import User -from django.test import Client, RequestFactory -from django.test import TestCase +from django.test import RequestFactory from django.urls import reverse +from web.tests import LoggedInWithWorkerTestCase 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 import Availability, Holiday, AppointmentTypeLink from web.models.constants import TUESDAY_AS_DAY_OF_WEEK from web.tests.functions import create_worker, create_study_subject, create_appointment, create_flying_team, \ create_visit, create_appointment_type, get_test_location +logger = logging.getLogger(__name__) -class TestApi(TestCase): - def setUp(self): - self.client = Client() - username = 'piotr' - password = 'top_secret' - self.user = User.objects.create_user( - username=username, email='jacob@bla', password=password) - self.worker = create_worker(self.user, True) - self.worker.role = Worker.ROLE_CHOICES_DOCTOR - self.worker.save() - self.client.login(username=username, password=password) + +class TestDailyPlanningApi(LoggedInWithWorkerTestCase): def test_empty_availabilities(self): response = self.client.get(reverse('web.api.events', kwargs={'date': "2017-09-05"})) diff --git a/smash/web/tests/api_views/test_worker.py b/smash/web/tests/api_views/test_worker.py index 0b32a0b321347ea85467de611b981e1d551508b7..22b8e76eec524117da068ee506cc020ff8bf569f 100644 --- a/smash/web/tests/api_views/test_worker.py +++ b/smash/web/tests/api_views/test_worker.py @@ -10,7 +10,7 @@ from web.api_views.worker import availabilities from web.tests import LoggedInWithWorkerTestCase -class TestApi(LoggedInWithWorkerTestCase): +class TestWorkerApi(LoggedInWithWorkerTestCase): def test_specializations(self): specialization_name = "some spec" diff --git a/smash/web/tests/forms/test_AppointmentEditForm.py b/smash/web/tests/forms/test_AppointmentEditForm.py index f62fe60937dcb10e313d273f93e3a908619d1b88..ff8140c0db4c7d5dca4a071d6fc34713d7f62559 100644 --- a/smash/web/tests/forms/test_AppointmentEditForm.py +++ b/smash/web/tests/forms/test_AppointmentEditForm.py @@ -1,7 +1,6 @@ from django.test import TestCase -from web.forms import AppointmentAddForm -from web.forms import AppointmentEditForm +from web.forms import AppointmentEditForm, AppointmentAddForm from web.models import Appointment, Worker, AppointmentTypeLink from web.tests.functions import get_test_location, create_user, create_visit, create_location, create_appointment_type diff --git a/smash/web/tests/forms/test_voucher_forms.py b/smash/web/tests/forms/test_voucher_forms.py index 64a78eef15b2365f515aecfa030b4da88aaeb166..ef712cfd73ce50411dced4da76f553eced508b16 100644 --- a/smash/web/tests/forms/test_voucher_forms.py +++ b/smash/web/tests/forms/test_voucher_forms.py @@ -3,10 +3,12 @@ import logging from django.urls import reverse from web.forms import VoucherForm -from web.models import Voucher +from web.models import WorkerStudyRole, Voucher from web.models.constants import VOUCHER_STATUS_USED +from web.models.worker_study_role import ROLE_CHOICES_VOUCHER_PARTNER from web.tests import LoggedInWithWorkerTestCase -from web.tests.functions import create_study_subject, create_voucher_type, format_form_field, create_voucher +from web.tests.functions import create_study_subject, create_voucher_type, format_form_field, create_voucher, \ + create_worker logger = logging.getLogger(__name__) @@ -14,6 +16,8 @@ logger = logging.getLogger(__name__) class VoucherFormTests(LoggedInWithWorkerTestCase): def setUp(self): super(VoucherFormTests, self).setUp() + self.voucher_partner = create_worker() + WorkerStudyRole.objects.filter(worker=self.voucher_partner).update(role=ROLE_CHOICES_VOUCHER_PARTNER) def test_auto_generated_use_date(self): voucher_type = create_voucher_type() @@ -24,7 +28,7 @@ class VoucherFormTests(LoggedInWithWorkerTestCase): voucher_form = VoucherForm() form_data = { "status": VOUCHER_STATUS_USED, - "usage_partner": str(self.worker.id), + "usage_partner": str(self.voucher_partner.id), "voucher_type": voucher_type.id } for key, value in voucher_form.initial.items(): @@ -58,7 +62,7 @@ class VoucherFormTests(LoggedInWithWorkerTestCase): def test_valid_status(self): study_subject = create_study_subject() voucher = create_voucher(study_subject) - voucher.usage_partner = self.worker + voucher.usage_partner = self.voucher_partner voucher.save() voucher_form = VoucherForm(instance=voucher) diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index 8261bd6335145cc090f610467b3a7c6523eaeaef..b3855137660292a0c9726bff01709179fa6d35b8 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -4,12 +4,13 @@ import os from django.contrib.auth.models import User +from web.models.worker_study_role import ROLE_CHOICES_DOCTOR from web.models import Location, AppointmentType, StudySubject, Worker, Visit, Appointment, ConfigurationItem, \ Language, ContactAttempt, FlyingTeam, Availability, Subject, Study, StudyColumns, StudyNotificationParameters, \ - VoucherType, VoucherTypePrice, Voucher + VoucherType, VoucherTypePrice, Voucher, WorkerStudyRole 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, VOUCHER_STATUS_NEW + MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW, GLOBAL_STUDY_ID from web.redcap_connector import RedcapSubject from web.views.notifications import get_today_midnight_date @@ -187,12 +188,16 @@ def create_worker(user=None, with_test_location=False): worker = Worker.objects.create( first_name='piotr', last_name="gawron", - email='jacob@bla', + email='jacob@bla.com', user=user, + specialization="spec", + unit="LCSB", + phone_number="0123456789" ) if with_test_location: worker.locations = [get_test_location()] worker.save() + WorkerStudyRole.objects.create(worker=worker, study_id=GLOBAL_STUDY_ID, role=ROLE_CHOICES_DOCTOR) return worker diff --git a/smash/web/tests/models/test_worker.py b/smash/web/tests/models/test_worker.py index e43ae0bb4120e7c1e610336cb10380253d5f25f1..1b479467a673a15eb108e2ba61aaf519fde703f2 100644 --- a/smash/web/tests/models/test_worker.py +++ b/smash/web/tests/models/test_worker.py @@ -4,6 +4,9 @@ from django.contrib.auth.models import User, AnonymousUser from django.test import Client, TestCase from web.models import Holiday, Worker +from web.models.worker import role_choices_by_worker_type, worker_type_by_worker +from web.models.worker_study_role import WORKER_STAFF, ROLE_CHOICES_SECRETARY, ROLE_CHOICES_HEALTH_PARTNER, \ + WORKER_HEALTH_PARTNER, ROLE_CHOICES_VOUCHER_PARTNER, WORKER_VOUCHER_PARTNER from web.tests.functions import create_worker from web.views.notifications import get_today_midnight_date @@ -60,3 +63,31 @@ class WorkerModelTests(TestCase): def test_get_by_user_for_anonymous(self): self.assertIsNone(Worker.get_by_user(AnonymousUser())) + + def test_role_choices_by_worker_type(self): + with self.assertRaises(Exception): + role_choices_by_worker_type("invalid arg") + + def test_worker_type_by_worker_without_role(self): + self.assertEqual(WORKER_STAFF, worker_type_by_worker(Worker())) + + def test_worker_type_by_worker_with_secretary_role(self): + worker = create_worker() + worker.roles.update(role=ROLE_CHOICES_SECRETARY) + self.assertEqual(WORKER_STAFF, worker_type_by_worker(worker)) + + def test_worker_type_by_worker_with_health_partner_role(self): + worker = create_worker() + worker.roles.update(role=ROLE_CHOICES_HEALTH_PARTNER) + self.assertEqual(WORKER_HEALTH_PARTNER, worker_type_by_worker(worker)) + + def test_worker_type_by_worker_with_voucher_partner_role(self): + worker = create_worker() + worker.roles.update(role=ROLE_CHOICES_VOUCHER_PARTNER) + self.assertEqual(WORKER_VOUCHER_PARTNER, worker_type_by_worker(worker)) + + def test_worker_type_by_worker_with_invalid_role(self): + worker = create_worker() + worker.roles.update(role="unk") + with self.assertRaises(Exception): + worker_type_by_worker(worker) diff --git a/smash/web/tests/view/test_appointments.py b/smash/web/tests/view/test_appointments.py index 9560cfa02a577b900b541addbf304c98e0506747..a1544f213bdcfcb70a699f4b9299c61faa5b069d 100644 --- a/smash/web/tests/view/test_appointments.py +++ b/smash/web/tests/view/test_appointments.py @@ -4,7 +4,7 @@ import logging from django.core.files.uploadedfile import SimpleUploadedFile from django.urls import reverse -from web.forms import AppointmentEditForm, SubjectEditForm, StudySubjectEditForm +from web.forms import SubjectEditForm, StudySubjectEditForm, AppointmentEditForm from web.models import Appointment, StudySubject, Visit from web.tests import LoggedInTestCase from web.tests.functions import create_study_subject, create_visit, create_appointment, create_worker, \ diff --git a/smash/web/tests/view/test_doctor.py b/smash/web/tests/view/test_worker.py similarity index 51% rename from smash/web/tests/view/test_doctor.py rename to smash/web/tests/view/test_worker.py index 5689a18676c0a8dd5b67e357a9302d1c58785304..ecf3d30329507a5a3f8acb668ed9488a35b65834 100644 --- a/smash/web/tests/view/test_doctor.py +++ b/smash/web/tests/view/test_worker.py @@ -2,77 +2,94 @@ import logging from django.urls import reverse -from web.tests.functions import create_language, create_location, create_availability -from web.forms import WorkerAddForm -from web.tests import create_worker +from web.forms import WorkerForm from web.models import Worker +from web.models.worker_study_role import WORKER_STAFF, ROLE_CHOICES_DOCTOR +from web.tests import create_worker +from web.tests.functions import create_language, create_location, create_availability, format_form_field from .. import LoggedInTestCase logger = logging.getLogger(__name__) -class DoctorViewTests(LoggedInTestCase): +class WorkerViewTests(LoggedInTestCase): def test_render_workers_list_request(self): create_worker() - response = self.client.get(reverse('web.views.doctors')) + response = self.client.get(reverse('web.views.workers')) self.assertEqual(response.status_code, 200) def test_render_add_worker_request(self): create_worker() - response = self.client.get(reverse('web.views.doctor_add')) + response = self.client.get(reverse('web.views.worker_add', kwargs={'worker_type': WORKER_STAFF})) self.assertEqual(response.status_code, 200) def test_render_worker_added_request(self): - language = create_language() location = create_location() - count = Worker.objects.all().count() - form = WorkerAddForm() - form_data = {} - for key, value in form.initial.items(): - if value is not None: - form_data[key] = value + form_data = self.get_form_data(Worker()) form_data["first_name"] = "John" form_data["last_name"] = "Doe" form_data["phone_number"] = "0123456789" form_data["unit"] = "TEST DEPARTMENT" form_data["email"] = "john.doe@unknown.domain.com" - form_data["role"] = Worker.ROLE_CHOICES_DOCTOR form_data["specialization"] = "tester" form_data["languages"] = [language.id] form_data["locations"] = [location.id] + form_data["role"] = ROLE_CHOICES_DOCTOR - response = self.client.post(reverse('web.views.doctor_add'), data=form_data) + response = self.client.post(reverse('web.views.worker_add', kwargs={'worker_type': WORKER_STAFF}), + data=form_data) + logger.debug(response.content) self.assertEqual(response.status_code, 302) - new_count = Worker.objects.all().count() - self.assertEqual(count + 1, new_count) + self.assertEqual(1, Worker.objects.all().count()) + + @staticmethod + def get_form_data(worker=None): + form = WorkerForm(instance=worker) + form_data = {} + for key, value in form.initial.items(): + form_data[key] = format_form_field(value) + return form_data def test_render_edit_worker_request(self): worker = create_worker() - response = self.client.get(reverse('web.views.doctor_edit', args=[worker.id])) + form_data = self.get_form_data(worker) + form_data["last_name"] = "XYZ" + form_data["role"] = ROLE_CHOICES_DOCTOR + + response = self.client.post(reverse('web.views.worker_edit', args=[worker.id]), data=form_data) + self.assertEqual(response.status_code, 302) + + updated_worker = Worker.objects.get(id=worker.id) + self.assertEqual(updated_worker.last_name, form_data["last_name"]) + + def test_edit_worker(self): + worker = create_worker() + + response = self.client.get(reverse('web.views.worker_edit', args=[worker.id])) self.assertEqual(response.status_code, 200) def test_render_add_availability_request(self): worker = create_worker() - response = self.client.get(reverse('views.doctor.doctor_availability_add', args=[worker.id])) + response = self.client.get(reverse('web.views.worker_availability_add', args=[worker.id])) self.assertEqual(response.status_code, 200) def test_render_edit_availability_request(self): worker = create_worker() availability = create_availability(worker) - response = self.client.get(reverse('web.views.doctor_availability_edit', args=[availability.id])) + response = self.client.get(reverse('web.views.worker_availability_edit', args=[availability.id])) self.assertEqual(response.status_code, 200) def test_render_add_holiday_request(self): worker = create_worker() - response = self.client.get(reverse('views.doctor.doctor_holiday_add', args=[worker.id])) + response = self.client.get(reverse('web.views.worker_holiday_add', args=[worker.id])) self.assertEqual(response.status_code, 200) diff --git a/smash/web/urls.py b/smash/web/urls.py index c58aa7bb6a3cebcf9310718f5c7042e1ca58e02f..f5da2cb56dd161f3a3f82f2e37d366d25ca9fef4 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -99,23 +99,24 @@ urlpatterns = [ # DOCTORS # #################### - url(r'^doctors$', views.doctor.doctors, name='web.views.doctors'), - url(r'^doctors/add$', views.doctor.doctor_add, name='web.views.doctor_add'), - url(r'^doctors/edit/(?P<doctor_id>\d+)$', views.doctor.doctor_edit, name='web.views.doctor_edit'), - url(r'^doctors/disable/(?P<doctor_id>\d+)$', views.doctor.doctor_disable, name='web.views.doctor_disable'), - url(r'^doctors/(?P<doctor_id>\d+)/availability/add$', views.doctor.doctor_availability_add, - name='views.doctor.doctor_availability_add'), - - url(r'^doctors/availability/(?P<availability_id>\d+)/delete$', views.doctor.doctor_availability_delete, - name='web.views.doctor_availability_delete'), - url(r'^doctors/availability/(?P<availability_id>\d+)/edit', views.doctor.doctor_availability_edit, - name='web.views.doctor_availability_edit'), - - url(r'^doctors/(?P<doctor_id>\d+)/holiday/add$', views.doctor.doctor_holiday_add, - name='views.doctor.doctor_holiday_add'), - - url(r'^doctors/holiday/(?P<holiday_id>\d+)/delete$', views.doctor.doctor_holiday_delete, - name='web.views.doctor_holiday_delete'), + url(r'^doctors/$', views.worker.worker_list, name='web.views.workers'), + url(r'^doctors/(?P<worker_type>[A-z]+)$', views.worker.worker_list, name='web.views.workers'), + url(r'^doctors/add/(?P<worker_type>[A-z]+)$', views.worker.worker_add, name='web.views.worker_add'), + url(r'^doctors/edit/(?P<worker_id>\d+)$', views.worker.worker_edit, name='web.views.worker_edit'), + url(r'^doctors/disable/(?P<doctor_id>\d+)$', views.worker.worker_disable, name='web.views.worker_disable'), + + url(r'^doctors/(?P<doctor_id>\d+)/availability/add$', views.worker.worker_availability_add, + name='web.views.worker_availability_add'), + url(r'^doctors/availability/(?P<availability_id>\d+)/delete$', views.worker.worker_availability_delete, + name='web.views.worker_availability_delete'), + url(r'^doctors/availability/(?P<availability_id>\d+)/edit', views.worker.worker_availability_edit, + name='web.views.worker_availability_edit'), + + url(r'^doctors/(?P<doctor_id>\d+)/holiday/add$', views.worker.worker_holiday_add, + name='web.views.worker_holiday_add'), + + url(r'^doctors/holiday/(?P<holiday_id>\d+)/delete$', views.worker.worker_holiday_delete, + name='web.views.worker_holiday_delete'), #################### # EQUIPMENT # diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py index fedbe5fd8b6300d36166e6e9ba058174553a0352..6ca5ef439294203655a29509a9fc29cb49c64e8f 100644 --- a/smash/web/views/__init__.py +++ b/smash/web/views/__init__.py @@ -67,7 +67,7 @@ class WrappedView(ContextMixin): import auth import appointment import visit -import doctor +import worker import subject import equipment import flying_teams diff --git a/smash/web/views/appointment.py b/smash/web/views/appointment.py index fac01b5a1d1aabe6537fb641e13612734b3a6e76..d45f67f8c6067ac2185519b3284e5f6ffd0c2b76 100644 --- a/smash/web/views/appointment.py +++ b/smash/web/views/appointment.py @@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404, redirect from web.models.appointment_list import APPOINTMENT_LIST_APPROACHING, APPOINTMENT_LIST_GENERIC, \ APPOINTMENT_LIST_UNFINISHED from . import wrap_response -from ..forms import AppointmentDetailForm, AppointmentAddForm, AppointmentEditForm, SubjectEditForm, \ +from web.forms import AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm, SubjectEditForm, \ StudySubjectEditForm from ..models import Appointment, StudySubject, MailTemplate diff --git a/smash/web/views/contact_attempt.py b/smash/web/views/contact_attempt.py index 6694bc41836623bef0041fe6c7861ee2e4a4c7bc..40d66ae2c454bfe265aa021f31e8041e1f93de16 100644 --- a/smash/web/views/contact_attempt.py +++ b/smash/web/views/contact_attempt.py @@ -1,14 +1,14 @@ from django.shortcuts import redirect, get_object_or_404 +from web.forms import ContactAttemptAddForm, ContactAttemptEditForm +from web.models import StudySubject, ContactAttempt from . import wrap_response -from ..forms import ContactAttemptForm, ContactAttemptEditForm -from ..models import StudySubject, ContactAttempt def contact_add(request, 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 = ContactAttemptAddForm(request.POST, user=request.user, subject=subject) form.instance.subject_id = subject_id if form.is_valid(): form.save() @@ -17,7 +17,7 @@ def contact_add(request, subject_id): else: return redirect('web.views.subject_edit', id=subject_id) else: - form = ContactAttemptForm(user=request.user, subject=subject) + form = ContactAttemptAddForm(user=request.user, subject=subject) return wrap_response(request, 'contact_attempt/add.html', {'form': form, 'subject_id': subject_id}) diff --git a/smash/web/views/doctor.py b/smash/web/views/worker.py similarity index 55% rename from smash/web/views/doctor.py rename to smash/web/views/worker.py index d6738333ac2225cebac3be65db76e3e85448a403..12914af5ca91250b897ce1824437b780d213aa95 100644 --- a/smash/web/views/doctor.py +++ b/smash/web/views/worker.py @@ -1,79 +1,86 @@ # coding=utf-8 +import logging + +from django.contrib.auth.decorators import login_required from django.shortcuts import redirect, get_object_or_404 +from web.forms import AvailabilityAddForm, AvailabilityEditForm, HolidayAddForm +from web.forms import WorkerForm +from web.models import Worker, Availability, Holiday +from web.models.constants import WEEKDAY_CHOICES, GLOBAL_STUDY_ID +from web.models.worker import worker_type_by_worker +from web.models.worker_study_role import WORKER_STAFF from . import wrap_response -from ..forms import WorkerAddForm, WorkerEditForm, AvailabilityAddForm, AvailabilityEditForm, HolidayAddForm -from ..models import Worker, Availability, Holiday -from ..models.constants import WEEKDAY_CHOICES + +logger = logging.getLogger(__name__) -def doctors(request): - doctors_list = Worker.objects.order_by('-last_name') +def worker_list(request, worker_type=WORKER_STAFF): + doctors_list = Worker.get_workers_by_worker_type(worker_type, study_id=GLOBAL_STUDY_ID, ).order_by('-last_name') context = { - 'doctors_list': doctors_list + 'doctors_list': doctors_list, + 'worker_type': worker_type } return wrap_response(request, "doctors/index.html", context) -def doctor_add(request): +def worker_add(request, worker_type): if request.method == 'POST': - form = WorkerAddForm(request.POST, request.FILES) + form = WorkerForm(request.POST, request.FILES, worker_type=worker_type) if form.is_valid(): form.save() - return redirect('web.views.doctors') + return redirect('web.views.workers') else: - form = WorkerAddForm() + form = WorkerForm(worker_type=worker_type) return wrap_response(request, 'doctors/add.html', {'form': form}) -def doctor_edit(request, doctor_id): - the_doctor = get_object_or_404(Worker, id=doctor_id) +def worker_edit(request, worker_id): + worker = get_object_or_404(Worker, id=worker_id) if request.method == 'POST': - form = WorkerEditForm(request.POST, request.FILES, instance=the_doctor) + form = WorkerForm(request.POST, request.FILES, instance=worker, worker_type=worker_type_by_worker(worker)) if form.is_valid(): form.save() - return redirect('web.views.doctors') + return redirect('web.views.workers') else: - form = WorkerEditForm(instance=the_doctor) - availabilities = Availability.objects.filter(person=doctor_id).order_by('day_number', 'available_from') - holidays = Holiday.objects.filter(person=doctor_id).order_by('-datetime_start') + form = WorkerForm(instance=worker) + availabilities = Availability.objects.filter(person=worker_id).order_by('day_number', 'available_from') + holidays = Holiday.objects.filter(person=worker_id).order_by('-datetime_start') return wrap_response(request, 'doctors/edit.html', { 'form': form, 'availabilities': availabilities, 'holidays': holidays, - 'doctor_id': doctor_id, + 'doctor_id': worker_id, 'weekdays': WEEKDAY_CHOICES }) -def doctor_disable(request, doctor_id): +def worker_disable(request, doctor_id): the_doctor = get_object_or_404(Worker, id=doctor_id) the_doctor.disable() - return doctors(request) + return worker_list(request) -def doctor_availability_delete(request, availability_id): +@login_required +def worker_availability_delete(request, availability_id): availability = Availability.objects.filter(id=availability_id) doctor_id = availability[0].person.id availability.delete() - return redirect(doctor_edit, doctor_id=doctor_id) + return redirect(worker_edit, doctor_id=doctor_id) -def doctor_availability_add(request, doctor_id): - doctors = Worker.objects.filter(id=doctor_id) - doctor = None - if len(doctors) > 0: - doctor = doctors[0] +def worker_availability_add(request, doctor_id): + worker = get_object_or_404(Worker, id=doctor_id) if request.method == 'POST': form = AvailabilityAddForm(request.POST, request.FILES) if form.is_valid(): form.save() - return redirect(doctor_edit, doctor_id=doctor_id) + return redirect(worker_edit, doctor_id=doctor_id) else: - form = AvailabilityAddForm(initial={'person': doctor}) + form = AvailabilityAddForm(initial={'person': worker}) return wrap_response(request, 'doctors/add_availability.html', { 'form': form, @@ -81,13 +88,13 @@ def doctor_availability_add(request, doctor_id): }) -def doctor_availability_edit(request, availability_id): +def worker_availability_edit(request, availability_id): availability = get_object_or_404(Availability, id=availability_id) if request.method == 'POST': form = AvailabilityEditForm(request.POST, request.FILES, instance=availability) if form.is_valid(): form.save() - return redirect(doctor_edit, doctor_id=availability.person_id) + return redirect(worker_edit, doctor_id=availability.person_id) else: form = AvailabilityEditForm(instance=availability) return wrap_response(request, 'doctors/edit_availability.html', @@ -98,14 +105,15 @@ def doctor_availability_edit(request, availability_id): }) -def doctor_holiday_delete(request, holiday_id): +@login_required +def worker_holiday_delete(request, holiday_id): holiday = Holiday.objects.filter(id=holiday_id) doctor_id = holiday[0].person.id holiday.delete() - return redirect(doctor_edit, doctor_id=doctor_id) + return redirect(worker_edit, doctor_id=doctor_id) -def doctor_holiday_add(request, doctor_id): +def worker_holiday_add(request, doctor_id): doctors = Worker.objects.filter(id=doctor_id) doctor = None if len(doctors) > 0: @@ -114,7 +122,7 @@ def doctor_holiday_add(request, doctor_id): form = HolidayAddForm(request.POST, request.FILES) if form.is_valid(): form.save() - return redirect(doctor_edit, doctor_id=doctor_id) + return redirect(worker_edit, doctor_id=doctor_id) else: form = HolidayAddForm(initial={'person': doctor}) return wrap_response(request, 'doctors/add_holiday.html',