feat: add view to end relationship

This commit is contained in:
James Graham
2021-03-19 12:00:32 +00:00
parent 8093b23870
commit 42d95beb5a
10 changed files with 160 additions and 19 deletions

View File

@@ -0,0 +1,37 @@
# Generated by Django 2.2.10 on 2021-03-19 10:50
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('people', '0048_disciplines_and_organisations'),
]
operations = [
migrations.AlterModelOptions(
name='organisationanswerset',
options={'get_latest_by': 'timestamp', 'ordering': ['timestamp']},
),
migrations.AlterModelOptions(
name='organisationrelationship',
options={'get_latest_by': 'created'},
),
migrations.AlterModelOptions(
name='organisationrelationshipanswerset',
options={'get_latest_by': 'timestamp', 'ordering': ['timestamp']},
),
migrations.AlterModelOptions(
name='personanswerset',
options={'get_latest_by': 'timestamp', 'ordering': ['timestamp']},
),
migrations.AlterModelOptions(
name='relationship',
options={'get_latest_by': 'created'},
),
migrations.AlterModelOptions(
name='relationshipanswerset',
options={'get_latest_by': 'timestamp', 'ordering': ['timestamp']},
),
]

View File

@@ -0,0 +1,37 @@
# Generated by Django 2.2.10 on 2021-03-19 11:09
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('people', '0049_relationship_latest_by_timestamp'),
]
operations = [
migrations.AlterModelOptions(
name='organisationrelationship',
options={},
),
migrations.RemoveField(
model_name='organisationrelationship',
name='created',
),
migrations.RemoveField(
model_name='organisationrelationship',
name='expired',
),
migrations.AlterModelOptions(
name='relationship',
options={},
),
migrations.RemoveField(
model_name='relationship',
name='created',
),
migrations.RemoveField(
model_name='relationship',
name='expired',
),
]

View File

@@ -124,6 +124,7 @@ class AnswerSet(models.Model):
ordering = [ ordering = [
'timestamp', 'timestamp',
] ]
get_latest_by = 'timestamp'
#: Entity to which this answer set belongs #: Entity to which this answer set belongs
#: This foreign key must be added to each concrete subclass #: This foreign key must be added to each concrete subclass
@@ -145,6 +146,10 @@ class AnswerSet(models.Model):
null=True, null=True,
editable=False) editable=False)
@property
def is_current(self) -> bool:
return self.replaced_timestamp is None
def as_dict(self, answers: typing.Optional[typing.Dict[str, typing.Any]] = None): def as_dict(self, answers: typing.Optional[typing.Dict[str, typing.Any]] = None):
"""Get the answers from this set as a dictionary for use in Form.initial.""" """Get the answers from this set as a dictionary for use in Form.initial."""
if answers is None: if answers is None:

View File

@@ -1,6 +1,6 @@
""" """Models describing relationships between people."""
Models describing relationships between people.
""" import typing
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
@@ -59,15 +59,17 @@ class Relationship(models.Model):
blank=False, blank=False,
null=False) null=False)
#: When was this relationship defined? @property
created = models.DateTimeField(auto_now_add=True) def current_answers(self) -> typing.Optional['RelationshipAnswerSet']:
answer_set = self.answer_sets.latest()
if answer_set.is_current:
return answer_set
#: When was this marked as expired? Default None means it has not expired return None
expired = models.DateTimeField(blank=True, null=True)
@property @property
def current_answers(self) -> 'RelationshipAnswerSet': def is_current(self) -> bool:
return self.answer_sets.last() return self.current_answers is not None
def get_absolute_url(self): def get_absolute_url(self):
return reverse('people:relationship.detail', kwargs={'pk': self.pk}) return reverse('people:relationship.detail', kwargs={'pk': self.pk})
@@ -141,15 +143,17 @@ class OrganisationRelationship(models.Model):
blank=False, blank=False,
null=False) null=False)
#: When was this relationship defined? @property
created = models.DateTimeField(auto_now_add=True) def current_answers(self) -> typing.Optional['OrganisationRelationshipAnswerSet']:
answer_set = self.answer_sets.latest()
if answer_set.is_current:
return answer_set
#: When was this marked as expired? Default None means it has not expired return None
expired = models.DateTimeField(blank=True, null=True)
@property @property
def current_answers(self) -> 'OrganisationRelationshipAnswerSet': def is_current(self) -> bool:
return self.answer_sets.last() return self.current_answers is not None
def get_absolute_url(self): def get_absolute_url(self):
return reverse('people:organisation.relationship.detail', return reverse('people:organisation.relationship.detail',

View File

@@ -37,6 +37,14 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% if relationship %}
<div class="col-md-3">
<a class="btn btn-danger btn-block"
href="{% url 'people:relationship.end' pk=relationship.pk %}">End Relationship
</a>
</div>
{% endif %}
</div> </div>
<hr> <hr>

View File

@@ -37,6 +37,14 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% if relationship %}
<div class="col-md-3">
<a class="btn btn-danger btn-block"
href="{% url 'people:relationship.end' pk=relationship.pk %}">End Relationship
</a>
</div>
{% endif %}
</div> </div>
<hr> <hr>

