You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
15 KiB
Python
310 lines
15 KiB
Python
from django.utils.translation import gettext_lazy as _
|
|
|
|
# from rest_framework import viewsets
|
|
from rest_framework import status
|
|
from rest_framework.decorators import action, api_view
|
|
from rest_framework.exceptions import PermissionDenied, ValidationError
|
|
from rest_framework.generics import get_object_or_404
|
|
from rest_framework.response import Response
|
|
from rest_framework.viewsets import ModelViewSet
|
|
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
|
|
from .models import Study, StudyRole, StudyRoleNames
|
|
from .serializers import ContributorSerializer, ContributorUpdateSerializer, InviteSerializer, StudyRoleSerializer, StudySerializer, StudyDetailSerializer
|
|
|
|
from apps.researcher.models import Researcher
|
|
from apps.university.serializers import StudyFieldSerializer
|
|
|
|
|
|
class Studies(ModelViewSet):
|
|
"""
|
|
API endpoint for creating/reading/updating/deleting studies.
|
|
"""
|
|
|
|
queryset = Study.objects.all().order_by('name')
|
|
serializer_class = StudyDetailSerializer
|
|
http_method_names = ['get', 'post', 'put', 'delete']
|
|
|
|
serializer_class_by_action = {
|
|
'create': StudySerializer,
|
|
'update': StudySerializer,
|
|
}
|
|
|
|
def get_serializer_class(self):
|
|
|
|
if hasattr(self, 'serializer_class_by_action'):
|
|
return self.serializer_class_by_action.get(self.action, self.serializer_class)
|
|
|
|
return super().get_serializer_class()
|
|
|
|
def get_queryset(self):
|
|
|
|
if getattr(self, 'swagger_fake_view', False):
|
|
return self.queryset
|
|
|
|
return Study.objects.filter(contributors__in=[self.request.user.researcher]).order_by('name')
|
|
|
|
def _valid_study_field_check(self, serializer):
|
|
# Check if the study field is correct based on the researcher that is creating this study
|
|
if serializer.validated_data.get('field').faculty != self.request.user.researcher.faculty:
|
|
raise ValidationError(_('Study field %(study_field_name)s is not valid for faculty %(faculty_name)s') % {'study_field_name': serializer.validated_data['field'], 'faculty_name': self.request.user.researcher.faculty})
|
|
|
|
def _valid_contributors_check(self, serializer):
|
|
contributors = serializer.validated_data.get('contributors', [])
|
|
if len(contributors) == 0:
|
|
# There are zero contributors, so add the current logged in user as the first one with admin rights
|
|
contributors.append({'researcher': self.request.user.researcher, 'role': StudyRoleNames.ADMIN, 'active': True})
|
|
|
|
else:
|
|
# Check if the current logged in user is already part of this study
|
|
creator_in_contributors = any([contributor['researcher'] == self.request.user.researcher for contributor in contributors])
|
|
if not creator_in_contributors:
|
|
# Add the current logged in user as an admin to the study
|
|
contributors.append({'researcher': self.request.user.researcher, 'role': StudyRoleNames.ADMIN, 'active': True})
|
|
|
|
# Get a list of the researchers working on the same faculty
|
|
valid_researchers = list(Researcher.objects.filter(faculty=self.request.user.researcher.faculty).values_list('pk', flat=True))
|
|
# Filter the list based on the logged in researcher his faculty
|
|
contributors = [contributor for contributor in contributors if contributor['researcher'].id in valid_researchers]
|
|
|
|
return contributors
|
|
|
|
def perform_create(self, serializer):
|
|
# Check if there are already extra contributors added
|
|
contributors = self._valid_contributors_check(serializer)
|
|
|
|
# Check if the study field is correct based on the researcher that is creating this study
|
|
self._valid_study_field_check(serializer)
|
|
|
|
serializer.save(owner=self.request.user.researcher, contributors=contributors)
|
|
|
|
def perform_update(self, serializer):
|
|
# Check if there are already extra contributors added
|
|
# TODO: Is this needed? As with an update, we cannot change the contributors...
|
|
# self._valid_contributors_check(serializer)
|
|
|
|
# Check if the study field is correct based on the researcher that is creating this study
|
|
self._valid_study_field_check(serializer)
|
|
|
|
serializer.save()
|
|
|
|
@swagger_auto_schema(request_body=StudySerializer(many=False), responses={200: StudyDetailSerializer(many=False)})
|
|
def create(self, request, *args, **kwargs):
|
|
study_response = super().create(request, *args, **kwargs)
|
|
# Get the created study and reload it for different serializer
|
|
study = get_object_or_404(Study, pk=study_response.data['id'], contributors__in=[self.request.user.researcher])
|
|
return Response(StudyDetailSerializer(study).data, status=study_response.status_code)
|
|
|
|
@swagger_auto_schema(request_body=StudySerializer(many=False), responses={200: StudyDetailSerializer(many=False)})
|
|
def update(self, request, *args, **kwargs):
|
|
study_response = super().update(request, *args, **kwargs)
|
|
# Get the created study and reload it for different serializer
|
|
study = get_object_or_404(Study, pk=study_response.data['id'], contributors__in=[self.request.user.researcher])
|
|
return Response(StudyDetailSerializer(study).data, status=study_response.status_code)
|
|
|
|
# TODO: Figure out how to add the pagination to the schema output...
|
|
@swagger_auto_schema(responses={200: StudyRoleSerializer(many=True)})
|
|
@action(detail=False, methods=['get'])
|
|
def roles(self, request):
|
|
"""Get all the availabe researcher roles for a study.
|
|
|
|
Args:
|
|
request ([type]): The incoming web request
|
|
|
|
Returns:
|
|
StudyRoleSerializer: A list of zero or more study roles
|
|
"""
|
|
study_roles = []
|
|
for role in StudyRoleNames.choices:
|
|
study_roles.append({'id': role[0], 'name': role[1]})
|
|
|
|
page = self.paginate_queryset(study_roles)
|
|
if page is not None:
|
|
serializer = StudyRoleSerializer(page, many=True)
|
|
return self.get_paginated_response(serializer.data)
|
|
|
|
serializer = StudyRoleSerializer(study_roles, many=True)
|
|
return Response(serializer.data)
|
|
|
|
# TODO: Figure out how to add the pagination to the schema output...
|
|
@swagger_auto_schema(responses={200: StudyFieldSerializer(many=True)})
|
|
@action(detail=False, methods=['get'])
|
|
def fields(self, request):
|
|
"""Get the logged in researcher his study fields based on the faculty where he belongs to. In other words, this is the list of study fields where the logged in user can do research on.
|
|
|
|
Args:
|
|
request ([type]): The incoming web request
|
|
|
|
Returns:
|
|
StudyFieldSerializer: A list with zero or more study fields for the logged in researcher.
|
|
"""
|
|
study_fields = request.user.researcher.faculty.studyfield_set.all().order_by('name')
|
|
|
|
page = self.paginate_queryset(study_fields)
|
|
if page is not None:
|
|
serializer = StudyFieldSerializer(page, many=True)
|
|
return self.get_paginated_response(serializer.data)
|
|
|
|
serializer = StudyFieldSerializer(study_fields, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@swagger_auto_schema(responses={200: StudyDetailSerializer(many=True)})
|
|
@action(detail=False, methods=['get'])
|
|
def invited(self, request):
|
|
"""Show a list of studies where the researcher is invited to.
|
|
|
|
Args:
|
|
request ([type]): The incoming web request
|
|
|
|
Returns:
|
|
StudyFieldSerializer: A list with zero or more study fields for the logged in researcher which he is invited to.
|
|
"""
|
|
studies = Study.objects.filter(contributors__in=[request.user.researcher], studyrole__active=False, studyrole__invited_at__isnull=False)
|
|
|
|
request.user.researcher.faculty.studyfield_set.all().order_by('name')
|
|
|
|
page = self.paginate_queryset(studies)
|
|
if page is not None:
|
|
serializer = StudyDetailSerializer(page, many=True)
|
|
return self.get_paginated_response(serializer.data)
|
|
|
|
serializer = StudyDetailSerializer(studies, many=True)
|
|
return Response(serializer.data)
|
|
|
|
|
|
class Contributors(ModelViewSet):
|
|
http_method_names = ['get', 'put', 'post', 'delete']
|
|
queryset = StudyRole.objects.all()
|
|
serializer_class = ContributorSerializer
|
|
|
|
@swagger_auto_schema(responses={200: ContributorSerializer(many=True)})
|
|
def list(self, request, *args, **kwargs):
|
|
"""
|
|
Get all the contributors that are assigned to a study of which you are also a member of.
|
|
"""
|
|
|
|
study = get_object_or_404(Study, pk=kwargs['study_id'], contributors__in=[self.request.user.researcher])
|
|
contributors = StudyRole.objects.filter(study=study).order_by('researcher__last_name')
|
|
# Add manual pagination.
|
|
page = self.paginate_queryset(contributors)
|
|
if page is not None:
|
|
serializer = ContributorSerializer(page, many=True)
|
|
return self.get_paginated_response(serializer.data)
|
|
|
|
serializer = ContributorSerializer(contributors, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@swagger_auto_schema(responses={200: ContributorSerializer(many=False)})
|
|
def get(self, request, *args, **kwargs):
|
|
"""
|
|
Get a single contributors that is assigned to a study for a study of which you are also a member of.
|
|
"""
|
|
|
|
contributor = get_object_or_404(StudyRole, pk=kwargs['contributor_id'], study=kwargs['study_id'], study__contributors__in=[self.request.user.researcher])
|
|
serializer = ContributorSerializer(contributor, many=False)
|
|
return Response(serializer.data)
|
|
|
|
@swagger_auto_schema(request_body=ContributorUpdateSerializer(many=False), responses={200: ContributorSerializer(many=False)})
|
|
def update(self, request, *args, **kwargs):
|
|
"""Update an existing contributor role. This can only be done by study admins.
|
|
|
|
Raises:
|
|
Http404 : When the combination contributor and study does not exists, or you are not a member of this study
|
|
PermissionDenied: If you are not an admin of this study.
|
|
ValidationError: If you change the owner rights or when you delete all admins
|
|
|
|
Returns:
|
|
ContributorSerializer: Serialized data of the new contributor role
|
|
"""
|
|
|
|
# Check if combination of study and contributor does exists and the logged in user is part of the study
|
|
contributor = get_object_or_404(StudyRole, pk=kwargs['contributor_id'], study=kwargs['study_id'], study__contributors__in=[self.request.user.researcher])
|
|
|
|
# Check if logged in user is an admin for this study
|
|
if not (contributor.study.owner == request.user.researcher or contributor.study.is_an_admin(request.user.researcher)):
|
|
raise PermissionDenied(_('You are not allowed to update this contributor.'))
|
|
|
|
# We cannot change the study rights for an owner
|
|
if contributor.researcher == contributor.study.owner:
|
|
raise ValidationError(_('The rights of an owner of the study cannot be changed or deleted.'))
|
|
|
|
# Load serializer
|
|
serializer = self.get_serializer(contributor, data=request.data, partial=False)
|
|
|
|
# Check if data is valid
|
|
serializer.is_valid(raise_exception=True)
|
|
if contributor.role != serializer.validated_data['role'] and contributor.role == StudyRoleNames.ADMIN:
|
|
# Make sure at least 1 admin is left
|
|
if contributor.study.contributors.filter(studyrole__role=StudyRoleNames.ADMIN).exclude(studyrole__researcher=contributor.researcher).count() == 0:
|
|
raise ValidationError(_('At least 1 administrator needs to remain for this study.'))
|
|
|
|
# Update the role
|
|
contributor.role = serializer.validated_data['role']
|
|
|
|
# Save to database
|
|
contributor.save()
|
|
|
|
serializer = ContributorSerializer(contributor, many=False)
|
|
return Response(serializer.data)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
# Check if combination of study and contributor does exists and the logged in user is part of the study
|
|
contributor = get_object_or_404(StudyRole, pk=kwargs['contributor_id'], study=kwargs['study_id'], study__contributors__in=[self.request.user.researcher])
|
|
|
|
# Check if logged in user is an admin for this study
|
|
if not (contributor.study.owner == request.user.researcher or contributor.study.is_an_admin(request.user.researcher)):
|
|
raise PermissionDenied(_('You are not allowed to delete this contributor'))
|
|
|
|
# We cannot change the study rights for an owner
|
|
if contributor.researcher == contributor.study.owner:
|
|
raise ValidationError(_('The rights of an owner of the study cannot be removed'))
|
|
|
|
if contributor.role == StudyRoleNames.ADMIN:
|
|
# Make sure at least 1 admin is left
|
|
if contributor.study.contributors.filter(studyrole__role=StudyRoleNames.ADMIN).exclude(studyrole__researcher=contributor.researcher).count() == 0:
|
|
raise ValidationError(_('At least 1 administrator needs to remain for this study'))
|
|
|
|
# Send an email to the researcher that is removed from the study
|
|
contributor.send_remove_notification_email(sender=self.request.user.researcher)
|
|
|
|
self.perform_destroy(contributor)
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
|
|
|
|
@swagger_auto_schema(methods=['post'], request_body=InviteSerializer(many=False))
|
|
@api_view(['POST'])
|
|
def process_study_invite(request, *args, **kwargs):
|
|
# TODO: How is allowed to make an invitation? Owner or any Admin? For now, an owner and any admin can send invitations
|
|
|
|
# Check if we are a contributor of the project and does project exists
|
|
study = get_object_or_404(Study, pk=kwargs['study_id'], contributors__in=[request.user.researcher], studyrole__active=True)
|
|
|
|
# Check if logged in user is an admin for this study
|
|
if not (study.owner == request.user.researcher or study.is_an_admin(request.user.researcher)):
|
|
raise PermissionDenied(_('You are not allowed to invite a new researcher'))
|
|
|
|
invite = InviteSerializer(data=request.data)
|
|
invite.is_valid(raise_exception=True)
|
|
contributor = invite.save(study=study)
|
|
contributor.sent_invitation_email(sender=request.user.researcher)
|
|
|
|
return Response({'message': _('Invitation to %(researcher_name)s for study %(study_name)s is sent.') % {'researcher_name': contributor.researcher.display_name, 'study_name': study.name}})
|
|
|
|
|
|
@swagger_auto_schema(method='get', auto_schema=None)
|
|
@api_view(['GET'])
|
|
def validate_study_invite(request, *args, **kwargs):
|
|
# Check if we are a contributor of the project and does project exists. And make sure the user is not yet activated
|
|
study = get_object_or_404(Study, pk=kwargs['pk'], contributors__in=[request.user.researcher], studyrole__active=False)
|
|
|
|
# Check if we are invited for this study
|
|
invitation = get_object_or_404(StudyRole, study=study, researcher=request.user.researcher, active=False)
|
|
|
|
if not invitation.check_invitation_link(request.user.researcher, kwargs['jwt_token']):
|
|
raise ValidationError(_('Could not validate the invitation to study %(study_name)s') % {'study_name': study.name})
|
|
|
|
invitation.active = True
|
|
invitation.save()
|
|
return Response({'message': _('Invitation for %(researcher_name)s for study %(study_name)s is accepted.') % {'researcher_name': invitation.researcher.display_name, 'study_name': study.name}})
|