mirror of
https://github.com/Southampton-RSG/breccia-mapper.git
synced 2026-03-03 11:27:09 +00:00
All Persons now have associated Users, meaning Users can be hijacked by admins to manage the relationships for the associated Person.
206 lines
7.0 KiB
Python
206 lines
7.0 KiB
Python
"""
|
|
Views for displaying or manipulating instances of :class:`Person`.
|
|
"""
|
|
|
|
import typing
|
|
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.http import HttpRequest, HttpResponse
|
|
from django.shortcuts import redirect
|
|
from django.utils import timezone
|
|
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
|
|
|
from people import forms, models, permissions
|
|
from .map import get_map_data
|
|
|
|
from random import randint
|
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
User = get_user_model() # pylint: disable=invalid-name
|
|
|
|
|
|
class PersonCreateView(LoginRequiredMixin, CreateView):
|
|
"""View to create a new instance of :class:`Person`.
|
|
|
|
If 'user' is passed as a URL parameter - link the new person to the current user.
|
|
"""
|
|
model = models.Person
|
|
template_name = 'people/person/create.html'
|
|
form_class = forms.PersonForm
|
|
|
|
def form_valid(self, form):
|
|
try:
|
|
self.request.user.person
|
|
# user already has associated person
|
|
# assign newly created user, required for user hijacking
|
|
# so admins can manage relationships of all people
|
|
random_int = randint(0,999999999)
|
|
while User.objects.filter(username='autogen_'+str(random_int)):
|
|
random_int += 1
|
|
|
|
form.instance.user = User.objects.create_user('autogen_' + str(random_int))
|
|
form.instance.user.consent_given = self.request.user.consent_given
|
|
form.instance.user.save()
|
|
except ObjectDoesNotExist:
|
|
form.instance.user = self.request.user
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
class PersonListView(LoginRequiredMixin, ListView):
|
|
"""View displaying a list of :class:`Person` objects - searchable."""
|
|
model = models.Person
|
|
template_name = 'people/person/list.html'
|
|
|
|
def get_context_data(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
existing_relationships = set()
|
|
try:
|
|
existing_relationships = set(
|
|
self.request.user.person.relationships_as_source.filter(
|
|
answer_sets__replaced_timestamp__isnull=True
|
|
).values_list('target_id', flat=True)
|
|
)
|
|
|
|
except ObjectDoesNotExist:
|
|
# No linked Person yet
|
|
pass
|
|
|
|
context['existing_relationships'] = existing_relationships
|
|
|
|
return context
|
|
|
|
|
|
class ProfileView(LoginRequiredMixin, DetailView):
|
|
"""View displaying the profile of a :class:`Person` - who may be a user."""
|
|
model = models.Person
|
|
|
|
def get(self, request: HttpRequest, *args: typing.Any, **kwargs: typing.Any) -> HttpResponse:
|
|
try:
|
|
self.object = self.get_object() # pylint: disable=attribute-defined-outside-init
|
|
|
|
except ObjectDoesNotExist:
|
|
# User has no linked Person yet
|
|
return redirect('index')
|
|
|
|
if self.object.user == self.request.user and self.object.current_answers is None:
|
|
return redirect('people:person.update', pk=self.object.pk)
|
|
|
|
context = self.get_context_data(object=self.object)
|
|
return self.render_to_response(context)
|
|
|
|
def get_template_names(self) -> typing.List[str]:
|
|
"""Return template depending on level of access."""
|
|
if (self.object.user == self.request.user) or self.request.user.is_superuser:
|
|
return ['people/person/detail_full.html']
|
|
|
|
return ['people/person/detail_partial.html']
|
|
|
|
def get_object(self, queryset=None) -> models.Person:
|
|
"""Get the :class:`Person` object to be represented by this page.
|
|
|
|
If not determined from url get current user.
|
|
"""
|
|
try:
|
|
return super().get_object(queryset)
|
|
|
|
except AttributeError:
|
|
# pk was not provided in URL
|
|
return self.request.user.person
|
|
|
|
def get_context_data(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
|
"""Add current :class:`PersonAnswerSet` to context."""
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
answer_set = self.object.current_answers
|
|
context['answer_set'] = answer_set
|
|
context['map_markers'] = [get_map_data(self.object)]
|
|
|
|
context['question_answers'] = {}
|
|
if answer_set is not None:
|
|
show_all = (self.object.user == self.request.user) or self.request.user.is_superuser
|
|
context['question_answers'] = answer_set.build_question_answers(show_all)
|
|
|
|
context['relationship'] = None
|
|
try:
|
|
relationship = models.Relationship.objects.get(
|
|
source=self.request.user.person, target=self.object
|
|
)
|
|
|
|
if relationship.is_current:
|
|
context['relationship'] = relationship
|
|
|
|
except models.Relationship.DoesNotExist:
|
|
pass
|
|
|
|
except ObjectDoesNotExist:
|
|
# No linked Person yet
|
|
pass
|
|
|
|
return context
|
|
|
|
|
|
class PersonUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView):
|
|
"""View for updating a :class:`Person` record."""
|
|
model = models.Person
|
|
context_object_name = 'person'
|
|
template_name = 'people/person/update.html'
|
|
form_class = forms.PersonAnswerSetForm
|
|
|
|
def get(self, request: HttpRequest, *args: str, **kwargs: typing.Any) -> HttpResponse:
|
|
self.object = self.get_object()
|
|
|
|
try:
|
|
if (self.object.user == self.request.user) and not self.request.user.consent_given:
|
|
return redirect('consent')
|
|
|
|
except AttributeError:
|
|
# No linked user
|
|
pass
|
|
|
|
context = self.get_context_data(object=self.object)
|
|
return self.render_to_response(context)
|
|
|
|
def get_context_data(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context['map_markers'] = [get_map_data(self.object)]
|
|
|
|
return context
|
|
|
|
def get_initial(self) -> typing.Dict[str, typing.Any]:
|
|
try:
|
|
previous_answers = self.object.current_answers.as_dict()
|
|
|
|
except AttributeError:
|
|
previous_answers = {}
|
|
|
|
previous_answers.update({
|
|
'person_id': self.object.id,
|
|
})
|
|
|
|
return previous_answers
|
|
|
|
def get_form_kwargs(self) -> typing.Dict[str, typing.Any]:
|
|
"""Remove instance from form kwargs as it's a person, but expects a PersonAnswerSet."""
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs.pop('instance')
|
|
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
"""Mark any previous answer sets as replaced."""
|
|
response = super().form_valid(form)
|
|
now_date = timezone.now().date()
|
|
|
|
# Saving the form made self.object a PersonAnswerSet - so go up, then back down
|
|
# Shouldn't be more than one after initial updates after migration
|
|
for answer_set in self.object.person.answer_sets.exclude(pk=self.object.pk):
|
|
answer_set.replaced_timestamp = now_date
|
|
answer_set.save()
|
|
|
|
return response
|