feat: add relationships from person to org

Resolves #77
See #78
This commit is contained in:
James Graham
2021-03-02 09:03:10 +00:00
parent 6d5188af72
commit 936a375992
11 changed files with 454 additions and 30 deletions

View File

@@ -2,11 +2,10 @@
Models describing relationships between people.
"""
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.urls import reverse
from .person import Person
from .person import Organisation, Person
from .question import AnswerSet, Question, QuestionChoice
__all__ = [
@@ -14,6 +13,10 @@ __all__ = [
'RelationshipQuestionChoice',
'RelationshipAnswerSet',
'Relationship',
'OrganisationRelationshipQuestion',
'OrganisationRelationshipQuestionChoice',
'OrganisationRelationshipAnswerSet',
'OrganisationRelationship',
]
@@ -32,24 +35,10 @@ class RelationshipQuestionChoice(QuestionChoice):
null=False)
# class ExternalPerson(models.Model):
# """Model representing a person external to the project.
# These will never need to be linked to a :class:`User` as they
# will never log in to the system.
# """
# name = models.CharField(max_length=255,
# blank=False, null=False)
# def __str__(self) -> str:
# return self.name
class Relationship(models.Model):
"""
A directional relationship between two people allowing linked questions.
"""
class Meta:
constraints = [
models.UniqueConstraint(fields=['source', 'target'],
@@ -57,9 +46,11 @@ class Relationship(models.Model):
]
#: Person reporting the relationship
source = models.ForeignKey(Person, related_name='relationships_as_source',
source = models.ForeignKey(Person,
related_name='relationships_as_source',
on_delete=models.CASCADE,
blank=False, null=False)
blank=False,
null=False)
#: Person with whom the relationship is reported
target = models.ForeignKey(Person,
@@ -67,15 +58,6 @@ class Relationship(models.Model):
on_delete=models.CASCADE,
blank=False,
null=False)
# blank=True,
# null=True)
# target_external_person = models.ForeignKey(
# ExternalPerson,
# related_name='relationships_as_target',
# on_delete=models.CASCADE,
# blank=True,
# null=True)
#: When was this relationship defined?
created = models.DateTimeField(auto_now_add=True)
@@ -100,8 +82,7 @@ class Relationship(models.Model):
@raise Relationship.DoesNotExist: When the reverse relationship is not known
"""
return type(self).objects.get(source=self.target,
target=self.source)
return type(self).objects.get(source=self.target, target=self.source)
class RelationshipAnswerSet(AnswerSet):
@@ -119,3 +100,78 @@ class RelationshipAnswerSet(AnswerSet):
def get_absolute_url(self):
return self.relationship.get_absolute_url()
class OrganisationRelationshipQuestion(Question):
"""Question which may be asked about an :class:`OrganisationRelationship`."""
class OrganisationRelationshipQuestionChoice(QuestionChoice):
"""Allowed answer to a :class:`OrganisationRelationshipQuestion`."""
#: Question to which this answer belongs
question = models.ForeignKey(OrganisationRelationshipQuestion,
related_name='answers',
on_delete=models.CASCADE,
blank=False,
null=False)
class OrganisationRelationship(models.Model):
"""A directional relationship between a person and an organisation with linked questions."""
class Meta:
constraints = [
models.UniqueConstraint(fields=['source', 'target'],
name='unique_relationship'),
]
#: Person reporting the relationship
source = models.ForeignKey(
Person,
related_name='organisation_relationships_as_source',
on_delete=models.CASCADE,
blank=False,
null=False)
#: Organisation with which the relationship is reported
target = models.ForeignKey(
Organisation,
related_name='organisation_relationships_as_target',
on_delete=models.CASCADE,
blank=False,
null=False)
#: When was this relationship defined?
created = models.DateTimeField(auto_now_add=True)
#: When was this marked as expired? Default None means it has not expired
expired = models.DateTimeField(blank=True, null=True)
@property
def current_answers(self) -> 'OrganisationRelationshipAnswerSet':
return self.answer_sets.last()
def get_absolute_url(self):
return reverse('people:organisation.relationship.detail',
kwargs={'pk': self.pk})
def __str__(self) -> str:
return f'{self.source} -> {self.target}'
class OrganisationRelationshipAnswerSet(AnswerSet):
"""The answers to the organisation relationship questions at a particular point in time."""
#: OrganisationRelationship to which this answer set belongs
relationship = models.ForeignKey(OrganisationRelationship,
on_delete=models.CASCADE,
related_name='answer_sets',
blank=False,
null=False)
#: Answers to :class:`OrganisationRelationshipQuestion`s
question_answers = models.ManyToManyField(
OrganisationRelationshipQuestionChoice)
def get_absolute_url(self):
return self.relationship.get_absolute_url()