diff --git a/people/migrations/0002_add_relationship_models.py b/people/migrations/0002_add_relationship_models.py new file mode 100644 index 0000000..b5a2d93 --- /dev/null +++ b/people/migrations/0002_add_relationship_models.py @@ -0,0 +1,59 @@ +# Generated by Django 2.2.9 on 2020-01-30 15:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('people', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('core_member', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='RelationshipQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('version', models.PositiveSmallIntegerField(default=1)), + ('text', models.CharField(max_length=1023)), + ], + ), + migrations.CreateModel( + name='RelationshipQuestionChoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.CharField(max_length=1023)), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='people.RelationshipQuestion')), + ], + ), + migrations.CreateModel( + name='Relationship', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='relationships_as_source', to='people.Person')), + ('target', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='relationships_as_target', to='people.Person')), + ], + ), + migrations.AddField( + model_name='person', + name='relationship_targets', + field=models.ManyToManyField(related_name='relationship_sources', through='people.Relationship', to='people.Person'), + ), + migrations.AddConstraint( + model_name='relationshipquestionchoice', + constraint=models.UniqueConstraint(fields=('question', 'text'), name='unique_question_answer'), + ), + migrations.AddConstraint( + model_name='relationship', + constraint=models.UniqueConstraint(fields=('source', 'target'), name='unique_relationship'), + ), + ] diff --git a/people/migrations/0003_fix_people_plural.py b/people/migrations/0003_fix_people_plural.py new file mode 100644 index 0000000..867471b --- /dev/null +++ b/people/migrations/0003_fix_people_plural.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.9 on 2020-02-14 09:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('people', '0002_add_relationship_models'), + ] + + operations = [ + migrations.AlterModelOptions( + name='person', + options={'verbose_name_plural': 'people'}, + ), + ] diff --git a/people/models.py b/people/models.py index b21370b..c828eb5 100644 --- a/people/models.py +++ b/people/models.py @@ -1,8 +1,86 @@ from django.contrib.auth.models import AbstractUser +from django.db import models class User(AbstractUser): """ Custom user model in case we need to make changes later. """ - pass + + +class Person(models.Model): + """ + A person may be a member of the BRECcIA core team or an external stakeholder. + """ + + class Meta: + verbose_name_plural = 'people' + + #: Name of the person + name = models.CharField(max_length=255, + blank=False, null=False) + + #: Is this person a member of the core project team? + core_member = models.BooleanField(default=False, + blank=False, null=False) + + #: People with whom this person has relationship - via intermediate :class:`Relationship` model + relationship_targets = models.ManyToManyField('self', related_name='relationship_sources', + through='Relationship', + through_fields=('source', 'target'), + symmetrical=False) + + +class RelationshipQuestion(models.Model): + """ + Question which may be asked about a relationship. + """ + #: Version number of this question - to allow modification without invalidating existing data + version = models.PositiveSmallIntegerField(default=1, + blank=False, null=False) + + #: Text of question + text = models.CharField(max_length=1023, + blank=False, null=False) + + +class RelationshipQuestionChoice(models.Model): + """ + Allowed answer to a :class:`RelationshipQuestion`. + """ + class Meta: + constraints = [ + models.UniqueConstraint(fields=['question', 'text'], + name='unique_question_answer') + ] + + #: Question to which this answer belongs + question = models.ForeignKey(RelationshipQuestion, related_name='answers', + on_delete=models.CASCADE, + blank=False, null=False) + + #: Text of answer + text = models.CharField(max_length=1023, + blank=False, null=False) + + +class Relationship(models.Model): + """ + A directional relationship between two people allowing linked questions. + """ + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['source', 'target'], + name='unique_relationship'), + ] + + #: Person reporting the relationship + source = models.ForeignKey(Person, related_name='relationships_as_source', + on_delete=models.CASCADE, + blank=False, null=False) + + #: Person with whom the relationship is reported + target = models.ForeignKey(Person, related_name='relationships_as_target', + on_delete=models.CASCADE, + blank=False, null=False)