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

Merge branch 'master' into GREG-9_audit_log

parents d87e4450 d4c6f053
No related branches found
No related tags found
1 merge request!9GREG-9 Audit log
Showing
with 307 additions and 28 deletions
......@@ -10,8 +10,7 @@
.settings/
.venv/
.vscode/
venv/
venv*/
gregsite/db.sqlite3
gregsite/settings/local.py
gregsite/static/
......@@ -5,6 +5,7 @@ django-settings-module=gregsite.settings
[MESSAGES CONTROL]
disable=
duplicate-code,
fixme,
import-outside-toplevel,
invalid-name,
......@@ -13,6 +14,7 @@ disable=
missing-function-docstring,
missing-module-docstring,
no-self-use,
redefined-outer-name,
too-few-public-methods,
too-many-ancestors,
unused-argument,
DJANGO_SETTINGS_MODULE ?= gregsite.settings.dev
BLACK ?= black -q
MYPY ?= mypy
PIP ?= pip -q
POETRY ?= poetry
PYLINT ?= pylint -sn
PYTEST ?= pytest -v -s --no-header
PYTHON ?= python3.9
VENV ?= venv
mypy = $(MYPY) --config-file mypy.ini
pip = python -m $(PIP)
poetry = python -m $(POETRY)
pytest = DJANGO_SETTINGS_MODULE=$(DJANGO_SETTINGS_MODULE) python -m $(PYTEST)
venv = . $(VENV)/bin/activate &&
PACKAGES = greg/ gregsite/
......@@ -30,7 +34,7 @@ $(VENV)/touchfile:
.PHONY: test
test: $(VENV)
$(venv) $(mypy) -p greg
$(venv) python manage.py test
$(venv) $(pytest)
.PHONY: lint
lint: $(VENV)
......
......@@ -12,6 +12,10 @@ class PersonRoleFilter(filters.FilterSet):
class PersonFilter(filters.FilterSet):
verified = filters.BooleanFilter(
field_name="person__verified_by_id", lookup_expr="isnull", exclude=True
)
class Meta:
model = Person
fields = ["first_name"]
fields = ["first_name", "last_name", "verified"]
from rest_framework.serializers import ModelSerializer
from greg.models import Consent
class ConsentSerializer(ModelSerializer):
class Meta:
model = Consent
fields = "__all__"
from rest_framework.serializers import ModelSerializer
from greg.models import OrganizationalUnit
class OrganizationalUnitSerializer(ModelSerializer):
class Meta:
model = OrganizationalUnit
fields = "__all__"
......@@ -6,7 +6,14 @@ from greg.models import Person, PersonRole, Role
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ("id", "first_name", "last_name", "date_of_birth", "email", "mobile_phone")
fields = [
"id",
"first_name",
"last_name",
"date_of_birth",
"email",
"mobile_phone",
]
class PersonRoleSerializer(serializers.ModelSerializer):
......@@ -16,6 +23,10 @@ class PersonRoleSerializer(serializers.ModelSerializer):
model = PersonRole
fields = [
"id",
"start_date",
"end_date",
"registered_by",
"unit",
"created",
"updated",
"role",
......
from rest_framework import serializers
from greg.models import Sponsor
class SponsorSerializer(serializers.ModelSerializer):
class Meta:
model = Sponsor
fields = ["id", "feide_id"]
from django.conf.urls import url
from django.urls import path
from django.urls import (
path,
re_path,
)
from rest_framework.routers import DefaultRouter
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
)
from greg.api.views.person import PersonViewSet, PersonRoleViewSet
from greg.api.views.consent import ConsentViewSet
from greg.api.views.organizational_unit import OrganizationalUnitViewSet
from greg.api.views.person import (
PersonRoleViewSet,
PersonViewSet,
)
from greg.api.views.role import RoleViewSet
from greg.api.views.health import Health
from greg.api.views.sponsor import SponsorViewSet
router = DefaultRouter()
router.register(r"persons", PersonViewSet, basename="person")
router.register(r"roles", RoleViewSet, basename="role")
router.register(r"consents", ConsentViewSet, basename="consent")
router.register(r"sponsors", SponsorViewSet, basename="sponsor")
router.register(r"orgunit", OrganizationalUnitViewSet, basename="orgunit")
urlpatterns = router.urls
......@@ -21,12 +36,12 @@ urlpatterns += [
name="swagger-ui",
),
path("health/", Health.as_view()),
url(
re_path(
r"^persons/(?P<person_id>[0-9]+)/roles/$",
PersonRoleViewSet.as_view({"get": "list"}),
PersonRoleViewSet.as_view({"get": "list", "post": "create"}),
name="person_role-list",
),
url(
re_path(
r"^persons/(?P<person_id>[0-9]+)/roles/(?P<id>[0-9]+)/$",
PersonRoleViewSet.as_view({"get": "retrieve"}),
name="person_role-detail",
......
from rest_framework import viewsets
from greg.api.pagination import PrimaryKeyCursorPagination
from greg.api.serializers.consent import ConsentSerializer
from greg.models import Consent
class ConsentViewSet(viewsets.ModelViewSet):
"""Consent API"""
queryset = Consent.objects.all().order_by("id")
serializer_class = ConsentSerializer
pagination_class = PrimaryKeyCursorPagination
lookup_field = "id"
from rest_framework import viewsets
from greg.api.pagination import PrimaryKeyCursorPagination
from greg.api.serializers.organizational_unit import OrganizationalUnitSerializer
from greg.models import OrganizationalUnit
class OrganizationalUnitViewSet(viewsets.ModelViewSet):
"""OrganizationalUnit API"""
queryset = OrganizationalUnit.objects.all().order_by("id")
serializer_class = OrganizationalUnitSerializer
pagination_class = PrimaryKeyCursorPagination
lookup_field = "id"
from django.core.exceptions import ValidationError
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import viewsets
from greg.api.filters import PersonFilter, PersonRoleFilter
from greg.api.pagination import PrimaryKeyCursorPagination
from greg.api.serializers.person import PersonSerializer, PersonRoleSerializer
from greg.api.filters import PersonFilter, PersonRoleFilter
from greg.models import Person, PersonRole
......@@ -18,6 +19,20 @@ class PersonViewSet(viewsets.ModelViewSet):
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = PersonFilter
@extend_schema(
parameters=[
OpenApiParameter(
name="verified",
description="Only include verified or only not verified. When nothing is set, "
"both verified and not verified are returned",
required=False,
type=bool,
)
]
)
def list(self, request, *args, **kwargs):
return super().list(request)
class PersonRoleViewSet(viewsets.ModelViewSet):
"""Person role API"""
......@@ -38,3 +53,12 @@ class PersonRoleViewSet(viewsets.ModelViewSet):
if person_role_id:
qs = qs.filter(id=person_role_id)
return qs
def perform_create(self, serializer):
person_id = self.kwargs["person_id"]
if person_id is None:
# Should not happen, the person ID is part of the API path
raise ValidationError("No person id")
serializer.save(person_id=person_id)
from rest_framework.viewsets import ReadOnlyModelViewSet
from greg.api.pagination import PrimaryKeyCursorPagination
from greg.api.serializers.sponsor import SponsorSerializer
from greg.models import Sponsor
class SponsorViewSet(ReadOnlyModelViewSet):
"""Sponsor API"""
queryset = Sponsor.objects.all().order_by("id")
serializer_class = SponsorSerializer
pagination_class = PrimaryKeyCursorPagination
lookup_field = "id"
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
class GregAppConfig(AppConfig):
......
# Generated by Django 3.2.5 on 2021-07-14 12:28
# Generated by Django 3.2.5 on 2021-07-15 13:31
import datetime
import dirtyfields.dirtyfields
......@@ -147,6 +147,9 @@ class Migration(migrations.Migration):
('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')),
],
options={
'abstract': False,
},
bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
),
migrations.CreateModel(
......@@ -198,10 +201,6 @@ class Migration(migrations.Migration):
model_name='sponsor',
constraint=models.UniqueConstraint(fields=('feide_id',), name='unique_feide_id'),
),
migrations.AddConstraint(
model_name='personrole',
constraint=models.UniqueConstraint(fields=('person', 'role', 'unit'), name='personrole_person_role_unit_unique'),
),
migrations.AddConstraint(
model_name='personconsent',
constraint=models.UniqueConstraint(fields=('person', 'consent'), name='person_consent_unique'),
......
......@@ -103,14 +103,6 @@ class PersonRole(BaseModel):
"Sponsor", on_delete=models.PROTECT, related_name="sponsor_role"
)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["person", "role", "unit"],
name="personrole_person_role_unit_unique",
)
]
def __repr__(self):
return "{}(id={!r}, person={!r}, role={!r})".format(
self.__class__.__name__, self.pk, self.person, self.role
......
import pytest
from rest_framework import status
from rest_framework.reverse import reverse
from greg.models import Consent
@pytest.fixture
def consent_foo() -> Consent:
return Consent.objects.create(
type="test_consent",
consent_name_en="Test1",
consent_name_nb="Test2",
consent_description_en="Test description",
consent_description_nb="Test beskrivelse",
consent_link_en="https://example.org",
consent_link_nb="https://example.org",
valid_from="2018-01-20",
user_allowed_to_change=True,
)
@pytest.mark.django_db
def test_get_consent(client, consent_foo):
resp = client.get(reverse("consent-detail", kwargs={"id": consent_foo.id}))
assert resp.status_code == status.HTTP_200_OK
data = resp.json()
assert data.get("id") == consent_foo.id
assert data.get("type") == consent_foo.type
assert data.get("consent_name_en") == consent_foo.consent_name_en
import pytest
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.status import HTTP_200_OK
from greg.models import Person
@pytest.fixture
def person_foo() -> Person:
return Person.objects.create(
first_name="Foo",
last_name="Foo",
date_of_birth="2001-01-27",
email="test@example.org",
mobile_phone="123456788",
)
@pytest.fixture
def person_bar() -> Person:
return Person.objects.create(
first_name="Bar",
last_name="Bar",
date_of_birth="2000-07-01",
email="test2@example.org",
mobile_phone="123456789",
)
@pytest.mark.django_db
def test_get_person(client, person_foo):
resp = client.get(reverse("person-detail", kwargs={"id": person_foo.id}))
assert resp.status_code == HTTP_200_OK
data = resp.json()
assert data.get("id") == person_foo.id
assert data.get("first_name") == person_foo.first_name
assert data.get("last_name") == person_foo.last_name
@pytest.mark.django_db
def test_persons(client, person_foo, person_bar):
resp = client.get(reverse("person-list"))
assert resp.status_code == HTTP_200_OK
data = resp.json()
assert len(data["results"]) == 2
@pytest.mark.django_db
def test_persons_verified_filter_include(client, setup_db_test_data):
url = reverse("person-list")
response = client.get(url, {"verified": "true"})
results = response.json()["results"]
assert len(results) == 1
# The following person will have a verified identity set up for him
# in the test data
assert results[0]["first_name"] == "Christopher"
assert results[0]["last_name"] == "Flores"
@pytest.mark.django_db
def test_persons_verified_filter_exclude(client, setup_db_test_data):
url = reverse("person-list")
response = client.get(url, {"verified": "false"})
results = response.json()["results"]
names = [(result["first_name"], result["last_name"]) for result in results]
assert len(results) == 9
assert ("Christopher", "Flores") not in names
@pytest.mark.django_db
def test_add_role(client, person_foo):
url = reverse("person_role-list", kwargs={"person_id": person_foo.id})
roles_for_person = client.get(url).json()["results"]
# Check that there are no roles for the person, and then add one
assert len(roles_for_person) == 0
role_data = {
"role": "Visiting Professor",
"start_date": "2021-06-10",
"end_date": "2021-08-10",
"registered_by": "1",
"unit": "1",
}
response = client.post(url, role_data)
assert response.status_code == status.HTTP_201_CREATED
response_data = response.json()
roles_for_person = client.get(url).json()["results"]
# Check that the role shows up when listing roles for the person now
assert len(roles_for_person) == 1
assert roles_for_person[0]["id"] == response_data["id"]
import logging
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model
# faker spams the logs with localisation warnings
# see https://github.com/joke2k/faker/issues/753
import pytest
from greg.tests.populate_database import DatabasePopulation
logging.getLogger("faker").setLevel(logging.ERROR)
# The database is populated once when scope is session.
# If the scope is changed to function some additional
# logic is needed to make sure the old data is cleaned
# before the seeding is run again
@pytest.fixture(scope="session")
def setup_db_test_data(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
database_seeder = DatabasePopulation()
database_seeder.populate_database()
@pytest.fixture
def client() -> APIClient:
user, _ = get_user_model().objects.get_or_create(username="test")
token, _ = Token.objects.get_or_create(user=user)
client = APIClient()
client.credentials(HTTP_AUTHORIZATION=f"Token {token.key}")
return client
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