#!/usr/bin/env python
# crate_anon/crateweb/userprofile/models.py
"""
===============================================================================
Copyright (C) 2015-2018 Rudolf Cardinal (rudolf@pobox.com).
This file is part of CRATE.
CRATE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CRATE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CRATE. If not, see <http://www.gnu.org/licenses/>.
===============================================================================
"""
from typing import Any, List, Optional, Type
from cardinal_pythonlib.django.fields.jsonclassfield import JsonClassField
from django.conf import settings
from django.db import models
from django.dispatch import receiver
from django.http.request import HttpRequest
from crate_anon.crateweb.core.constants import (
LEN_ADDRESS,
LEN_PHONE,
LEN_TITLE,
)
from crate_anon.crateweb.extra.salutation import (
forename_surname,
salutation,
title_forename_surname,
)
# =============================================================================
# User profile information
# =============================================================================
# This is used for:
# - stuff the user might edit, e.g. per_page
# - a representation of the user as a researcher (or maybe clinician)
[docs]class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
primary_key=True,
on_delete=models.CASCADE,
related_name="profile")
# http://stackoverflow.com/questions/14345303/creating-a-profile-model-with-both-an-inlineadmin-and-a-post-save-signal-in-djan # noqa
# first_name: in Django User model
# last_name: in Django User model
# email: in Django User model
N_PAGE_CHOICES = (
(10, '10'),
(20, '20'),
(50, '50'),
(100, '100'),
(200, '200'),
(500, '500'),
(1000, '1000'),
)
N_PATIENT_PAGE_CHOICES = (
(1, '1'),
(5, '5'),
(10, '10'),
(20, '20'),
(50, '50'),
(100, '100'),
)
# -------------------------------------------------------------------------
# Web site personal settings
# -------------------------------------------------------------------------
per_page = models.PositiveSmallIntegerField(
choices=N_PAGE_CHOICES, default=50,
verbose_name="Number of items to show per page")
patients_per_page = models.PositiveSmallIntegerField(
choices=N_PATIENT_PAGE_CHOICES, default=1,
verbose_name="Number of patients to show per page "
"(for Patient Explorer view)")
line_length = models.PositiveSmallIntegerField(
default=80,
verbose_name="Characters to word-wrap text at in results "
"display (0 for no wrap)")
collapse_at_len = models.PositiveSmallIntegerField(
default=400,
verbose_name="Number of characters beyond which results field starts "
"collapsed (0 for none)")
collapse_at_n_lines = models.PositiveSmallIntegerField(
default=5,
verbose_name="Number of lines beyond which result/query field starts "
"collapsed (0 for none)")
sql_scratchpad = models.TextField(
verbose_name='SQL scratchpad for query builder')
patient_multiquery_scratchpad = JsonClassField(
verbose_name='PatientMultiQuery scratchpad (in JSON) for builder',
null=True) # type: 'PatientMultiQuery'
# -------------------------------------------------------------------------
# Developer
# -------------------------------------------------------------------------
is_developer = models.BooleanField(
default=False,
verbose_name="Enable developer functions?")
# -------------------------------------------------------------------------
# Contact details
# -------------------------------------------------------------------------
title = models.CharField(max_length=LEN_TITLE, blank=True)
address_1 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 1")
address_2 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 2")
address_3 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 3")
address_4 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 4")
address_5 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 5 (county)")
address_6 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 6 (postcode)")
address_7 = models.CharField(max_length=LEN_ADDRESS, blank=True,
verbose_name="Address line 7 (country)")
telephone = models.CharField(max_length=LEN_PHONE, blank=True)
# -------------------------------------------------------------------------
# Clinician-specific bits
# -------------------------------------------------------------------------
is_clinician = models.BooleanField(
default=False,
verbose_name="User is a clinician (with implied permission to look "
"up RIDs)")
is_consultant = models.BooleanField(
default=False,
verbose_name="User is an NHS consultant "
"(relevant for clinical trials)")
signatory_title = models.CharField(
max_length=255,
verbose_name='Title for signature (e.g. "Consultant psychiatrist")')
# -------------------------------------------------------------------------
# Functions
# -------------------------------------------------------------------------
def get_address_components(self) -> List[str]:
return list(filter(None,
[self.address_1, self.address_2, self.address_3,
self.address_4, self.address_5, self.address_6,
self.address_7]))
def get_title_forename_surname(self) -> str:
# noinspection PyTypeChecker
return title_forename_surname(self.title, self.user.first_name,
self.user.last_name)
def get_salutation(self) -> str:
# noinspection PyTypeChecker
return salutation(self.title, self.user.first_name,
self.user.last_name, assume_dr=True)
def get_forename_surname(self) -> str:
return forename_surname(self.user.first_name, self.user.last_name)
# noinspection PyUnusedLocal
@receiver(models.signals.post_save, sender=settings.AUTH_USER_MODEL)
def user_saved_so_create_profile(sender: Type[settings.AUTH_USER_MODEL],
instance: settings.AUTH_USER_MODEL,
created: bool,
**kwargs: Any) -> None:
UserProfile.objects.get_or_create(user=instance)
# =============================================================================
# Helper functions
# =============================================================================
def get_per_page(request: HttpRequest) -> Optional[int]:
if not request.user.is_authenticated:
return None
return request.user.profile.per_page
def get_patients_per_page(request: HttpRequest) -> Optional[int]:
if not request.user.is_authenticated:
return None
return request.user.profile.patients_per_page