Skip to content
Snippets Groups Projects
Commit 7cdf830a authored by Tore.Brede's avatar Tore.Brede
Browse files

GREG-3: Adding more model classes

parent f6ac5016
No related branches found
No related tags found
1 merge request!1GREG-3: Adding more model classes
# Generated by Django 3.2.5 on 2021-07-09 07:27
# Generated by Django 3.2.5 on 2021-07-13 06:47
import datetime
import dirtyfields.dirtyfields
from django.db import migrations, models
import django.db.models.deletion
......@@ -13,6 +14,27 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='Consent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('type', models.SlugField(max_length=64, unique=True)),
('consent_name_en', models.CharField(max_length=256)),
('consent_name_nb', models.CharField(max_length=256)),
('consent_description_en', models.TextField()),
('consent_description_nb', models.TextField()),
('consent_link_en', models.URLField(null=True)),
('consent_link_nb', models.URLField(null=True)),
('valid_from', models.DateField(default=datetime.date.today)),
('user_allowed_to_change', models.BooleanField()),
],
options={
'abstract': False,
},
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='Notification',
fields=[
......@@ -30,6 +52,19 @@ class Migration(migrations.Migration):
},
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='OrganizationalUnit',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('orgreg_id', models.CharField(max_length=256)),
('name_nb', models.CharField(max_length=256)),
('name_en', models.CharField(max_length=256)),
('parent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='greg.organizationalunit')),
],
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='Person',
fields=[
......@@ -38,6 +73,13 @@ class Migration(migrations.Migration):
('updated', models.DateTimeField(auto_now=True)),
('first_name', models.CharField(max_length=256)),
('last_name', models.CharField(max_length=256)),
('date_of_birth', models.DateField()),
('email', models.EmailField(max_length=254)),
('email_verified_date', models.DateField(blank=True, null=True)),
('mobile_phone', models.CharField(max_length=15)),
('mobile_phone_verified_date', models.DateField(blank=True, null=True)),
('registration_completed_date', models.DateField(blank=True, null=True)),
('token', models.CharField(blank=True, max_length=32)),
],
options={
'abstract': False,
......@@ -53,30 +95,119 @@ class Migration(migrations.Migration):
('type', models.SlugField(max_length=64, unique=True)),
('name_nb', models.CharField(max_length=256)),
('name_en', models.CharField(max_length=256)),
('meta', models.JSONField(blank=True, null=True)),
('description_nb', models.TextField()),
('description_en', models.TextField()),
('default_duration_days', models.IntegerField(null=True)),
],
options={
'abstract': False,
},
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='Sponsor',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('feide_id', models.CharField(max_length=256)),
],
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='SponsorOrganizationalUnit',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('hierarchical_access', models.BooleanField()),
('organizational_unit', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_unit', to='greg.organizationalunit')),
('sponsor', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_sponsor', to='greg.sponsor')),
],
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.AddField(
model_name='sponsor',
name='units',
field=models.ManyToManyField(related_name='sponsor_unit', through='greg.SponsorOrganizationalUnit', to='greg.OrganizationalUnit'),
),
migrations.CreateModel(
name='PersonRole',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('start_date', models.DateField()),
('end_date', models.DateField()),
('contact_person_unit', models.TextField()),
('comments', models.TextField(blank=True)),
('available_in_search', models.BooleanField(default=False)),
('person', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='person_roles', to='greg.person')),
('registered_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sponsor_role', to='greg.sponsor')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='person_roles', to='greg.role')),
('unit', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unit_person_role', to='greg.organizationalunit')),
],
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='PersonIdentity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('type', models.CharField(choices=[('PASSPORT_NUMBER', 'Passport Number'), ('FEIDE_ID', 'Feide Id')], max_length=15)),
('source', models.CharField(max_length=256)),
('value', models.CharField(max_length=256)),
('verified', models.CharField(blank=True, choices=[('AUTOMATIC', 'Automatic'), ('MANUAL', 'Manual')], max_length=9)),
('verified_when', models.DateField(blank=True)),
('person', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='person', to='greg.person')),
('verified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sponsor', to='greg.sponsor')),
],
options={
'abstract': False,
},
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
name='PersonConsent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('consent_given_at', models.DateField()),
('consent', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_person_consent', to='greg.consent')),
('person', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_person_consent', to='greg.person')),
],
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.AddField(
model_name='person',
name='consents',
field=models.ManyToManyField(related_name='consent', through='greg.PersonConsent', to='greg.Consent'),
),
migrations.AddField(
model_name='person',
name='roles',
field=models.ManyToManyField(related_name='persons', through='greg.PersonRole', to='greg.Role'),
),
migrations.AddConstraint(
model_name='sponsororganizationalunit',
constraint=models.UniqueConstraint(fields=('sponsor', 'organizational_unit'), name='sponsor_organizational_unit_unique'),
),
migrations.AddConstraint(
model_name='sponsor',
constraint=models.UniqueConstraint(fields=('feide_id',), name='unique_feide_id'),
),
migrations.AddConstraint(
model_name='personrole',
constraint=models.UniqueConstraint(fields=('person', 'role'), name='personrole_person_role_unique'),
),
migrations.AddConstraint(
model_name='personconsent',
constraint=models.UniqueConstraint(fields=('person', 'consent'), name='person_consent_unique'),
),
migrations.AddConstraint(
model_name='organizationalunit',
constraint=models.UniqueConstraint(fields=('orgreg_id',), name='unique_orgreg_id'),
),
]
from datetime import date
from dirtyfields import DirtyFieldsMixin
from django.db import models
from django.db.models import Lookup
from django.db.models.fields import Field
from dirtyfields import DirtyFieldsMixin
@Field.register_lookup
class Like(Lookup):
......@@ -29,11 +30,21 @@ class BaseModel(DirtyFieldsMixin, models.Model):
class Person(BaseModel):
"""A person."""
"""A person is someone who has requested guest access."""
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
date_of_birth = models.DateField()
email = models.EmailField()
email_verified_date = models.DateField(null=True, blank=True)
mobile_phone = models.CharField(max_length=15)
mobile_phone_verified_date = models.DateField(null=True, blank=True)
registration_completed_date = models.DateField(null=True, blank=True)
token = models.CharField(max_length=32, blank=True)
roles = models.ManyToManyField("Role", through="PersonRole", related_name="persons")
consents = models.ManyToManyField(
"Consent", through="PersonConsent", related_name="consent"
)
def __str__(self):
return "{} {} ({})".format(self.first_name, self.last_name, self.pk)
......@@ -53,19 +64,20 @@ class Role(BaseModel):
type = models.SlugField(max_length=64, unique=True)
name_nb = models.CharField(max_length=256)
name_en = models.CharField(max_length=256)
meta = models.JSONField(null=True, blank=True)
description_nb = models.TextField()
description_en = models.TextField()
default_duration_days = models.IntegerField(null=True)
def __str__(self):
return str(self.name_nb or self.name_en or self.slug)
def __repr__(self):
return "{}(id={!r}, type={!r}, name_nb={!r}, name_en={!r}, meta={!r})".format(
return "{}(id={!r}, type={!r}, name_nb={!r}, name_en={!r})".format(
self.__class__.__name__,
self.pk,
self.type,
self.name_nb,
self.name_en,
self.meta,
)
......@@ -78,13 +90,25 @@ class PersonRole(BaseModel):
role = models.ForeignKey(
"Role", on_delete=models.PROTECT, related_name="person_roles"
)
unit = models.ForeignKey(
"OrganizationalUnit", on_delete=models.PROTECT, related_name="unit_person_role"
)
start_date = models.DateField()
end_date = models.DateField()
# TODO Is this field needed?
contact_person_unit = models.TextField()
comments = models.TextField(blank=True)
available_in_search = models.BooleanField(default=False)
registered_by = models.ForeignKey(
"Sponsor", on_delete=models.PROTECT, related_name="sponsor_role"
)
# class Meta:
# constraints = [
# models.UniqueConstraint(
# fields=["person", "role"], name="personrole_person_role_unique"
# )
# ]
class Meta:
constraints = [
models.UniqueConstraint(
fields=["person", "role"], name="personrole_person_role_unique"
)
]
def __repr__(self):
return "{}(id={!r}, person={!r}, role={!r})".format(
......@@ -111,3 +135,169 @@ class Notification(BaseModel):
self.issued_at,
self.meta,
)
class PersonIdentity(BaseModel):
# TODO: Add more types
class IdentityType(models.TextChoices):
PASSPORT_NUMBER = "PASSPORT_NUMBER"
FEIDE_ID = "FEIDE_ID"
class Verified(models.TextChoices):
AUTOMATIC = "AUTOMATIC"
MANUAL = "MANUAL"
person = models.ForeignKey(
"Person", on_delete=models.PROTECT, related_name="person"
)
type = models.CharField(max_length=15, choices=IdentityType.choices)
source = models.CharField(max_length=256)
value = models.CharField(max_length=256)
verified = models.CharField(max_length=9, choices=Verified.choices, blank=True)
verified_by = models.ForeignKey(
"Sponsor", on_delete=models.PROTECT, related_name="sponsor", null=True
)
verified_when = models.DateField(blank=True)
def __repr__(self):
return (
"{}(id={!r}, type={!r}, source={!r}, value={!r}, verified_by={!r})".format(
self.__class__.__name__,
self.pk,
self.type,
self.source,
self.value,
self.verified_by,
)
)
class Consent(BaseModel):
"""
Describes some consent, like acknowledging the IT department guidelines, a guest can give.
"""
type = models.SlugField(max_length=64, unique=True)
consent_name_en = models.CharField(max_length=256)
consent_name_nb = models.CharField(max_length=256)
consent_description_en = models.TextField()
consent_description_nb = models.TextField()
consent_link_en = models.URLField(null=True)
consent_link_nb = models.URLField(null=True)
valid_from = models.DateField(default=date.today)
user_allowed_to_change = models.BooleanField()
def __repr__(self):
return "{}(id={!r}, type={!r}, consent_name_en={!r}, valid_from={!r}, user_allowed_to_change={!r})".format(
self.__class__.__name__,
self.pk,
self.type,
self.consent_name_en,
self.valid_from,
self.user_allowed_to_change,
)
class PersonConsent(BaseModel):
"""
Links a person and a consent he has given.
"""
person = models.ForeignKey(
"Person", on_delete=models.PROTECT, related_name="link_person_consent"
)
consent = models.ForeignKey(
"Consent", on_delete=models.PROTECT, related_name="link_person_consent"
)
consent_given_at = models.DateField()
class Meta:
constraints = [
models.UniqueConstraint(
fields=["person", "consent"], name="person_consent_unique"
)
]
def __repr__(self):
return "{}(id={!r}, person={!r}, consent={!r}, consent_given_at={!r})".format(
self.__class__.__name__,
self.pk,
self.person,
self.consent,
self.consent_given_at,
)
class OrganizationalUnit(BaseModel):
"""
An organizational unit. Units can be organized in a hierarchical manner.
"""
orgreg_id = models.CharField(max_length=256)
name_nb = models.CharField(max_length=256)
name_en = models.CharField(max_length=256)
parent = models.ForeignKey("self", on_delete=models.PROTECT, null=True)
def __repr__(self):
return "{}(id={!r}, orgreg_id={!r}, name_en={!r}, parent={!r})".format(
self.__class__.__name__, self.pk, self.orgreg_id, self.name_en, self.parent
)
class Meta:
constraints = [
models.UniqueConstraint(name="unique_orgreg_id", fields=["orgreg_id"])
]
class Sponsor(BaseModel):
"""
A sponsor is someone who is allowed, with some restrictions, to send out invitations to guests and to verify their identity.
"""
feide_id = models.CharField(max_length=256)
units = models.ManyToManyField(
"OrganizationalUnit",
through="SponsorOrganizationalUnit",
related_name="sponsor_unit",
)
def __repr__(self):
return "{}(id={!r}, feide_id={!r})".format(
self.__class__.__name__, self.pk, self.feide_id
)
class Meta:
constraints = [
models.UniqueConstraint(name="unique_feide_id", fields=["feide_id"])
]
class SponsorOrganizationalUnit(BaseModel):
"""
A link between a sponsor and an organizational unit.
"""
sponsor = models.ForeignKey(
"Sponsor", on_delete=models.PROTECT, related_name="link_sponsor"
)
organizational_unit = models.ForeignKey(
"OrganizationalUnit", on_delete=models.PROTECT, related_name="link_unit"
)
hierarchical_access = models.BooleanField()
class Meta:
constraints = [
models.UniqueConstraint(
fields=["sponsor", "organizational_unit"],
name="sponsor_organizational_unit_unique",
)
]
def __repr__(self):
return "{}(id={!r}, sponsor={!r}, organizational_unit={!r}, hierarchical_access={!r})".format(
self.__class__.__name__,
self.pk,
self.sponsor,
self.organizational_unit,
self.hierarchical_access,
)
......@@ -28,10 +28,16 @@ class PersonTestData:
self.person_foo_data = dict(
first_name="Foo",
last_name="Foo",
date_of_birth="2000-01-27",
email="test@example.org",
mobile_phone="123456788",
)
self.person_bar_data = dict(
first_name="Bar",
last_name="Bar",
date_of_birth="2000-07-01",
email="test2@example.org",
mobile_phone="123456789",
)
self.person_foo = Person.objects.create(**self.person_foo_data)
self.person_bar = Person.objects.create(**self.person_bar_data)
......
from django.db.models import ProtectedError
from django.test import TestCase
from greg.models import (
Person,
Role,
PersonRole,
OrganizationalUnit,
Sponsor,
SponsorOrganizationalUnit,
Consent,
)
class PersonModelTests(TestCase):
test_person = dict(
first_name="Test",
last_name="Tester",
date_of_birth="2000-01-27",
email="test@example.org",
mobile_phone="123456789",
)
def test_add_multiple_roles_to_person(self):
role1 = Role.objects.create(type="role1", name_en="Role 1")
role2 = Role.objects.create(type="role2", name_en="Role 2")
unit = OrganizationalUnit.objects.create(orgreg_id="12345", name_en="Test unit")
sponsor = Sponsor.objects.create(feide_id="test@uio.no")
sponsor2 = Sponsor.objects.create(feide_id="test2@uio.no")
person = Person.objects.create(**self.test_person)
# Add two roles to the person and check that they appear when listing the roles for the person
PersonRole.objects.create(
person=person,
role=role1,
unit=unit,
start_date="2020-03-05",
end_date="2020-06-10",
contact_person_unit="Contact Person",
available_in_search=True,
registered_by=sponsor,
)
PersonRole.objects.create(
person=person,
role=role2,
unit=unit,
start_date="2021-03-05",
end_date="2021-06-10",
contact_person_unit="Contact Person",
available_in_search=True,
registered_by=sponsor2,
)
person_roles = person.roles.all()
self.assertEqual(2, len(person_roles))
self.assertIn(role1, person_roles)
self.assertIn(role2, person_roles)
def test_person_not_allowed_deleted_when_roles_are_present(self):
person = Person.objects.create(**self.test_person)
role = Role.objects.create(type="role1", name_en="Role 1")
unit = OrganizationalUnit.objects.create(orgreg_id="12345", name_en="Test unit")
sponsor = Sponsor.objects.create(feide_id="test@uio.no")
PersonRole.objects.create(
person=person,
role=role,
unit=unit,
start_date="2020-03-05",
end_date="2020-06-10",
contact_person_unit="Contact Person",
available_in_search=True,
registered_by=sponsor,
)
# It is not clear what cleanup needs to be done when a person is going to be
# removed, so for now is prohibited to delete a person if there is data
# attached to him in other tables
self.assertRaises(ProtectedError, person.delete)
class SponsorModelTests(TestCase):
def test_add_sponsor_to_multiple_units(self):
sponsor = Sponsor.objects.create(feide_id="test@uio.no")
unit1 = OrganizationalUnit.objects.create(
orgreg_id="12345", name_en="Test unit"
)
unit2 = OrganizationalUnit.objects.create(
orgreg_id="123456", name_en="Test unit2"
)
SponsorOrganizationalUnit.objects.create(
sponsor=sponsor, organizational_unit=unit1, hierarchical_access=False
)
SponsorOrganizationalUnit.objects.create(
sponsor=sponsor, organizational_unit=unit2, hierarchical_access=False
)
sponsor_units = sponsor.units.all()
self.assertEqual(2, len(sponsor_units))
self.assertIn(unit1, sponsor_units)
self.assertIn(unit2, sponsor_units)
def test_add_multiple_sponsors_to_unit(self):
sponsor1 = Sponsor.objects.create(feide_id="test@uio.no")
sponsor2 = Sponsor.objects.create(feide_id="test2@uio.no")
unit = OrganizationalUnit.objects.create(orgreg_id="12345", name_en="Test unit")
unit2 = OrganizationalUnit.objects.create(
orgreg_id="123456", name_en="Test unit2"
)
SponsorOrganizationalUnit.objects.create(
sponsor=sponsor1, organizational_unit=unit, hierarchical_access=False
)
SponsorOrganizationalUnit.objects.create(
sponsor=sponsor2, organizational_unit=unit, hierarchical_access=True
)
sponsor1_units = sponsor1.units.all()
self.assertEqual(1, len(sponsor1_units))
self.assertIn(unit, sponsor1_units)
sponsor2_units = sponsor2.units.all()
self.assertEqual(1, len(sponsor2_units))
self.assertIn(unit, sponsor2_units)
sponsors_for_unit = Sponsor.objects.filter(units=unit.id)
self.assertEqual(2, len(sponsors_for_unit))
self.assertIn(sponsor1, sponsors_for_unit)
self.assertIn(sponsor2, sponsors_for_unit)
sponsors_for_unit2 = Sponsor.objects.filter(units=unit2.id)
self.assertEqual(0, len(sponsors_for_unit2))
class ConsentModelTest(TestCase):
def test_add_consent_to_person(self):
person = Person.objects.create(
first_name="Test",
last_name="Tester",
date_of_birth="2000-01-27",
email="test@example.org",
mobile_phone="123456789",
)
consent = Consent.objects.create(
type="it_guidelines",
consent_name_en="IT Guidelines",
consent_name_nb="IT Regelverk",
consent_description_en="IT Guidelines description",
consent_description_nb="IT Regelverk beskrivelse",
consent_link_en="https://example.org/it_guidelines",
user_allowed_to_change=False,
)
person.consents.add(
consent, through_defaults={"consent_given_at": "2021-06-20"}
)
class OrganizationalUnitTest(TestCase):
def test_set_parent_for_unit(self):
parent = OrganizationalUnit.objects.create(
orgreg_id="12345", name_en="Parent unit", name_nb="Foreldreseksjon"
)
child = OrganizationalUnit.objects.create(
orgreg_id="123456",
name_en="Child unit",
name_nb="Barneseksjon",
parent=parent,
)
query_result = OrganizationalUnit.objects.filter(parent__id=parent.id)
self.assertEqual(1, len(query_result))
self.assertIn(child, query_result)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment