mirror of
https://github.com/Southampton-RSG/breccia-mapper.git
synced 2026-03-03 03:17:07 +00:00
feat: add view to end relationship
This commit is contained in:
37
people/migrations/0049_relationship_latest_by_timestamp.py
Normal file
37
people/migrations/0049_relationship_latest_by_timestamp.py
Normal 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']},
|
||||||
|
),
|
||||||
|
]
|
||||||
37
people/migrations/0050_relationship_remove_timestamps.py
Normal file
37
people/migrations/0050_relationship_remove_timestamps.py
Normal 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',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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`."""
|
||||||
|
|||||||
Reference in New Issue
Block a user