From bb3b54d4e15b0642e40aecdc1cf6bc8397a4ff83 Mon Sep 17 00:00:00 2001 From: Oskari Tiala Date: Thu, 23 Jun 2022 14:55:01 +0300 Subject: [PATCH] Changes for admin panel --- events/api.py | 252 +++++++++++++++++++++++++++++++++++++++++------ helevents/api.py | 4 +- 2 files changed, 226 insertions(+), 30 deletions(-) diff --git a/events/api.py b/events/api.py index 75d4f95c1..44d5ec110 100644 --- a/events/api.py +++ b/events/api.py @@ -37,7 +37,8 @@ from rest_framework.views import get_view_name as original_get_view_name from rest_framework.routers import APIRootView from rest_framework.fields import DateTimeField - +from django.db.models import Prefetch, Q, QuerySet +from django.contrib.auth.models import AnonymousUser # 3rd party from isodate import Duration, duration_isoformat, parse_duration @@ -68,6 +69,7 @@ ) from events.translation import EventTranslationOptions, ImageTranslationOptions from helevents.models import User +from helevents.api import UserSerializer from events.renderers import DOCXRenderer from events.signals import post_save, post_update @@ -630,25 +632,9 @@ def validate_publisher(self, value): " must be left blank or set to %(required)s or any other organization" " the user belongs to.") % {'given': str(value), - 'required': str(self.publisher - if not self.publisher.replaced_by - else self.publisher.replaced_by)}}) - if value.replaced_by: - # for replaced organizations, we automatically update to the current organization - # even if the POST uses the old id - return value.replaced_by - return value - - def validate(self, data): - if 'name' in self.translated_fields: - name_exists = False - languages = [x[0] for x in settings.LANGUAGES] - for language in languages: - # null or empty strings are not allowed, they are the same as missing name! - if 'name_%s' % language in data and data['name_%s' % language]: - name_exists = True - break - else: + 'required': str(self.publisher + if not self.publisher.replaced_by + else self.publisher.replaced_by)}}) # null or empty strings are not allowed, they are the same as missing name! name_exists = 'name' in data and data['name'] if not name_exists: @@ -1162,9 +1148,109 @@ class LanguageViewSet(JSONAPIViewMixin, viewsets.ReadOnlyModelViewSet): LOCAL_TZ = pytz.timezone(settings.TIME_ZONE) -class OrganizationSerializer(LinkedEventsSerializer): +class OrganizationBaseSerializer(LinkedEventsSerializer): view_name = 'organization-detail' + class Meta: + model = Organization + fields = '__all__' + + +class OrganizationCreateSerializer(OrganizationBaseSerializer): + + def validate(self, data): + if Organization.objects.filter(id=str(self.request.data['id'])): + raise DRFPermissionDenied(f"Organization with id {self.request.data['id']} already exists.") + + if 'parent' in data.keys(): + user = self.request.user + if user and user.get_default_organization(): + if user.get_default_organization().id == self.request.data['parent']: + pass + else: + raise DRFPermissionDenied('User has no rights to this organization') + else: + raise DRFPermissionDenied('User must be logged in to create organizations.') + data = super().validate(data) + return data + + def connected_organizations(self, org_type, org): + internal_types = {'sub_organizations': Organization.NORMAL, + 'affiliated_organizations': Organization.AFFILIATED} + if org_type in self.request.data.keys(): + for conn_org_id in self.request.data[org_type]: + conn_org = Organization.objects.filter(id=str(conn_org_id), internal_type=internal_types[org_type]) + if conn_org: + org.children.add(conn_org[0]) + + def create(self, validated_data): + org = super().create(validated_data) + self.connected_organizations('sub_organizations', org) + self.connected_organizations('affiliated_organizations', org) + return org + + class Meta: + model = Organization + fields = '__all__' + +class OrganizationUpdateSerializer(OrganizationBaseSerializer): + # admin_users = UserSerializer(many=True) + # regular_users = UserSerializer(many=True) + + class Meta: + model = Organization + fields = [ + # Organization + 'internal_type', + 'classification', + 'name', + 'founding_date', + 'dissolution_date', + 'parent', + 'admin_users', + 'private_users', + 'regular_users', + 'created_by', + 'last_modified_by', + 'replaced_by', + # DataModel + 'last_modified_time', + ] + + def validate(self, data): + if 'parent' in data.keys(): + user = self.request.user + if user and user.get_default_organization(): + if user.get_default_organization().id == self.request.data['parent']: + pass + else: + raise DRFPermissionDenied('User has no rights to this organization') + else: + raise DRFPermissionDenied('User must be logged in to create organizations.') + data = super().validate(data) + return data + + def connected_organizations(self, org_type, org): + internal_types = {'sub_organizations': Organization.NORMAL, + 'affiliated_organizations': Organization.AFFILIATED} + if org_type in self.request.data.keys(): + for conn_org_id in self.request.data[org_type]: + conn_org = Organization.objects.filter(id=str(conn_org_id), internal_type=internal_types[org_type]) + if conn_org: + org.children.add(conn_org[0]) + + def update(self, instance, validated_data): + # admin_users = validated_data.pop('admin_users', None) + # regular_users = validated_data.pop('regular_users', None) + print(validated_data) + print(instance.__dict__) + org = super().update(instance, validated_data) + # self.connected_organizations('sub_organizations', org) + # self.connected_organizations('affiliated_organizations', org) + return org + + +class OrganizationListSerializer(OrganizationBaseSerializer): parent_organization = serializers.HyperlinkedRelatedField( queryset=Organization.objects.all(), source='parent', @@ -1186,6 +1272,90 @@ class OrganizationSerializer(LinkedEventsSerializer): ) is_affiliated = serializers.SerializerMethodField() has_regular_users = serializers.SerializerMethodField() + created_time = DateTimeField(default_timezone=pytz.UTC, required=False, allow_null=True) + last_modified_time = DateTimeField(default_timezone=pytz.UTC, required=False, allow_null=True) + + class Meta: + model = Organization + fields = ( + 'id', 'data_source', 'origin_id', + 'classification', 'name', 'founding_date', + 'dissolution_date', + 'parent_organization', + 'sub_organizations', + 'affiliated_organizations', + 'created_time', 'last_modified_time', 'created_by', + 'last_modified_by', 'replaced_by', + 'has_regular_users', + 'is_affiliated', + ) + + def get_is_affiliated(self, obj): + return obj.internal_type == Organization.AFFILIATED + + def get_has_regular_users(self, obj): + return obj.regular_users.count() > 0 + + +class OrganizationDetailSerializer(OrganizationListSerializer): + regular_users = serializers.SerializerMethodField() + admin_users = serializers.SerializerMethodField() + + def get_regular_users(self, obj): + user = self.context['user'] + if not isinstance(user, AnonymousUser) and user.is_admin(obj): + return UserSerializer(obj.regular_users.all(), read_only=True, many=True).data + else: + return '' + + def get_admin_users(self, obj): + user = self.context['user'] + if not isinstance(user, AnonymousUser) and user.is_admin(obj): + return UserSerializer(obj.admin_users.all(), read_only=True, many=True).data + else: + return '' + + class Meta: + model = Organization + fields = ( + 'id', 'data_source', 'origin_id', + 'classification', 'name', 'founding_date', + 'dissolution_date', 'parent_organization', + 'sub_organizations', 'affiliated_organizations', + 'created_time', 'last_modified_time', 'created_by', + 'last_modified_by', 'is_affiliated', 'replaced_by', + 'has_regular_users', 'regular_users', 'admin_users' + ) + +class OrganizationSerializer(LinkedEventsSerializer): + view_name = 'organization-detail' + + parent_organization = serializers.HyperlinkedRelatedField( + queryset=Organization.objects.all(), + source='parent', + view_name='organization-detail', + required=False, + allow_null=True, + ) + sub_organizations = serializers.HyperlinkedRelatedField( + # queryset=Organization.objects.all(), + view_name='organization-detail', + many=True, + read_only=True, + ) + affiliated_organizations = serializers.HyperlinkedRelatedField( + queryset=Organization.objects.all(), + view_name='organization-detail', + many=True, + ) + replaced_by = serializers.HyperlinkedRelatedField( + queryset=Organization.objects.all(), + view_name='organization-detail', + required=False, + allow_null=True, + ) + is_affiliated = serializers.SerializerMethodField() + has_regular_users = serializers.SerializerMethodField() created_time = DateTimeField( default_timezone=pytz.UTC, required=False, allow_null=True) last_modified_time = DateTimeField( @@ -1202,6 +1372,7 @@ class Meta: 'last_modified_by', 'is_affiliated', 'replaced_by', 'has_regular_users' ) + read_only_fields = ['origin_id'] def get_is_affiliated(self, obj): return obj.internal_type == Organization.AFFILIATED @@ -1210,21 +1381,46 @@ def get_has_regular_users(self, obj): return obj.regular_users.count() > 0 -class OrganizationViewSet(JSONAPIViewMixin, viewsets.ReadOnlyModelViewSet): +class OrganizationViewSet(JSONAPIViewMixin, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + mixins.ListModelMixin, + mixins.CreateModelMixin, + viewsets.GenericViewSet): queryset = Organization.objects.all() - serializer_class = OrganizationSerializer - def get_queryset(self): - queryset = Organization.objects.all() + def get_serializer_class(self, *args, **kwargs): + if self.action == 'retrieve': + return OrganizationDetailSerializer + elif self.action == 'create': + return OrganizationCreateSerializer + elif self.action == 'update': + return OrganizationUpdateSerializer + else: + return OrganizationListSerializer + def get_queryset(self): + queryset = Organization.objects.prefetch_related('regular_users', 'admin_users')\ + .prefetch_related(Prefetch('children', + queryset=Organization.objects.filter(internal_type='normal'), # noqa E501 + to_attr='sub_organizations'), + Prefetch('children', + queryset=Organization.objects.filter(internal_type='affiliated'), # noqa E501 + to_attr='affiliated_organizations')) id = self.request.query_params.get('child', None) if id: - queryset = queryset.get(id=id).get_ancestors() + try: + queryset = queryset.get(id=id).get_ancestors() + except Organization.DoesNotExist: + queryset = queryset.none() id = self.request.query_params.get('parent', None) if id: - queryset = queryset.get(id=id).get_descendants() - + try: + queryset = queryset.get(id=id).get_descendants() + except Organization.DoesNotExist: + queryset = queryset.none() return queryset diff --git a/helevents/api.py b/helevents/api.py index abb3309ff..11a108f0f 100644 --- a/helevents/api.py +++ b/helevents/api.py @@ -27,14 +27,14 @@ def to_representation(self, obj): class Meta: fields = [ - 'last_login', 'username', 'email', 'date_joined', + 'pk', 'last_login', 'username', 'email', 'date_joined', 'first_name', 'last_name', 'uuid', 'department_name', 'is_staff', 'display_name', ] model = get_user_model() -class UserViewSet(viewsets.ReadOnlyModelViewSet): +class UserViewSet(viewsets.ModelViewSet): def get_queryset(self): user = self.request.user