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(
return Role.objects.create(
person=person,
type=role_type_bar,
orgunit_id=unit_foo,
orgunit=unit_foo,
start_date="2020-03-05",
end_date=datetime.today() + timedelta(days=30),
contact_person_unit="Contact Person",
available_in_search=True,
sponsor_id=sponsor_guy,
sponsor=sponsor_guy,
)
......
......@@ -73,8 +73,8 @@ def test_role_add_notification(
type=role_type_foo,
start_date="2021-05-06",
end_date="2021-10-20",
orgunit_id=org_unit_bar,
sponsor_id=sponsor,
orgunit=org_unit_bar,
sponsor=sponsor,
)
notifications = Notification.objects.filter(object_type="Role")
assert len(notifications) == 1
......@@ -97,8 +97,8 @@ def test_role_update_notification(
type=role_type_foo,
start_date="2021-05-06",
end_date="2021-10-20",
orgunit_id=org_unit_bar,
sponsor_id=sponsor,
orgunit=org_unit_bar,
sponsor=sponsor,
)
assert len(person.roles.all()) == 1
person_role = person.roles.all()[0]
......@@ -125,8 +125,8 @@ def test_role_delete_notification(
type=role_type_foo,
start_date="2021-05-06",
end_date="2021-10-20",
orgunit_id=org_unit_bar,
sponsor_id=sponsor,
orgunit=org_unit_bar,
sponsor=sponsor,
)
assert len(person.roles.all()) == 1
person_role = person.roles.all()[0]
......
......@@ -5,13 +5,13 @@ from django.utils import timezone
from rest_framework import serializers
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
class InviteGuestSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
role = RoleSerializerUi(required=True)
role = InviteRoleSerializerUi(required=True)
uuid = serializers.UUIDField(read_only=True)
def create(self, validated_data):
......@@ -30,7 +30,7 @@ class InviteGuestSerializer(serializers.ModelSerializer):
source="greg",
)
role_data["person"] = person
role_data["sponsor_id"] = user.sponsor
role_data["sponsor"] = user.sponsor
role = Role.objects.create(**role_data)
invitation = Invitation.objects.create(role=role)
InvitationLink.objects.create(
......
import datetime
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.validators import UniqueTogetherValidator
from greg.models import Role
class RoleSerializerUi(serializers.ModelSerializer):
def update(self, instance, validated_data):
# validate new date is after old date, and old date has not passed
"""Serializer for the Role model with validation of various fields"""
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()
new_start = validated_data.get("start_date")
new_end = validated_data.get("end_date")
if new_start:
new_start = datetime.datetime.strptime(
validated_data["start_date"], "%Y-%m-%d"
).date()
if new_start < today:
raise serializers.ValidationError("Start date cannot be in the past")
if instance.start_date < today and new_start:
raise serializers.ValidationError(
"Role has started, cannot change start date"
)
instance.start_date = new_start
if new_end:
new_end = datetime.datetime.strptime(
validated_data["end_date"], "%Y-%m-%d"
).date()
if new_end < today:
raise serializers.ValidationError("End date cannot be in the past")
if instance.end_date < today and new_end:
# New start dates cannot be in the past
if start_date < today:
raise serializers.ValidationError("Start date cannot be in the past")
return start_date
def validate_end_date(self, end_date):
"""Ensure rules for end_date are followed"""
today = datetime.date.today()
if end_date < today:
raise serializers.ValidationError("End date cannot be in the past")
if self.instance and self.instance.end_date < today:
raise serializers.ValidationError("Role has ended, cannot change end date")
return end_date
def validate_orgunit(self, unit):
"""Enforce rules related to orgunit"""
sponsor = self.context["sponsor"]
units = sponsor.units.all()
# 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(
"Role has ended, cannot change end date"
"End date cannot be before start date."
)
max_days = today + datetime.timedelta(days=instance.type.max_days)
if new_end > max_days:
else:
if attrs.get("start_date") and attrs["end_date"] < attrs["start_date"]:
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:
model = Role
fields = [
"orgunit_id",
"orgunit",
"start_date",
"type",
"end_date",
......
......@@ -6,4 +6,4 @@ from greg.models import RoleType
class RoleTypeSerializerUi(ModelSerializer):
class Meta:
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 (
InvitedGuestView,
)
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.unit import UnitsViewSet
router = DefaultRouter(trailing_slash=False)
router.register(r"role", RoleInfoViewSet, basename="role")
urlpatterns = router.urls
urlpatterns += [
......
......@@ -30,7 +30,7 @@ class CreateInvitationView(CreateAPIView):
"last_name": "sss",
"date_of_birth": null,
"role": {
"orgunit_id": 1,
"orgunit": 1,
"start_date": null,
"type": 1,
"end_date": "2021-12-15",
......@@ -58,16 +58,15 @@ class CreateInvitationView(CreateAPIView):
"""
sponsor_user = GregUserProfile.objects.get(user=request.user)
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)
# TODO: check that sponsor has access to OU
person = serializer.save()
invitationlink = InvitationLink.objects.filter(
invitation__role__person=person.id,
invitation__role__sponsor_id=sponsor_user.sponsor,
invitation__role__person_id=person.id,
invitation__role__sponsor_id=sponsor_user.sponsor_id,
)
# TODO: send email to invited guest
print(invitationlink)
......@@ -133,7 +132,7 @@ class InvitedGuestView(GenericAPIView):
invite_link = InvitationLink.objects.get(uuid=invite_id)
role = invite_link.invitation.role
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
session_type = (
......@@ -165,8 +164,8 @@ class InvitedGuestView(GenericAPIView):
"last_name": sponsor.last_name,
},
"role": {
"ou_name_nb": role.orgunit_id.name_nb,
"ou_name_en": role.orgunit_id.name_en,
"ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en,
"start": role.start_date,
......
......@@ -29,13 +29,16 @@ class PersonView(APIView):
"email": person.private_email and person.private_email.value,
"mobile": person.private_mobile and person.private_mobile.value,
"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": [
{
"id": role.id,
"name_nb": role.type.name_nb,
"name_en": role.type.name_en,
"ou_nb": role.orgunit_id.name_nb,
"ou_en": role.orgunit_id.name_en,
"ou_nb": role.orgunit.name_nb,
"ou_en": role.orgunit.name_en,
"start_date": role.start_date,
"end_date": role.end_date,
"max_days": role.type.max_days,
......
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.generics import GenericAPIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from greg.models import Role
from greg.permissions import IsSponsor
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()
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated, IsSponsor]
serializer_class = RoleSerializerUi
def patch(self, request, id):
role = Role.objects.get(id=id)
def partial_update(self, request, pk):
role = Role.objects.get(pk=pk)
sponsor = GregUserProfile.objects.get(user=self.request.user).sponsor
with transaction.atomic():
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)
instance = serializer.update(role, request.data)
instance = serializer.update(role, serializer.validated_data)
instance.save()
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):
{
"roles": [
{
"ou_name_nb": role.orgunit_id.name_nb,
"ou_name_en": role.orgunit_id.name_en,
"ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en,
"start": role.start_date,
"end": role.end_date,
"comments": role.comments,
"sponsor": {
"first_name": role.sponsor_id.first_name,
"last_name": role.sponsor_id.last_name,
"first_name": role.sponsor.first_name,
"last_name": role.sponsor.last_name,
},
}
for role in roles.all()
......@@ -105,16 +105,16 @@ class UserInfoView(APIView):
"passport": passports and passports.value,
"roles": [
{
"ou_name_nb": role.orgunit_id.name_nb,
"ou_name_en": role.orgunit_id.name_en,
"ou_name_nb": role.orgunit.name_nb,
"ou_name_en": role.orgunit.name_en,
"role_name_nb": role.type.name_nb,
"role_name_en": role.type.name_en,
"start": role.start_date,
"end": role.end_date,
"comments": role.comments,
"sponsor": {
"first_name": role.sponsor_id.first_name,
"last_name": role.sponsor_id.last_name,
"first_name": role.sponsor.first_name,
"last_name": role.sponsor.last_name,
},
}
for role in person.roles.all()
......
import datetime
import pytest
from rest_framework import status
from rest_framework.reverse import reverse
......@@ -14,9 +15,13 @@ def test_invite_guest(client, user_sponsor, unit_foo, role_type_foo):
"last_name": "Bar",
"email": "test@example.com",
"role": {
"start_date": "2019-08-06",
"end_date": "2019-08-10",
"orgunit_id": unit_foo.id,
"start_date": (
datetime.datetime.today() + datetime.timedelta(days=1)
).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,
},
}
......
......@@ -190,8 +190,8 @@ def person(person_foo_data) -> Person:
def role(person, sponsor_guy, unit_foo, role_type_foo) -> Role:
role = Role.objects.create(
person=person,
sponsor_id=sponsor_guy,
orgunit_id=unit_foo,
sponsor=sponsor_guy,
orgunit=unit_foo,
end_date="2050-10-15",
type_id=role_type_foo.id,
)
......
......@@ -5,7 +5,6 @@ from django.urls import path
from django.urls.resolvers import URLResolver
from gregui.api import urls as api_urls
from gregui.api.views.role import RoleInfoView
from gregui.api.views.userinfo import UserInfoView
from gregui.views import OusView, GuestInfoView
from . import views
......@@ -23,5 +22,4 @@ urlpatterns: List[URLResolver] = [
path("api/ui/v1/userinfo/", UserInfoView.as_view()), # type: ignore
path("api/ui/v1/ous/", OusView.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):
"id": role.id,
"name_nb": role.type.name_nb,
"name_en": role.type.name_en,
"ou_nb": role.orgunit_id.name_nb,
"ou_en": role.orgunit_id.name_en,
"ou_nb": role.orgunit.name_nb,
"ou_en": role.orgunit.name_en,
"start_date": role.start_date,
"end_date": role.end_date,
"max_days": role.type.max_days,
......@@ -123,7 +123,7 @@ class GuestInfoView(APIView):
],
}
for person in Person.objects.filter(
roles__sponsor_id=user.sponsor
roles__sponsor=user.sponsor
).distinct()
]
}
......