"""
This module contains all the views which are used
by the manager to add/edit healthprofessionals and the
views used by the healthprofessional itselves.
:subtitle:`Class definitions:`
"""
import StringIO
from datetime import date
from django.contrib.auth.decorators import login_required, user_passes_test
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from apps.utils.utils import sent_password_change_request
from apps.healthperson.healthprofessional.forms import\
HealthProfessionalAddForm, HealthProfessionalSearchForm,\
HealthProfessionalEditForm, HealthProfessionalPhotoForm,\
HealthProfessionalNotificationEditForm,\
HealthProfessionalOutOfOfficeEditForm
from apps.account.forms import SetPasswordForm
from apps.healthperson.healthprofessional.models import HealthProfessional
from apps.questionnaire.models import QuestionnaireRequest
from django.utils.translation import ugettext as _
from PIL import Image
from django.contrib.auth.models import Group
from apps.account.models import User
from core.encryption.random import randomkey
from django.db.models import Q
from dateutil import parser
from apps.healthperson.utils import is_allowed_healthprofessional,\
is_allowed_manager, is_allowed_manager_and_healthprofessional, login_url
from apps.rcmessages.views import get_all_messages_for_healthprofessional
from django.core.files.uploadedfile import InMemoryUploadedFile
from apps.base.views import BaseIndexTemplateView
from django.views.generic.base import View, TemplateView
from django.utils.decorators import method_decorator
from core.views import FormView
from apps.healthperson.views import BaseAddView
from django.http import Http404
PHOTO_WIDTH = 133
PHOTO_HEIGHT = 165
[docs]class HealthProfessionalBaseView(View):
"""
Base view which adds the healthprofessional by
using the healthprofessional_session_id
or logged in user
"""
@method_decorator(login_required)
[docs] def dispatch(self, *args, **kwargs):
"""Adds healthprofessional to the view class"""
if 'healthprofessional_session_id' in kwargs:
healthprofessional_session_id = kwargs.get(
'healthprofessional_session_id')
if healthprofessional_session_id not in self.request.session:
raise Http404
healthperson_ptr_id =\
self.request.session[healthprofessional_session_id][8:]
try:
self.healthprofessional =\
HealthProfessional.objects.select_related(
'user__personal_encryption_key').get(
healthperson_ptr_id=healthperson_ptr_id)
except HealthProfessional.DoesNotExist:
raise Http404
else:
# set healthprofessional to self.
self.healthprofessional = self.request.user.healthperson
return super(
HealthProfessionalBaseView, self).dispatch(*args, **kwargs)
[docs] def get_context_data(self, **kwargs):
"""Base context, include the healthprofessional by default"""
context = super(HealthProfessionalBaseView,
self).get_context_data(**kwargs)
context.update({'healthprofessional': self.healthprofessional})
if hasattr(self, 'submenu'):
context.update({'submenu': self.submenu})
return context
[docs]class HealthProfessionalIndexView(BaseIndexTemplateView,
HealthProfessionalBaseView):
"""
This view shows the homepage of the healthprofessional
"""
template_name = 'healthprofessional/index.html'
[docs] def get_controles_for_healthprofessional(self, healthprofessional):
"""
Adds controles and urgent_patient_controles to the view so
they can be shown in the overview.
Args:
- healthprofessional: The healthprofessional to get all\
all controles for
"""
# Get the controles (appointment_healthprofessional =
# self or controle_healthprofessional=self))
controle_filter_base = (Q(
practitioner=healthprofessional) |
Q(appointment__appointment_healthprofessional=healthprofessional))
controle_filter_base = controle_filter_base & Q(
finished_on__isnull=False)
# Re-add controles after handling but still appointment_needed with:
# | (Q(appointment_needed = True) &
# Q(appointment_added_on__isnull = True))
extra_filter_base = Q(handled_on__isnull=True)
# Show (urgent) controles which are finished & (not handled)
# For re-adding controles with (handled & no appointment)
# see comments above...
controle_filter = controle_filter_base & (extra_filter_base)
# urgent_controle_filter = Q(urgent=True) & controle_filter_base & (
# extra_filter_base)
controles = QuestionnaireRequest.objects.filter(
controle_filter).order_by('-finished_on')
self.urgent_patient_controles = []
self.controles = []
for controle in controles:
if controle.urgent:
self.urgent_patient_controles.append(controle)
else:
self.controles.append(controle)
def get_context_data(self, **kwargs):
context = super(HealthProfessionalIndexView,
self).get_context_data(**kwargs)
self.get_controles_for_healthprofessional(self.healthprofessional)
controles = list(self.controles)
urgent_patient_controles = list(self.urgent_patient_controles)
# Add controles from healthprofessionals for which
# the logged-in healthprofessional is the replacement.
for hp_to_replace in self.healthprofessional.replacement_set.all():
if ((hp_to_replace.out_of_office_start <= date.today() and
hp_to_replace.out_of_office_end >= date.today())):
[extra_controles, extra_urgent_patient_controles] =\
self.get_controles_for_healthprofessional(hp_to_replace)
for extra_controle in extra_controles:
if extra_controle not in controles:
controles.append(extra_controle)
temp_controles = extra_urgent_patient_controles
for extra_urgent_patient_controle in temp_controles:
if ((extra_urgent_patient_controle not in
urgent_patient_controles)):
urgent_patient_controles.append(
extra_urgent_patient_controle)
try:
message = get_all_messages_for_healthprofessional(
self.healthprofessional)[0]
except IndexError:
message = None
context.update({'message': message,
'controles': controles,
'urgent_patient_controles': urgent_patient_controles,
'healthprofessional': self.healthprofessional})
return context
[docs]class SearchView(TemplateView):
"""Generic search page as available in the homepage"""
template_name = 'healthprofessional/search_index.html'
@method_decorator(login_required)
@method_decorator(user_passes_test(
is_allowed_healthprofessional, login_url=login_url))
[docs] def dispatch(self, *args, **kwargs):
"""Init default values to be used in the context"""
self.patients = []
self.no_search_term = False
return super(SearchView, self).dispatch(*args, **kwargs)
[docs] def post(self, request, *args, **kwargs):
"""Search for patients"""
if 'searchterm' in request.POST:
patients = []
searchterm = request.POST['searchterm']
if searchterm not in (None, ''):
# Build up search filter for persons
hospital = request.user.hospital
user_filter = Q(groups__name='patients') &\
Q(hospital=hospital) & Q(deleted_on__isnull=True)
user_filter2 = Q(hmac_last_name=searchterm)
user_filter2 = user_filter2 | Q(
hmac_first_name=searchterm)
user_filter2 = user_filter2 | Q(
hmac_BSN=searchterm)
# try parsing the filled in searchterm to a date,
# if failed don't include it.
try:
date = parser.parse(searchterm, dayfirst=True)
except (ValueError, TypeError):
date = None
if date:
user_filter2 = user_filter2 | Q(date_of_birth=date)
user_filter = user_filter & user_filter2
# Execute filter
users = User.objects.filter(user_filter)
patients = [user.healthperson for user in users]
else:
self.no_search_term = True
self.patients = patients
return self.get(request, *args, **kwargs)
[docs] def get_context_data(self, **kwargs):
"""Return the found patients in a context"""
context = super(SearchView, self).get_context_data(**kwargs)
context.update({'patients': self.patients,
'no_search_term': self.no_search_term})
return context
[docs]class HealthProfessionalCropPhoto(HealthProfessionalBaseView, TemplateView):
"""
View allows to crop the photo of the healthprofessional, if necessary.
"""
template_name = 'healthprofessional/photo_crop.html'
submenu = 'photo'
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
return super(HealthProfessionalCropPhoto,
self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
self.healthprofessional.photo_location.open('r')
image_file = self.healthprofessional.photo_location._file
image = Image.open(image_file.file)
x_offset = int(request.POST['x1'])
y_offset = int(request.POST['y1'])
# crop image...
image = image.crop(
(x_offset, y_offset,
x_offset + PHOTO_WIDTH, y_offset + PHOTO_HEIGHT))
# Save in memory temporarily to change photo name
image_io = StringIO.StringIO()
image.save(image_io, 'PNG', quality=100)
image_file = InMemoryUploadedFile(
image_io, None,
self.healthprofessional.photo_location.name,
'image/png', image_io.len, None)
# Remove old file, save new file & update field
self.healthprofessional.photo_location.delete()
self.healthprofessional.photo_location.save(
User.objects.make_random_password(length=10) + '.png', image_file)
image_file = None
image = None
image_io = None
return HttpResponseRedirect(reverse(
'healthprofessional_view_photo',
args=(self.kwargs.get('healthprofessional_session_id'),)))
[docs]class HealthProfessionalEditPhoto(HealthProfessionalBaseView, FormView):
"""
Add/edit or remove the photo of an healthprofessional
"""
template_name = 'healthprofessional/edit_view.html'
form_class = HealthProfessionalPhotoForm
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.photo_error = None
self.success_url = reverse(
'healthprofessional_view_photo',
args=(self.kwargs.get('healthprofessional_session_id'),))
return super(HealthProfessionalEditPhoto,
self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(HealthProfessionalEditPhoto, self).get_form_kwargs()
kwargs.update({'instance': self.healthprofessional})
return kwargs
def get_context_data(self, **kwargs):
context = super(HealthProfessionalEditPhoto,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.success_url, 'upload_photo': True,
'section': _('Behandelaar pasfoto')})
return context
def resize_photo(self, image):
x_ratio = float(image.size[0]) / PHOTO_WIDTH
y_ratio = float(image.size[1]) / PHOTO_HEIGHT
if y_ratio > x_ratio:
new_y = int(float(image.size[1]) / x_ratio)
new_x = PHOTO_WIDTH
else:
new_y = PHOTO_HEIGHT
new_x = int(float(image.size[0]) / y_ratio)
image = image.resize((new_x, new_y))
return image
def form_valid(self, form):
healthprofessional = form.save(commit=False)
photo_error = None
# check size and if to large resize
image_file = healthprofessional.photo_location._file
if image_file:
image = Image.open(image_file.file)
if image.size[0] < PHOTO_WIDTH or image.size[1] < PHOTO_HEIGHT:
photo_error = _('Pasfoto is te klein, minimaal: ') +\
str(PHOTO_WIDTH) + 'x' + str(PHOTO_HEIGHT) + 'px'
if not photo_error:
if ((image.size[0] != PHOTO_WIDTH and
image.size[1] != PHOTO_HEIGHT)):
image = self.resize_photo(image)
image_file = StringIO.StringIO()
image.save(image_file, 'PNG', quality=100)
healthprofessional.photo_location._file.file = image_file
healthprofessional.photo_location.name =\
User.objects.make_random_password(length=10) + '.png'
healthprofessional.save()
if ((image.size[0] != PHOTO_WIDTH or
image.size[1] != PHOTO_HEIGHT)):
# still not perfect.. need to crop
crop_url = reverse(
'healthprofessional_crop_photo',
args=(self.kwargs.get(
'healthprofessional_session_id'),))
return HttpResponseRedirect(crop_url)
else:
return super(HealthProfessionalEditPhoto,
self).form_valid(form)
else:
image_file = StringIO.StringIO()
image.save(image_file, 'PNG', quality=100)
healthprofessional.photo_location._file.file = image_file
healthprofessional.photo_location.name =\
User.objects.make_random_password(length=10) + '.png'
healthprofessional.save()
return super(HealthProfessionalEditPhoto,
self).form_valid(form)
else:
form.errors['photo_location'] = photo_error
return self.form_invalid(form)
else:
# done nothing or remove..
healthprofessional.save()
return super(HealthProfessionalEditPhoto, self).form_valid(form)
[docs]class HealthProfessionalPhotoView(HealthProfessionalBaseView, TemplateView):
"""
Show photo of healthprofessional
"""
template_name = 'healthprofessional/photo_view.html'
submenu = 'photo'
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
return super(HealthProfessionalPhotoView,
self).dispatch(*args, **kwargs)
[docs]class HealthProfessionalNotificationView(HealthProfessionalBaseView,
TemplateView):
"""
Show notification settings
"""
template_name = 'healthprofessional/notification_view.html'
submenu = 'notification'
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
return super(HealthProfessionalNotificationView,
self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = super(HealthProfessionalNotificationView,
self).get_context_data(**kwargs)
return context
[docs]class HealthProfessionalOutOfOfficeView(HealthProfessionalBaseView,
TemplateView):
"""
Show out of office settings
"""
template_name = 'healthprofessional/out_of_office_view.html'
submenu = 'out_of_office'
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
return super(HealthProfessionalOutOfOfficeView,
self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = super(HealthProfessionalOutOfOfficeView,
self).get_context_data(**kwargs)
return context
[docs]class HealthProfessionalOutOfOfficeEdit(HealthProfessionalBaseView,
FormView):
"""
Edit the out of office settings for an healthprofessional.
These settings are used to configure an out of office period
with a replacement.
"""
template_name = 'healthprofessional/edit_view.html'
form_class = HealthProfessionalOutOfOfficeEditForm
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.success_url = reverse(
'healthprofessional_view_out_of_office',
args=(self.kwargs.get('healthprofessional_session_id'),))
return super(HealthProfessionalOutOfOfficeEdit,
self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(HealthProfessionalOutOfOfficeEdit,
self).get_form_kwargs()
kwargs.update({'instance': self.healthprofessional})
return kwargs
def get_context_data(self, **kwargs):
context = super(HealthProfessionalOutOfOfficeEdit,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.success_url,
'section': _('Afwezigheid')})
return context
def form_valid(self, form):
healthprofessional = form.save(commit=False)
healthprofessional.save()
return super(HealthProfessionalOutOfOfficeEdit,
self).form_valid(form)
[docs]class HealthProfessionalNotificationEdit(HealthProfessionalBaseView, FormView):
"""
Edit the notification settings for an healthprofessional.
These settings are used for sending notifications of unhandeld
(urgent) controls.
"""
template_name = 'healthprofessional/edit_view.html'
form_class = HealthProfessionalNotificationEditForm
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.success_url = reverse(
'healthprofessional_view_notification',
args=(self.kwargs.get('healthprofessional_session_id'),))
return super(HealthProfessionalNotificationEdit,
self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(HealthProfessionalNotificationEdit,
self).get_form_kwargs()
kwargs.update({'instance': self.healthprofessional})
return kwargs
def get_context_data(self, **kwargs):
context = super(HealthProfessionalNotificationEdit,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.success_url,
'section': _('Notificatie instellingen')})
return context
def form_valid(self, form):
healthprofessional = form.save(commit=False)
healthprofessional.save()
return super(HealthProfessionalNotificationEdit, self).form_valid(form)
[docs]class HealthProfessionalPersonaliaView(HealthProfessionalBaseView,
TemplateView):
"""
Shows the personalia of an healhtprofessional which is the information
stored in the coupled :class:`apps.account.models.User` instance.
"""
template_name = 'healthprofessional/personalia_view.html'
submenu = 'personalia'
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
return super(HealthProfessionalPersonaliaView,
self).dispatch(*args, **kwargs)
[docs]class HealthProfessionalSetPassword(HealthProfessionalBaseView, FormView):
"""
Displays a form to set a password. Used to initialize password for an healthprofessional
"""
template_name = 'healthprofessional/edit_view.html'
form_class = SetPasswordForm
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.success_url = self.request.session.get('next_url', None)
return super(HealthProfessionalSetPassword,
self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(
HealthProfessionalSetPassword, self).get_form_kwargs()
kwargs.update({'user': self.healthprofessional.user})
return kwargs
def get_context_data(self, **kwargs):
context = super(HealthProfessionalSetPassword,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.success_url,
'section': _('Zet wachtwoord'),
'extra_info': _('U heeft nog geen wachtwoord ingesteld voor RemoteCare. ' +\
'Geef deze hieronder op om (ook) direct in RemoteCare te kunnen inloggen.')})
return context
def form_valid(self, form):
# Change password (optional)
user = self.healthprofessional.user
user.set_password(form.cleaned_data['password'])
# save user
user.save()
return super(HealthProfessionalSetPassword, self).form_valid(form)
[docs]class HealthProfessionalPersonaliaEdit(HealthProfessionalBaseView, FormView):
"""
Edit the personalia of an healhtprofessional which is the information
stored in the coupled :class:`apps.account.models.User` instance.
"""
template_name = 'healthprofessional/edit_view.html'
form_class = HealthProfessionalEditForm
@method_decorator(user_passes_test(
is_allowed_manager_and_healthprofessional, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.success_url = reverse(
'healthprofessional_view_personalia',
args=(self.kwargs.get('healthprofessional_session_id'),))
return super(HealthProfessionalPersonaliaEdit,
self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(
HealthProfessionalPersonaliaEdit, self).get_form_kwargs()
kwargs.update({'instance': self.healthprofessional.user})
return kwargs
def get_context_data(self, **kwargs):
context = super(HealthProfessionalPersonaliaEdit,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.success_url,
'section': _('Personalia & Account')})
return context
def form_valid(self, form):
user = form.save(commit=False)
# Change password (optional)
if form.cleaned_data['change_password'] == 'yes':
user.set_password(form.cleaned_data['password'])
# save user
user.save()
self.healthprofessional.function = form.cleaned_data['function']
self.healthprofessional.specialism = form.cleaned_data['specialism']
self.healthprofessional.telephone = form.cleaned_data['telephone']
self.healthprofessional.save()
return super(HealthProfessionalPersonaliaEdit, self).form_valid(form)
[docs]class HealthProfessionalSearchView(FormView):
"""
Search for an healthprofessional as a manager
"""
template_name = 'healthprofessional/search.html'
form_class = HealthProfessionalSearchForm
@method_decorator(login_required)
@method_decorator(user_passes_test(is_allowed_manager,
login_url=login_url))
def dispatch(self, *args, **kwargs):
self.has_searched = False
self.healthprofessionals = None
return super(HealthProfessionalSearchView,
self).dispatch(*args, **kwargs)
[docs] def get_context_data(self, **kwargs):
"""Add the search results to the context"""
context = super(HealthProfessionalSearchView,
self).get_context_data(**kwargs)
context.update({'healthprofessionals': self.healthprofessionals,
'has_searched': self.has_searched})
return context
[docs] def get_initial(self):
"""
Get initial data, used for showing the old results
and form data when
the user uses the back button to return to the form
"""
if (('last_search' in self.request.session and
'back' in self.request.GET)):
return self.request.session['last_search']
return None
[docs] def get(self, request, *args, **kwargs):
"""Re-execute the search if the user has used the back button"""
if 'last_search' in request.session and 'back' in request.GET:
form_class = self.get_form_class()
form = form_class(request.session['last_search'])
if form.is_valid():
self.form_valid(form)
return super(HealthProfessionalSearchView,
self).get(request, *args, **kwargs)
[docs]class HealthProfessionalAddView(BaseAddView):
"""
Class based view for adding a new healthprofessional
"""
template_name = 'healthprofessional/add.html'
form_class = HealthProfessionalAddForm
@method_decorator(login_required)
@method_decorator(user_passes_test(
is_allowed_manager, login_url=login_url))
def dispatch(self, *args, **kwargs):
self.succes_url = reverse('index')
return super(HealthProfessionalAddView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
user = self.get_user_for_form(form)
healthprofessional = HealthProfessional()
healthprofessional.function = form.cleaned_data['function']
healthprofessional.specialism = form.cleaned_data['specialism']
healthprofessional.telephone = form.cleaned_data['telephone']
# add to healthprofessionals group
user.groups = [Group.objects.get(name='healthprofessionals')]
healthprofessional.changed_by_user = self.request.user
healthprofessional.save()
user.healthperson = healthprofessional
user.save()
sent_password_change_request(user, self.url_prefix, False, True)
healthprofessional_session_id = randomkey()
self.request.session[healthprofessional_session_id] =\
'storage_{0}'.format(healthprofessional.health_person_id)
self.success_url = reverse('healthprofessional_view_personalia',
args=(healthprofessional_session_id,))
return super(HealthProfessionalAddView, self).form_valid(form)
[docs]class HealthProfessionalRemove(HealthProfessionalBaseView, TemplateView):
"""
Remove the healthprofessional by setting the deleted_on attribute
on the coupled :class:`apps.account.models.User` instance.
"""
template_name = 'healthprofessional/remove_confirmation.html'
@method_decorator(user_passes_test(is_allowed_manager,
login_url=login_url))
def dispatch(self, *args, **kwargs):
self.cancel_url = reverse('healthprofessional_search')
return super(HealthProfessionalRemove, self).dispatch(*args, **kwargs)
[docs] def post(self, request, *args, **kwargs):
"""
Remove the healthprofessional by setting the user to inactive
"""
user = self.healthprofessional.user
user.deleted_on = date.today()
user.set_unusable_password()
user.is_active = False
user.changed_by_user = self.request.user
user.save()
return HttpResponseRedirect(self.cancel_url)
def get_context_data(self, **kwargs):
context = super(HealthProfessionalRemove,
self).get_context_data(**kwargs)
context.update({'cancel_url': self.cancel_url})
return context