View File

@@ -13,7 +13,15 @@
<tbody> <tbody>
{% for relationship in person.relationships_as_source.all %} {% for relationship in person.relationships_as_source.all %}
<tr> <tr>
<td>{{ relationship.target }}</td> <td>
{% if relationship.is_current %}
{{ relationship.target }}
{% else %}
<del>
{{ relationship.target }}
</del>
{% endif %}
</td>
<td> <td>
<a class="btn btn-sm btn-info" <a class="btn btn-sm btn-info"
href="{% url 'people:person.detail' pk=relationship.target.pk %}">Profile</a> href="{% url 'people:person.detail' pk=relationship.target.pk %}">Profile</a>

View File

@@ -60,6 +60,10 @@ urlpatterns = [
views.relationship.RelationshipUpdateView.as_view(), views.relationship.RelationshipUpdateView.as_view(),
name='relationship.update'), name='relationship.update'),
path('relationships/<int:pk>/end',
views.relationship.RelationshipEndView.as_view(),
name='relationship.end'),
################################ ################################
# OrganisationRelationship views # OrganisationRelationship views
path('organisations/<int:organisation_pk>/relationships/create', path('organisations/<int:organisation_pk>/relationships/create',

View File

@@ -44,8 +44,10 @@ class PersonListView(LoginRequiredMixin, ListView):
existing_relationships = set() existing_relationships = set()
try: try:
existing_relationships = set( existing_relationships = set(
self.request.user.person.relationship_targets.values_list( self.request.user.person.relationships_as_source.filter(
'pk', flat=True)) answer_sets__replaced_timestamp__isnull=True
).values_list('target_id', flat=True)
)
except ObjectDoesNotExist: except ObjectDoesNotExist:
# No linked Person yet # No linked Person yet
@@ -132,9 +134,12 @@ class ProfileView(LoginRequiredMixin, DetailView):
context['relationship'] = None context['relationship'] = None
try: try:
context['relationship'] = models.Relationship.objects.get( relationship = models.Relationship.objects.get(
source=self.request.user.person, target=self.object) source=self.request.user.person, target=self.object)
if relationship.is_current:
context['relationship'] = relationship
except models.Relationship.DoesNotExist: except models.Relationship.DoesNotExist:
pass pass

View File

@@ -6,6 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.views.generic import DetailView, RedirectView, UpdateView from django.views.generic import DetailView, RedirectView, UpdateView
from django.views.generic.detail import SingleObjectMixin
from people import forms, models, permissions from people import forms, models, permissions
@@ -91,6 +92,30 @@ class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView):
return self.object.get_absolute_url() return self.object.get_absolute_url()
class RelationshipEndView(permissions.UserIsLinkedPersonMixin,
SingleObjectMixin, RedirectView):
"""View for marking a relationship as ended.
Sets `replaced_timestamp` on all answer sets where this is currently null.
"""
model = models.Relationship
def get_test_person(self) -> models.Person:
"""Get the person instance which should be used for access control checks."""
return self.get_object().source
def get_redirect_url(self, *args, **kwargs):
"""Mark any previous answer sets as replaced."""
now_date = timezone.now().date()
relationship = self.get_object()
relationship.answer_sets.filter(
replaced_timestamp__isnull=True).update(
replaced_timestamp=now_date)
return relationship.target.get_absolute_url()
class OrganisationRelationshipDetailView(permissions.UserIsLinkedPersonMixin, class OrganisationRelationshipDetailView(permissions.UserIsLinkedPersonMixin,
DetailView): DetailView):
"""View displaying details of an :class:`OrganisationRelationship`.""" """View displaying details of an :class:`OrganisationRelationship`."""