Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • andretol/greg
1 result
Show changes
...@@ -33,12 +33,12 @@ def role( ...@@ -33,12 +33,12 @@ def role(
return Role.objects.create( return Role.objects.create(
person=person, person=person,
type=role_type_bar, type=role_type_bar,
orgunit_id=unit_foo, orgunit=unit_foo,
start_date="2020-03-05", start_date="2020-03-05",
end_date=datetime.today() + timedelta(days=30), end_date=datetime.today() + timedelta(days=30),
contact_person_unit="Contact Person", contact_person_unit="Contact Person",
available_in_search=True, available_in_search=True,
sponsor_id=sponsor_guy, sponsor=sponsor_guy,
) )
......
...@@ -73,8 +73,8 @@ def test_role_add_notification( ...@@ -73,8 +73,8 @@ def test_role_add_notification(
type=role_type_foo, type=role_type_foo,
start_date="2021-05-06", start_date="2021-05-06",
end_date="2021-10-20", end_date="2021-10-20",
orgunit_id=org_unit_bar, orgunit=org_unit_bar,
sponsor_id=sponsor, sponsor=sponsor,
) )
notifications = Notification.objects.filter(object_type="Role") notifications = Notification.objects.filter(object_type="Role")
assert len(notifications) == 1 assert len(notifications) == 1
...@@ -97,8 +97,8 @@ def test_role_update_notification( ...@@ -97,8 +97,8 @@ def test_role_update_notification(
type=role_type_foo, type=role_type_foo,
start_date="2021-05-06", start_date="2021-05-06",
end_date="2021-10-20", end_date="2021-10-20",
orgunit_id=org_unit_bar, orgunit=org_unit_bar,
sponsor_id=sponsor, sponsor=sponsor,
) )
assert len(person.roles.all()) == 1 assert len(person.roles.all()) == 1
person_role = person.roles.all()[0] person_role = person.roles.all()[0]
...@@ -125,8 +125,8 @@ def test_role_delete_notification( ...@@ -125,8 +125,8 @@ def test_role_delete_notification(
type=role_type_foo, type=role_type_foo,
start_date="2021-05-06", start_date="2021-05-06",
end_date="2021-10-20", end_date="2021-10-20",
orgunit_id=org_unit_bar, orgunit=org_unit_bar,
sponsor_id=sponsor, sponsor=sponsor,
) )
assert len(person.roles.all()) == 1 assert len(person.roles.all()) == 1
person_role = person.roles.all()[0] person_role = person.roles.all()[0]
......
...@@ -5,13 +5,13 @@ from django.utils import timezone ...@@ -5,13 +5,13 @@ from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers
from greg.models import Invitation, InvitationLink, Person, Role, Identity from greg.models import Invitation, InvitationLink, Person, Role, Identity
from gregui.api.serializers.role import RoleSerializerUi from gregui.api.serializers.role import InviteRoleSerializerUi
from gregui.models import GregUserProfile from gregui.models import GregUserProfile
class InviteGuestSerializer(serializers.ModelSerializer): class InviteGuestSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True) email = serializers.EmailField(required=True)
role = RoleSerializerUi(required=True) role = InviteRoleSerializerUi(required=True)
uuid = serializers.UUIDField(read_only=True) uuid = serializers.UUIDField(read_only=True)
def create(self, validated_data): def create(self, validated_data):
...@@ -30,7 +30,7 @@ class InviteGuestSerializer(serializers.ModelSerializer): ...@@ -30,7 +30,7 @@ class InviteGuestSerializer(serializers.ModelSerializer):
source="greg", source="greg",
) )
role_data["person"] = person role_data["person"] = person
role_data["sponsor_id"] = user.sponsor role_data["sponsor"] = user.sponsor
role = Role.objects.create(**role_data) role = Role.objects.create(**role_data)
invitation = Invitation.objects.create(role=role) invitation = Invitation.objects.create(role=role)
InvitationLink.objects.create( InvitationLink.objects.create(
......
import datetime import datetime
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.validators import UniqueTogetherValidator
from greg.models import Role from greg.models import Role
class RoleSerializerUi(serializers.ModelSerializer): class RoleSerializerUi(serializers.ModelSerializer):
def update(self, instance, validated_data): """Serializer for the Role model with validation of various fields"""
# validate new date is after old date, and old date has not passed
class Meta:
model = Role
fields = [
"orgunit",
"start_date",
"type",
"end_date",
"contact_person_unit",
"comments",
"available_in_search",
"person",
]
validators = [
UniqueTogetherValidator(
queryset=Role.objects.all(),
fields=["person", "type", "orgunit", "start_date", "end_date"],
)
]
def validate_start_date(self, start_date):
today = datetime.date.today() today = datetime.date.today()
new_start = validated_data.get("start_date") # New start dates cannot be in the past
new_end = validated_data.get("end_date") if start_date < today:
if new_start: raise serializers.ValidationError("Start date cannot be in the past")
new_start = datetime.datetime.strptime(
validated_data["start_date"], "%Y-%m-%d" return start_date
).date()
if new_start < today: def validate_end_date(self, end_date):
raise serializers.ValidationError("Start date cannot be in the past") """Ensure rules for end_date are followed"""
if instance.start_date < today and new_start: today = datetime.date.today()
raise serializers.ValidationError( if end_date < today:
"Role has started, cannot change start date" raise serializers.ValidationError("End date cannot be in the past")
) if self.instance and self.instance.end_date < today:
instance.start_date = new_start raise serializers.ValidationError("Role has ended, cannot change end date")
if new_end: return end_date
new_end = datetime.datetime.strptime(
validated_data["end_date"], "%Y-%m-%d" def validate_orgunit(self, unit):
).date() """Enforce rules related to orgunit"""
if new_end < today: sponsor = self.context["sponsor"]
raise serializers.ValidationError("End date cannot be in the past") units = sponsor.units.all()
if instance.end_date < today and new_end: # Restrict to a sponsor's own units
if not units or unit not in units:
raise ValidationError(
"A sponsor can only make changes to roles at units they are sponsors for."
)
# If we are updating an existing roles, we must be the sponsor of the role
if self.instance and self.instance.sponsor != sponsor:
raise ValidationError("You can only edit your own roles.")
return unit
def validate(self, attrs):
"""Validate things that need access to multiple fields"""
# Ensure end date is not further into the future than the role type allows
today = datetime.date.today()
if self.instance:
max_days = today + datetime.timedelta(days=self.instance.type.max_days)
else:
max_days = today + datetime.timedelta(days=attrs["type"].max_days)
if attrs["end_date"] > max_days:
raise serializers.ValidationError(
f"New end date too far into the future for this type. Must be before {max_days.strftime('%Y-%m-%d')}"
)
# Ensure end date is after start date if start date is set
if self.instance:
start_date = attrs.get("start_date") or self.instance.start_date
end_date = attrs.get("end_date") or self.instance.end_date
if start_date and end_date < start_date:
raise serializers.ValidationError( raise serializers.ValidationError(
"Role has ended, cannot change end date" "End date cannot be before start date."
) )
max_days = today + datetime.timedelta(days=instance.type.max_days) else:
if new_end > max_days: if attrs.get("start_date") and attrs["end_date"] < attrs["start_date"]:
raise serializers.ValidationError( raise serializers.ValidationError(
f"New end date too far into the future. Must be before {max_days.strftime('%Y-%m-%d')}" "End date cannot be before start date."
) )
instance.end_date = new_end return attrs
class InviteRoleSerializerUi(RoleSerializerUi):
"""
Serializer for the role part of an invite.
return instance This one exists so that we don't have to specify the person argument on the role.
Simply reuses all the logic of the parent class except requiring the person field.
"""
class Meta: class Meta:
model = Role model = Role
fields = [ fields = [
"orgunit_id", "orgunit",
"start_date", "start_date",
"type", "type",
"end_date", "end_date",
......
...@@ -6,4 +6,4 @@ from greg.models import RoleType ...@@ -6,4 +6,4 @@ from greg.models import RoleType
class RoleTypeSerializerUi(ModelSerializer): class RoleTypeSerializerUi(ModelSerializer):
class Meta: class Meta:
model = RoleType model = RoleType
fields = ("id", "identifier", "name_en", "name_nb") fields = ("id", "identifier", "name_en", "name_nb", "max_days")
...@@ -8,10 +8,12 @@ from gregui.api.views.invitation import ( ...@@ -8,10 +8,12 @@ from gregui.api.views.invitation import (
InvitedGuestView, InvitedGuestView,
) )
from gregui.api.views.person import PersonSearchView, PersonView from gregui.api.views.person import PersonSearchView, PersonView
from gregui.api.views.role import RoleInfoViewSet
from gregui.api.views.roletypes import RoleTypeViewSet from gregui.api.views.roletypes import RoleTypeViewSet
from gregui.api.views.unit import UnitsViewSet from gregui.api.views.unit import UnitsViewSet
router = DefaultRouter(trailing_slash=False) router = DefaultRouter(trailing_slash=False)
router.register(r"role", RoleInfoViewSet, basename="role")
urlpatterns = router.urls urlpatterns = router.urls
urlpatterns += [ urlpatterns += [
......
...@@ -30,7 +30,7 @@ class CreateInvitationView(CreateAPIView): ...@@ -30,7 +30,7 @@ class CreateInvitationView(CreateAPIView):
"last_name": "sss", "last_name": "sss",
"date_of_birth": null, "date_of_birth": null,
"role": { "role": {
"orgunit_id": 1, "orgunit": 1,
"start_date": null, "start_date": null,
"type": 1, "type": 1,
"end_date": "2021-12-15", "end_date": "2021-12-15",
...@@ -58,16 +58,15 @@ class CreateInvitationView(CreateAPIView): ...@@ -58,16 +58,15 @@ class CreateInvitationView(CreateAPIView):
""" """
sponsor_user = GregUserProfile.objects.get(user=request.user) sponsor_user = GregUserProfile.objects.get(user=request.user)
serializer = self.serializer_class( serializer = self.serializer_class(
data=request.data, context={"request": request} data=request.data,
context={"request": request, "sponsor": sponsor_user.sponsor},
) )
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
# TODO: check that sponsor has access to OU
person = serializer.save() person = serializer.save()
invitationlink = InvitationLink.objects.filter( invitationlink = InvitationLink.objects.filter(
invitation__role__person=person.id, invitation__role__person_id=person.id,
invitation__role__sponsor_id=sponsor_user.sponsor, invitation__role__sponsor_id=sponsor_user.sponsor_id,
) )
# TODO: send email to invited guest # TODO: send email to invited guest
print(invitationlink) print(invitationlink)
...@@ -133,7 +132,7 @@ class InvitedGuestView(GenericAPIView): ...@@ -133,7 +132,7 @@ class InvitedGuestView(GenericAPIView):
invite_link = InvitationLink.objects.get(uuid=invite_id) invite_link = InvitationLink.objects.get(uuid=invite_id)
role = invite_link.invitation.role role = invite_link.invitation.role
person = role.person person = role.person
sponsor = role.sponsor_id sponsor = role.sponsor
# If the user is not logged in then tell the client to take him through the manual registration process # If the user is not logged in then tell the client to take him through the manual registration process
session_type = ( session_type = (
...@@ -165,8 +164,8 @@ class InvitedGuestView(GenericAPIView): ...@@ -165,8 +164,8 @@ class InvitedGuestView(GenericAPIView):
"last_name": sponsor.last_name, "last_name": sponsor.last_name,
}, },
"role": { "role": {
"ou_name_nb": role.orgunit_id.name_nb, "ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit_id.name_en, "ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb, "role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en, "role_name_en": role.type.name_en,
"start": role.start_date, "start": role.start_date,
......
...@@ -29,13 +29,16 @@ class PersonView(APIView): ...@@ -29,13 +29,16 @@ class PersonView(APIView):
"email": person.private_email and person.private_email.value, "email": person.private_email and person.private_email.value,
"mobile": person.private_mobile and person.private_mobile.value, "mobile": person.private_mobile and person.private_mobile.value,
"fnr": person.fnr and "".join((person.fnr.value[:-5], "*****")), "fnr": person.fnr and "".join((person.fnr.value[:-5], "*****")),
"active": person.is_registered and person.is_verified,
"registered": person.is_registered,
"verified": person.is_verified,
"roles": [ "roles": [
{ {
"id": role.id, "id": role.id,
"name_nb": role.type.name_nb, "name_nb": role.type.name_nb,
"name_en": role.type.name_en, "name_en": role.type.name_en,
"ou_nb": role.orgunit_id.name_nb, "ou_nb": role.orgunit.name_nb,
"ou_en": role.orgunit_id.name_en, "ou_en": role.orgunit.name_en,
"start_date": role.start_date, "start_date": role.start_date,
"end_date": role.end_date, "end_date": role.end_date,
"max_days": role.type.max_days, "max_days": role.type.max_days,
......
from django.db import transaction from django.db import transaction
from rest_framework import status from rest_framework import serializers, status
from rest_framework.authentication import BasicAuthentication, SessionAuthentication from rest_framework.authentication import BasicAuthentication, SessionAuthentication
from rest_framework.generics import GenericAPIView from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from greg.models import Role from greg.models import Role
from greg.permissions import IsSponsor from greg.permissions import IsSponsor
from gregui.api.serializers.role import RoleSerializerUi from gregui.api.serializers.role import RoleSerializerUi
from gregui.api.serializers.roletype import RoleTypeSerializerUi from gregui.models import GregUserProfile
class RoleInfoView(GenericAPIView): class RoleInfoViewSet(ModelViewSet):
queryset = Role.objects.all() queryset = Role.objects.all()
authentication_classes = [SessionAuthentication, BasicAuthentication] authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated, IsSponsor] permission_classes = [IsAuthenticated, IsSponsor]
serializer_class = RoleSerializerUi serializer_class = RoleSerializerUi
def patch(self, request, id): def partial_update(self, request, pk):
role = Role.objects.get(id=id) role = Role.objects.get(pk=pk)
sponsor = GregUserProfile.objects.get(user=self.request.user).sponsor
with transaction.atomic(): with transaction.atomic():
serializer = self.serializer_class( serializer = self.serializer_class(
instance=role, data=request.data, partial=True instance=role,
data=request.data,
partial=True,
context={"sponsor": sponsor},
) )
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
instance = serializer.update(role, request.data) instance = serializer.update(role, serializer.validated_data)
instance.save() instance.save()
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
def create(self, request):
sponsor = GregUserProfile.objects.get(user=self.request.user).sponsor
with transaction.atomic():
serializer = self.serializer_class(
data=request.data,
context={
"sponsor": sponsor,
},
)
serializer.is_valid(raise_exception=True)
serializer.save(sponsor=sponsor)
return Response(status=status.HTTP_201_CREATED)
...@@ -68,16 +68,16 @@ class UserInfoView(APIView): ...@@ -68,16 +68,16 @@ class UserInfoView(APIView):
{ {
"roles": [ "roles": [
{ {
"ou_name_nb": role.orgunit_id.name_nb, "ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit_id.name_en, "ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb, "role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en, "role_name_en": role.type.name_en,
"start": role.start_date, "start": role.start_date,
"end": role.end_date, "end": role.end_date,
"comments": role.comments, "comments": role.comments,
"sponsor": { "sponsor": {
"first_name": role.sponsor_id.first_name, "first_name": role.sponsor.first_name,
"last_name": role.sponsor_id.last_name, "last_name": role.sponsor.last_name,
}, },
} }
for role in roles.all() for role in roles.all()
...@@ -105,16 +105,16 @@ class UserInfoView(APIView): ...@@ -105,16 +105,16 @@ class UserInfoView(APIView):
"passport": passports and passports.value, "passport": passports and passports.value,
"roles": [ "roles": [
{ {
"ou_name_nb": role.orgunit_id.name_nb, "ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit_id.name_en, "ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb, "role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en, "role_name_en": role.type.name_en,
"start": role.start_date, "start": role.start_date,
"end": role.end_date, "end": role.end_date,
"comments": role.comments, "comments": role.comments,
"sponsor": { "sponsor": {
"first_name": role.sponsor_id.first_name, "first_name": role.sponsor.first_name,
"last_name": role.sponsor_id.last_name, "last_name": role.sponsor.last_name,
}, },
} }
for role in person.roles.all() for role in person.roles.all()
......
import datetime
import pytest import pytest
from rest_framework import status from rest_framework import status
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
...@@ -14,9 +15,13 @@ def test_invite_guest(client, user_sponsor, unit_foo, role_type_foo): ...@@ -14,9 +15,13 @@ def test_invite_guest(client, user_sponsor, unit_foo, role_type_foo):
"last_name": "Bar", "last_name": "Bar",
"email": "test@example.com", "email": "test@example.com",
"role": { "role": {
"start_date": "2019-08-06", "start_date": (
"end_date": "2019-08-10", datetime.datetime.today() + datetime.timedelta(days=1)
"orgunit_id": unit_foo.id, ).strftime("%Y-%m-%d"),
"end_date": (
datetime.datetime.today() + datetime.timedelta(days=10)
).strftime("%Y-%m-%d"),
"orgunit": unit_foo.id,
"type": role_type_foo.id, "type": role_type_foo.id,
}, },
} }
......
...@@ -190,8 +190,8 @@ def person(person_foo_data) -> Person: ...@@ -190,8 +190,8 @@ def person(person_foo_data) -> Person:
def role(person, sponsor_guy, unit_foo, role_type_foo) -> Role: def role(person, sponsor_guy, unit_foo, role_type_foo) -> Role:
role = Role.objects.create( role = Role.objects.create(
person=person, person=person,
sponsor_id=sponsor_guy, sponsor=sponsor_guy,
orgunit_id=unit_foo, orgunit=unit_foo,
end_date="2050-10-15", end_date="2050-10-15",
type_id=role_type_foo.id, type_id=role_type_foo.id,
) )
......
...@@ -5,7 +5,6 @@ from django.urls import path ...@@ -5,7 +5,6 @@ from django.urls import path
from django.urls.resolvers import URLResolver from django.urls.resolvers import URLResolver
from gregui.api import urls as api_urls from gregui.api import urls as api_urls
from gregui.api.views.role import RoleInfoView
from gregui.api.views.userinfo import UserInfoView from gregui.api.views.userinfo import UserInfoView
from gregui.views import OusView, GuestInfoView from gregui.views import OusView, GuestInfoView
from . import views from . import views
...@@ -23,5 +22,4 @@ urlpatterns: List[URLResolver] = [ ...@@ -23,5 +22,4 @@ urlpatterns: List[URLResolver] = [
path("api/ui/v1/userinfo/", UserInfoView.as_view()), # type: ignore path("api/ui/v1/userinfo/", UserInfoView.as_view()), # type: ignore
path("api/ui/v1/ous/", OusView.as_view()), path("api/ui/v1/ous/", OusView.as_view()),
path("api/ui/v1/guests/", GuestInfoView.as_view()), path("api/ui/v1/guests/", GuestInfoView.as_view()),
path("api/ui/v1/role/<int:id>", RoleInfoView.as_view()),
] ]
...@@ -113,8 +113,8 @@ class GuestInfoView(APIView): ...@@ -113,8 +113,8 @@ class GuestInfoView(APIView):
"id": role.id, "id": role.id,
"name_nb": role.type.name_nb, "name_nb": role.type.name_nb,
"name_en": role.type.name_en, "name_en": role.type.name_en,
"ou_nb": role.orgunit_id.name_nb, "ou_nb": role.orgunit.name_nb,
"ou_en": role.orgunit_id.name_en, "ou_en": role.orgunit.name_en,
"start_date": role.start_date, "start_date": role.start_date,
"end_date": role.end_date, "end_date": role.end_date,
"max_days": role.type.max_days, "max_days": role.type.max_days,
...@@ -123,7 +123,7 @@ class GuestInfoView(APIView): ...@@ -123,7 +123,7 @@ class GuestInfoView(APIView):
], ],
} }
for person in Person.objects.filter( for person in Person.objects.filter(
roles__sponsor_id=user.sponsor roles__sponsor=user.sponsor
).distinct() ).distinct()
] ]
} }
......