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 = [
'timestamp',
]
get_latest_by = 'timestamp'
#: Entity to which this answer set belongs
#: This foreign key must be added to each concrete subclass
@@ -145,6 +146,10 @@ class AnswerSet(models.Model):
null=True,
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):
"""Get the answers from this set as a dictionary for use in Form.initial."""
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.urls import reverse
@@ -59,15 +59,17 @@ class Relationship(models.Model):
blank=False,
null=False)
#: When was this relationship defined?
created = models.DateTimeField(auto_now_add=True)
@property
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
expired = models.DateTimeField(blank=True, null=True)
return None
@property
def current_answers(self) -> 'RelationshipAnswerSet':
return self.answer_sets.last()
def is_current(self) -> bool:
return self.current_answers is not None
def get_absolute_url(self):
return reverse('people:relationship.detail', kwargs={'pk': self.pk})
@@ -141,15 +143,17 @@ class OrganisationRelationship(models.Model):
blank=False,
null=False)
#: When was this relationship defined?
created = models.DateTimeField(auto_now_add=True)
@property
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
expired = models.DateTimeField(blank=True, null=True)
return None
@property
def current_answers(self) -> 'OrganisationRelationshipAnswerSet':
return self.answer_sets.last()
def is_current(self) -> bool:
return self.current_answers is not None
def get_absolute_url(self):
return reverse('people:organisation.relationship.detail',

View File

@@ -37,6 +37,14 @@
</a>
{% endif %}
</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>
<hr>

View File

@@ -37,6 +37,14 @@
</a>
{% endif %}
</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>
<hr>

View File

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

View File

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

View File

@@ -44,8 +44,10 @@ class PersonListView(LoginRequiredMixin, ListView):
existing_relationships = set()
try:
existing_relationships = set(
self.request.user.person.relationship_targets.values_list(
'pk', flat=True))
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
@@ -132,9 +134,12 @@ class ProfileView(LoginRequiredMixin, DetailView):
context['relationship'] = None
try:
context['relationship'] = models.Relationship.objects.get(
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

View File

@@ -6,6 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.utils import timezone
from django.views.generic import DetailView, RedirectView, UpdateView
from django.views.generic.detail import SingleObjectMixin
from people import forms, models, permissions
@@ -91,6 +92,30 @@ class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView):
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,
DetailView):
"""View displaying details of an :class:`OrganisationRelationship`."""