diff --git a/apps/analysis_framework/tests/test_apis.py b/apps/analysis_framework/tests/test_apis.py index 0d42d84bbf..8500cbb8dd 100644 --- a/apps/analysis_framework/tests/test_apis.py +++ b/apps/analysis_framework/tests/test_apis.py @@ -222,75 +222,6 @@ def test_create_analysis_framework(self): role=project.analysis_framework.get_or_create_owner_role(), ).first() is not None, "Membership Should be created" - def test_clone_analysis_framework_without_name(self): - analysis_framework = self.create(AnalysisFramework) - project = self.create( - Project, analysis_framework=analysis_framework, - role=self.admin_role - ) - - url = '/api/v1/clone-analysis-framework/{}/'.format( - analysis_framework.id - ) - data = { - 'project': project.id, - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_400(response) - assert 'title' in response.data['errors'] - - def test_clone_analysis_framework(self): - """This is relevant only to public frameworks""" - analysis_framework = self.create(AnalysisFramework, is_private=False) - project = self.create( - Project, analysis_framework=analysis_framework, - role=self.admin_role - ) - # Add self.user as member to analysis framework, to check if owner membership created or not - default_membership, _ = analysis_framework.add_member(self.user) - # Add owner user, but this should not be in the cloned framework - user = self.create(User) - owner_membership, _ = analysis_framework.add_member(user, analysis_framework.get_or_create_owner_role()) - - url = '/api/v1/clone-analysis-framework/{}/'.format( - analysis_framework.id - ) - cloned_title = 'Cloned AF' - data = { - 'project': project.id, - 'title': cloned_title, - 'description': 'New Description', - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_201(response) - - self.assertNotEqual(response.data['id'], analysis_framework.id) - self.assertEqual( - response.data['title'], - cloned_title) - - project = Project.objects.get(id=project.id) - new_af = project.analysis_framework - - self.assertNotEqual(new_af.id, analysis_framework.id) - self.assertEqual(project.analysis_framework.id, response.data['id']) - - # Check if description updated - assert new_af.description == data['description'], "Description should be updated" - assert new_af.title == data['title'], "Title should be updated" - - # Test permissions cloned - # Only the requester should be the owner of the new framework - assert new_af.members.all().count() == 1, "The cloned framework should have only one owner" - assert AnalysisFrameworkMembership.objects.filter( - framework=new_af, role=owner_membership.role, - member=self.user, - ).exists() - def test_create_private_framework_unauthorized(self): project = self.create(Project, role=self.admin_role) diff --git a/apps/analysis_framework/tests/test_roles_api.py b/apps/analysis_framework/tests/test_roles_api.py index ff10591962..0b5ecd4b99 100644 --- a/apps/analysis_framework/tests/test_roles_api.py +++ b/apps/analysis_framework/tests/test_roles_api.py @@ -41,66 +41,6 @@ def setUp(self): key='text-widget-002', ) - def test_get_private_roles(self): - url = '/api/v1/private-framework-roles/' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - - data = response.data - for role in data['results']: - assert role['is_private_role'] is True, "Must be a private role" - - def test_get_public_roles_all(self): - url = '/api/v1/public-framework-roles/' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - - data = response.data - for role in data['results']: - assert role['is_private_role'] is not True, "Must be a public role" - - assert any(x['is_default_role'] for x in data['results']), "A default role should be present" - - def test_get_public_roles_no_default(self): - url = '/api/v1/public-framework-roles/?is_default_role=false' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - - data = response.data - for role in data['results']: - assert role['is_private_role'] is not True, "Must be a public role" - - print([x['is_default_role'] for x in data['results']]) - assert not any(x['is_default_role'] for x in data['results']), "No default role should be present" - - def test_owner_role(self): - self.private_framework.add_member( - self.user, - self.private_framework.get_or_create_owner_role() - ) - self.public_framework.add_member( - self.user, - self.public_framework.get_or_create_owner_role() - ) - # CLONING THE FRAMEWORK - response = self._clone_framework_test(self.private_framework) - self.assert_403(response) - - response = self._clone_framework_test(self.public_framework) - self.assert_201(response) - - # EDITING THE FRAMEWORK OWNED - self._edit_framework_test(self.private_framework, self.user, 200) - self._edit_framework_test(self.public_framework, self.user, 200) - - # Can use the framework in other projects - - self._add_user_test(self.public_framework, self.user, 201) - self._add_user_test(self.private_framework, self.user, 201) - def test_patch_membership(self): self.private_framework.add_member( self.user, @@ -136,47 +76,6 @@ def test_get_membership(self): self.assert_200(resp) - def test_editor_role(self): - editor_user = self.create(User) - self.private_framework.add_member( - editor_user, - self.private_framework.get_or_create_editor_role() - ) - self.public_framework.add_member( - editor_user, - self.public_framework.get_or_create_editor_role() - ) - - # CLONING FRAMEWORK - response = self._clone_framework_test(self.private_framework, editor_user) - self.assert_403(response) - - # EDITING FRAMEWORK - self._edit_framework_test(self.private_framework, editor_user, status=200) - self._edit_framework_test(self.public_framework, editor_user, status=200) - - # ADDING USER and ASSIGNING ROLES - self._add_user_test(self.public_framework, editor_user, 403) - self._add_user_test(self.private_framework, editor_user, 403) - - def test_no_role(self): - normal_user = self.create(User) - # CLONING FRAMEWORK - # private framework - response = self._clone_framework_test(self.private_framework, normal_user) - self.assert_403(response) - # public framework - response = self._clone_framework_test(self.public_framework, normal_user) - self.assert_201(response) - - # EDITING FRAMEWORK - self._edit_framework_test(self.public_framework, normal_user, status=403) - self._edit_framework_test(self.private_framework, normal_user, status=404) - - # ADDING USER and ASSIGNING ROLES - self._add_user_test(self.public_framework, normal_user, 403) - self._add_user_test(self.private_framework, normal_user, 404) - def test_add_user_with_public_role_to_private_framework(self): private_framework = self.create(AnalysisFramework, is_private=True) public_framework = self.create(AnalysisFramework, is_private=False) @@ -286,12 +185,6 @@ def _edit_framework_test(self, framework, user=None, status=200): response = self.client.put(url, edit_data) self.assertEqual(response.status_code, status) - def _clone_framework_test(self, framework, user=None): - clone_url = f'/api/v1/clone-analysis-framework/{framework.id}/' - self.authenticate(user) - data = {'title': 'Cloned'} - return self.client.post(clone_url, data=data) - def _add_user_test(self, framework, user, status=201, role=None): add_user_url = '/api/v1/framework-memberships/' role = (role and role.id) or framework.get_or_create_editor_role().id, diff --git a/apps/analysis_framework/views.py b/apps/analysis_framework/views.py index 080ee1113a..0dc0225a39 100644 --- a/apps/analysis_framework/views.py +++ b/apps/analysis_framework/views.py @@ -3,31 +3,26 @@ import django_filters from django.db import models from rest_framework import ( - exceptions, permissions, response, status, filters, - views, viewsets, ) from rest_framework.decorators import action from deep.permissions import ModifyPermission from deep.paginations import SmallSizeSetPagination -from project.models import Project from entry.models import Entry from .models import ( AnalysisFramework, Widget, Filter, Exportable, AnalysisFrameworkMembership, - AnalysisFrameworkRole, ) from .serializers import ( AnalysisFrameworkSerializer, WidgetSerializer, FilterSerializer, ExportableSerializer, AnalysisFrameworkMembershipSerializer, - AnalysisFrameworkRoleSerializer, ) from .filter_set import AnalysisFrameworkFilterSet from .permissions import FrameworkMembershipModifyPermission @@ -85,57 +80,6 @@ def get_memberships(self, request, pk=None, version=None): return self.get_paginated_response(serializer.data) -class AnalysisFrameworkCloneView(views.APIView): - permission_classes = [permissions.IsAuthenticated] - - def post(self, request, af_id, version=None): - if not AnalysisFramework.objects.filter( - id=af_id - ).exists(): - raise exceptions.NotFound() - - analysis_framework = AnalysisFramework.objects.get( - id=af_id - ) - if not analysis_framework.can_clone(request.user): - raise exceptions.PermissionDenied() - - cloned_title = request.data.get('title') - if not cloned_title: - raise exceptions.ValidationError({ - 'title': 'Title should be present', - }) - - new_af = analysis_framework.clone( - request.user, - title=cloned_title, - description=request.data.get('description'), - ) - # Set the requesting user as owner member, don't create other memberships of old framework - new_af.add_member(request.user, new_af.get_or_create_owner_role()) - - serializer = AnalysisFrameworkSerializer( - new_af, - context={'request': request}, - ) - - project = request.data.get('project') - if project: - project = Project.objects.get(id=project) - if not project.can_modify(request.user): - raise exceptions.ValidationError({ - 'project': 'Invalid project', - }) - project.analysis_framework = new_af - project.modified_by = request.user - project.save() - - return response.Response( - serializer.data, - status=status.HTTP_201_CREATED, - ) - - class WidgetViewSet(viewsets.ModelViewSet): serializer_class = WidgetSerializer permission_classes = [permissions.IsAuthenticated, @@ -185,25 +129,3 @@ def destroy(self, request, *args, **kwargs): self.perform_destroy(instance) return response.Response(status=status.HTTP_204_NO_CONTENT) - - -class PrivateAnalysisFrameworkRoleViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = AnalysisFrameworkRoleSerializer - permission_classes = [permissions.IsAuthenticated] - - def get_queryset(self): - return AnalysisFrameworkRole.objects.filter(is_private_role=True) - - -class PublicAnalysisFrameworkRoleViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = AnalysisFrameworkRoleSerializer - permission_classes = [permissions.IsAuthenticated] - - def get_queryset(self): - no_default_role = self.request.query_params.get('is_default_role', 'true') == 'false' - extra = {} if not no_default_role else {'is_default_role': False} - - return AnalysisFrameworkRole.objects.filter( - is_private_role=False, - **extra, - ) diff --git a/apps/ary/serializers.py b/apps/ary/serializers.py index 0fcf8197cc..29f5614fd7 100644 --- a/apps/ary/serializers.py +++ b/apps/ary/serializers.py @@ -18,7 +18,7 @@ from organization.models import Organization, OrganizationType from organization.serializers import ( ArySourceOrganizationSerializer, - OrganizationTypeSerializer, + OrganizationGqSerializer, ) from gallery.serializers import SimpleFileSerializer @@ -322,7 +322,7 @@ def have_source(source_type): context=self.context, ).data if have_source(Field.ORGANIZATIONS or Field.DONORS) else [], - 'organization_type': OrganizationTypeSerializer( + 'organization_type': OrganizationGqSerializer( OrganizationType.objects.all(), many=True, ).data diff --git a/apps/geo/serializers.py b/apps/geo/serializers.py index cf70db9571..c451fbe57a 100644 --- a/apps/geo/serializers.py +++ b/apps/geo/serializers.py @@ -1,11 +1,9 @@ -from django.conf import settings from django.db import transaction from drf_dynamic_fields import DynamicFieldsMixin from deep.serializers import ( RemoveNullFieldsMixin, TempClientIdMixin, - URLCachedFileField, ) from rest_framework import serializers from user_resource.serializers import UserResourceSerializer @@ -16,7 +14,6 @@ ) from geo.tasks import load_geo_areas from project.models import Project -from gallery.serializers import SimpleFileSerializer class SimpleRegionSerializer(RemoveNullFieldsMixin, @@ -85,58 +82,6 @@ def create(self, validated_data): return region -class AdminLevelSerializer(RemoveNullFieldsMixin, - DynamicFieldsMixin, serializers.ModelSerializer): - """ - Admin Level Model Serializer - """ - geo_shape_file_details = SimpleFileSerializer(source='geo_shape_file', read_only=True) - - geojson_file = URLCachedFileField(required=False, read_only=True) - bounds_file = URLCachedFileField(required=False, read_only=True) - - class Meta: - model = AdminLevel - exclude = ('geo_area_titles',) - - # Validations - def validate_region(self, region): - if not region.can_modify(self.context['request'].user): - raise serializers.ValidationError('Invalid region') - return region - - def create(self, validated_data): - admin_level = super().create(validated_data) - admin_level.stale_geo_areas = True - admin_level.save() - - region = admin_level.region - region.modified_by = self.context['request'].user - region.save() - - if not settings.TESTING: - transaction.on_commit(lambda: load_geo_areas.delay(region.id)) - - return admin_level - - def update(self, instance, validated_data): - admin_level = super().update( - instance, - validated_data, - ) - admin_level.stale_geo_areas = True - admin_level.save() - - region = admin_level.region - region.modified_by = self.context['request'].user - region.save() - - if not settings.TESTING: - transaction.on_commit(lambda: load_geo_areas.delay(region.id)) - - return admin_level - - class GeoAreaSerializer(serializers.ModelSerializer): label = serializers.CharField() key = serializers.CharField() diff --git a/apps/geo/tests/test_apis.py b/apps/geo/tests/test_apis.py index 4a61ae38ca..83ab914f81 100644 --- a/apps/geo/tests/test_apis.py +++ b/apps/geo/tests/test_apis.py @@ -6,64 +6,6 @@ class RegionTests(TestCase): - def test_create_region(self): - region_count = Region.objects.count() - - project = self.create(Project, role=self.admin_role) - url = '/api/v1/regions/' - data = { - 'code': 'NLP', - 'title': 'Nepal', - 'data': {'testfield': 'testfile'}, - 'public': True, - 'project': project.id, - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_201(response) - - self.assertEqual(Region.objects.count(), region_count + 1) - self.assertEqual(response.data['code'], data['code']) - self.assertIn(Region.objects.get(id=response.data['id']), - project.regions.all()) - - def test_region_published_status(self): - """ - Once published is set to True for region don't allow it to modify - """ - project = self.create(Project, role=self.admin_role) - region = self.create(Region, is_published=True) - project.regions.add(region) - - data = { - 'is_published': False - } - url = f'/api/v1/regions/{region.id}/' - self.authenticate() - response = self.client.patch(url, data) - self.assert_403(response) - - def test_publish_region(self): - user = self.create_user() - user1 = self.create_user() - project = self.create(Project, role=self.admin_role) - region = self.create(Region, created_by=user) - project.regions.add(region) - - url = f'/api/v1/regions/{region.id}/publish/' - data = {} - - # authenticated with user that has not created region - self.authenticate(user1) - response = self.client.post(url, data) - self.assert_400(response) - - self.authenticate(user) - response = self.client.post(url, data) - self.assert_200(response) - self.assertEqual(response.data['is_published'], True) - def test_clone_region(self): project = self.create(Project, role=self.admin_role) region = self.create(Region) @@ -85,35 +27,6 @@ def test_clone_region(self): new_region = Region.objects.get(id=response.data['id']) self.assertTrue(new_region in project.regions.all()) - def test_region_filter_not_in_project(self): - project_1 = self.create(Project, role=self.admin_role) - project_2 = self.create(Project, role=self.admin_role) - region_1 = self.create(Region) - region_2 = self.create(Region) - region_3 = self.create(Region) - project_1.regions.add(region_1) - project_1.regions.add(region_2) - project_2.regions.add(region_3) - - # filter regions in project - url = f'/api/v1/regions/?project={project_1.id}' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(len(response.data['results']), 2) - self.assertEqual( - set(rg['id'] for rg in response.data['results']), - set([region_1.id, region_2.id]) - ) - - # filter the region that are not in project - url = f'/api/v1/regions/?exclude_project={project_1.id}' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(len(response.data['results']), 1) - self.assertEqual(response.data['results'][0]['id'], region_3.id) - def test_trigger_api(self): region = self.create(Region) url = '/api/v1/geo-areas-load-trigger/{}/'.format(region.id) @@ -123,29 +36,6 @@ def test_trigger_api(self): self.assert_200(response) -class AdminLevelTests(TestCase): - def test_create_admin_level(self): - admin_level_count = AdminLevel.objects.count() - - region = self.create(Region) - url = '/api/v1/admin-levels/' - data = { - 'region': region.pk, - 'title': 'test', - 'name_prop': 'test', - 'pcode_prop': 'test', - 'parent_name_prop': 'test', - 'parent_pcode_prop': 'test', - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_201(response) - - self.assertEqual(AdminLevel.objects.count(), admin_level_count + 1) - self.assertEqual(response.data['title'], data['title']) - - class GeoOptionsApi(TestCase): def test_geo_options(self): region1 = self.create(Region, title='Region 1') diff --git a/apps/geo/views.py b/apps/geo/views.py index f4a6578a93..4643c17084 100644 --- a/apps/geo/views.py +++ b/apps/geo/views.py @@ -1,23 +1,17 @@ from django.shortcuts import redirect, get_object_or_404 -from django.contrib.gis.geos import GEOSGeometry -from django.contrib.gis.gdal.error import GDALException from django.conf import settings from django.db import models from rest_framework import ( exceptions, - filters, permissions, response, status, views, viewsets, ) -from rest_framework.decorators import action -import django_filters from deep.permissions import ( - ModifyPermission, IsProjectMember ) from project.models import Project @@ -25,79 +19,15 @@ from .models import Region, AdminLevel, GeoArea from .serializers import ( - AdminLevelSerializer, RegionSerializer, GeoAreaSerializer ) from .filter_set import ( GeoAreaFilterSet, - AdminLevelFilterSet, - RegionFilterSet ) from .tasks import load_geo_areas -class RegionViewSet(viewsets.ModelViewSet): - serializer_class = RegionSerializer - permission_classes = [permissions.IsAuthenticated, ModifyPermission] - filter_backends = (django_filters.rest_framework.DjangoFilterBackend, - filters.SearchFilter, filters.OrderingFilter) - filterset_class = RegionFilterSet - search_fields = ('title', 'code') - - def get_queryset(self): - return Region.get_for(self.request.user).defer('geo_options') - - @action( - detail=True, - url_path='intersects', - methods=('post',), - # TODO: Better permissions - permission_classes=[permissions.IsAuthenticated], - ) - def get_intersects(self, request, pk=None, version=None): - region = self.get_object() - try: - geoms = [] - features = request.data['features'] - for feature in features: - geoms.append([feature.get('id'), GEOSGeometry(str(feature['geometry']))]) - except (GDALException, KeyError) as e: - raise exceptions.ValidationError( - f"Geometry parsed failed, Error: {getattr(e, 'message', repr(e))}" - ) - return response.Response([ - { - 'id': id, - 'region_id': region.pk, - 'geoareas': ( - # https://docs.djangoproject.com/en/2.1/ref/contrib/gis/geoquerysets/ - GeoArea.objects.filter( - admin_level__region=region, - polygons__intersects=geom, - ).values_list('id', flat=True) - ), - } - for id, geom in geoms - ]) - - @action( - detail=True, - url_path='publish', - methods=['post'], - serializer_class=RegionSerializer, - permission_classes=[permissions.IsAuthenticated] - ) - def get_published(self, request, pk=None, version=None): - region = self.get_object() - if not region.can_publish(self.request.user): - raise exceptions.ValidationError('Can be published by user who created it') - region.is_published = True - region.save(update_fields=['is_published']) - serializer = RegionSerializer(region, partial=True, context={'request': request}) - return response.Response(serializer.data) - - class RegionCloneView(views.APIView): permission_classes = [permissions.IsAuthenticated] @@ -126,24 +56,6 @@ def post(self, request, region_id, version=None): status=status.HTTP_201_CREATED) -class AdminLevelViewSet(viewsets.ModelViewSet): - """ - Admin Level API Point - """ - serializer_class = AdminLevelSerializer - permission_classes = [permissions.IsAuthenticated, - ModifyPermission] - filter_backends = (django_filters.rest_framework.DjangoFilterBackend, - filters.SearchFilter, filters.OrderingFilter) - filterset_class = AdminLevelFilterSet - search_fields = ('title') - - def get_queryset(self): - return AdminLevel.get_for(self.request.user).select_related('geo_shape_file').defer( - *AdminLevelSerializer.Meta.exclude - ) - - class GeoAreasLoadTriggerView(views.APIView): """ A trigger for loading geo areas from admin level diff --git a/apps/lead/serializers.py b/apps/lead/serializers.py index 0267122691..7f1cce58fd 100644 --- a/apps/lead/serializers.py +++ b/apps/lead/serializers.py @@ -304,29 +304,6 @@ class Meta: fields = ('id', 'file',) -class LeadPreviewSerializer(RemoveNullFieldsMixin, - DynamicFieldsMixin, serializers.ModelSerializer): - """ - Serializer for lead preview - """ - - text = serializers.CharField(source='leadpreview.text_extract', - read_only=True) - images = LeadPreviewAttachmentSerializer(many=True, read_only=True) - classified_doc_id = serializers.IntegerField( - source='leadpreview.classified_doc_id', - read_only=True, - ) - preview_id = serializers.IntegerField( - source='leadpreview.pk', - read_only=True, - ) - - class Meta: - model = Lead - fields = ('id', 'preview_id', 'text', 'images', 'classified_doc_id') - - class LeadGroupSerializer(RemoveNullFieldsMixin, DynamicFieldsMixin, UserResourceSerializer): leads = LeadSerializer(source='lead_set', diff --git a/apps/lead/tests/test_apis.py b/apps/lead/tests/test_apis.py index 11e6aae21a..e53a747e1f 100644 --- a/apps/lead/tests/test_apis.py +++ b/apps/lead/tests/test_apis.py @@ -131,46 +131,6 @@ def test_lead_create_with_status_validated(self, assignee=None): r_data = response.data self.assertEqual(r_data['status'], data['status']) - def test_pre_bulk_delete_leads(self): - project = self.create(Project) - lead1 = self.create(Lead, project=project) - lead3 = self.create(Lead, project=project) - - lead_ids = [lead1.id, lead3.id] - admin_user = self.create(User) - project.add_member(admin_user, role=self.admin_role) - url = '/api/v1/project/{}/leads/dry-bulk-delete/'.format(project.id) - self.authenticate(admin_user) - response = self.client.post(url, {'leads': lead_ids}) - self.assert_200(response) - r_data = response.data - self.assertIn('entries', r_data) - - def test_bulk_delete_leads(self): - project = self.create(Project) - lead1 = self.create(Lead, project=project) - self.create(Lead, project=project) - lead3 = self.create(Lead, project=project) - lead_count = Lead.objects.count() - - lead_ids = [lead1.id, lead3.id] - url = '/api/v1/project/{}/leads/bulk-delete/'.format(project.id) - - # calling without delete permissions - view_user = self.create(User) - project.add_member(view_user, role=self.view_only_role) - self.authenticate(view_user) - response = self.client.post(url, {'leads': lead_ids}) - self.assert_403(response) - - admin_user = self.create(User) - project.add_member(admin_user, role=self.admin_role) - self.authenticate(admin_user) - response = self.client.post(url, {'leads': lead_ids}) - self.assert_204(response) - - self.assertLess(Lead.objects.count(), lead_count) - def test_create_high_priority_lead(self, assignee=None): lead_count = Lead.objects.count() project = self.create(Project, role=self.admin_role) diff --git a/apps/lead/views.py b/apps/lead/views.py index 339563cb9d..db25caa0f7 100644 --- a/apps/lead/views.py +++ b/apps/lead/views.py @@ -25,7 +25,7 @@ import django_filters -from deep.permissions import ModifyPermission, CreateLeadPermission, DeleteLeadPermission +from deep.permissions import ModifyPermission, CreateLeadPermission from deep.paginations import AutocompleteSetPagination from deep.authentication import CSRFExemptSessionAuthentication @@ -55,7 +55,6 @@ LeadGroupSerializer, SimpleLeadGroupSerializer, LeadSerializer, - LeadPreviewSerializer, check_if_url_exists, LeadOptionsSerializer, LeadOptionsBodySerializer, @@ -237,43 +236,6 @@ def leads_filter(self, request, version=None): return response -class LeadBulkDeleteViewSet(viewsets.GenericViewSet): - permission_classes = [permissions.IsAuthenticated, DeleteLeadPermission] - - def get_serializer_class(self): - # required by ViewSchema generator - return serializers.Serializer - - @action( - detail=False, - methods=['post'], - url_path='dry-bulk-delete', - ) - def dry_bulk_delete(self, request, project_id, version=None): - lead_ids = request.data.get('leads', []) - tbd_entities = Lead.get_associated_entities(project_id, lead_ids) - return response.Response(tbd_entities, status=status.HTTP_200_OK) - - @action( - detail=False, - methods=['post'], - url_path='bulk-delete', - ) - def bulk_delete(self, request, project_id, version=None): - lead_ids = request.data.get('leads', []) - Lead.objects.filter(project_id=project_id, id__in=lead_ids).delete() - return response.Response(status=status.HTTP_204_NO_CONTENT) - - -class LeadPreviewViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = LeadPreviewSerializer - permission_classes = [permissions.IsAuthenticated, - ModifyPermission] - - def get_queryset(self): - return Lead.get_for(self.request.user) - - class LeadOptionsView(views.APIView): """ Options for various attributes related to lead diff --git a/apps/notification/tests/test_apis.py b/apps/notification/tests/test_apis.py index 0a5c9ddc2a..258c04168e 100644 --- a/apps/notification/tests/test_apis.py +++ b/apps/notification/tests/test_apis.py @@ -1,15 +1,11 @@ -import pytest from datetime import timedelta -from django.contrib.contenttypes.models import ContentType from deep.tests import TestCase from django.utils import timezone from user.models import User -from lead.models import Lead -from notification.models import Notification, Assignment +from notification.models import Notification from project.models import ProjectJoinRequest, Project -from quality_assurance.models import EntryReviewComment class TestNotificationAPIs(TestCase): @@ -232,325 +228,3 @@ def test_get_notification_count(self): # XXX: # apps/leads/tests/test_schemas.py::TestLeadBulkMutationSchema->BulkGrapheneMutation # is causing issue, so running this before all. -@pytest.mark.run(order=1) -class TestAssignmentApi(TestCase): - """ Api test for assignment model""" - - def test_get_assignments_lead(self): - project = self.create_project() - project1 = self.create_project() - user1 = self.create(User) - user2 = self.create(User) - - url = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - # try creating lead - lead = self.create_lead(project=project, assignee=[user1]) - self.create(Lead, project=project1, assignee=[user2]) - - self.authenticate(user1) - params = {'project': project.id} - - response = self.client.get(url, params) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - self.assertEqual(response.data['results'][0]['project_details']['id'], project.id) - self.assertEqual(response.data['results'][0]['content_object_type'], 'lead') - self.assertEqual(response.data['results'][0]['content_object_details']['id'], lead.id) - - def test_create_assignment_on_lead_title_change(self): - project = self.create_project() - user1 = self.create(User) - - url = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - # create lead with title - lead = self.create(Lead, title="Uncommitted", project=project, assignee=[user1]) - url = '/api/v1/leads/' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - # try to change the title this should not create another assignment - url = '/api/v1/leads/{}/'.format(lead.id) - data = { - 'title': 'Changed' - } - self.authenticate() - response = self.client.patch(url, data) - self.assert_200(response) - - # try to check the assignment - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - self.assertEqual(response.data['count'], 1) - self.assertEqual(response.data['results'][0]['content_object_type'], 'lead') - self.assertEqual(response.data['results'][0]['content_object_details']['id'], lead.id) - self.assertEqual(response.data['results'][0]['content_object_details']['title'], 'Changed') # the new title - - def test_create_assignment_on_lead_assignee_change(self): - project = self.create_project() - user1 = self.create(User) - user2 = self.create(User) - - url = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - # create lead with title - lead = self.create(Lead, title="Uncommitted", project=project, assignee=[user1]) - url = '/api/v1/leads/' - self.authenticate() - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - # try to change the title this should not create another assignment - url = '/api/v1/leads/{}/'.format(lead.id) - data = { - 'assignee': user2.id - } - self.authenticate() - response = self.client.patch(url, data) - self.assert_200(response) - - # try to check the assignment - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0 # changing the assignee should remove fromn the previous assignee - - # try to aunthenticate the user2 - url = '/api/v1/assignments/' - self.authenticate(user2) - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - def test_get_assignments_entrycomment(self): - project = self.create_project() - project1 = self.create_project() - user1 = self.create(User) - user2 = self.create(User) - entry = self.create_entry(project=project) - - url = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - entry_comment = self.create(EntryReviewComment, entry=entry, project=project, mentioned_users=[user1]) - self.create(EntryReviewComment, entry=entry, project=project1, mentioned_users=[user2]) - - self.authenticate(user1) - params = {'project': project.id} - - response = self.client.get(url, params) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - self.assertEqual(response.data['results'][0]['project_details']['id'], entry.project.id) - self.assertEqual(response.data['results'][0]['content_object_type'], 'entryreviewcomment') - self.assertEqual(response.data['results'][0]['content_object_details']['id'], entry_comment.id) - - def test_create_assignment_on_entry_comment_text_change(self): - project = self.create_project() - self.create_project() - user1 = self.create(User) - self.create(User) - entry = self.create_entry(project=project) - entry.project.add_member(user1) - - url1 = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url1) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - url = f'/api/v1/entries/{entry.pk}/review-comments/' - data = { - 'mentioned_users': [user1.pk], - 'text': 'This is first comment', - 'parent': None, - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_201(response) - comment_id = response.json()['id'] - - url1 = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url1) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - # Patch new text - new_text = 'this is second comment' - self.authenticate() - response = self.client.patch(f'{url}{comment_id}/', {'text': new_text}) - self.assert_200(response) - - # try to check the assignment - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 1 - self.assertEqual(response.data['results'][0]['content_object_details']['id'], comment_id) - self.assertEqual(response.data['results'][0]['content_object_details']['text'], new_text) - - def test_assignment_create_on_entry_comment_assignee_change(self): - project = self.create_project() - self.create_project() - user1 = self.create(User) - user2 = self.create(User) - entry = self.create_entry(project=project) - for user in [user1, user2]: - entry.project.add_member(user, role=self.normal_role) - - url1 = '/api/v1/assignments/' - - self.authenticate(user1) - response = self.client.get(url1) - self.assert_200(response) - - data = response.data - assert data['count'] == 0, "No Assignments till now" - - url = f'/api/v1/entries/{entry.pk}/review-comments/' - data = { - 'mentioned_users': [user1.pk], - 'text': 'This is first comment', - 'parent': None, - } - - self.authenticate() - response = self.client.post(url, data) - self.assert_201(response) - comment_id = response.json()['id'] - - url1 = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url1) - self.assert_200(response) - self.assertEqual(response.data['count'], 1) - - # Patch new assignee - self.authenticate() - response = self.client.patch(f'{url}{comment_id}/', {'mentioned_users': [user2.pk]}) - self.assert_200(response) - - # try to check the assignment - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 0 # no assignment for user1 - - url = '/api/v1/assignments/' - self.authenticate(user2) - response = self.client.get(url) - self.assert_200(response) - - data = response.data - assert data['count'] == 1 # assignment for user2 - - def test_assignment_is_done(self): - # XXX: To avoid using content type cache from pre-tests - ContentType.objects.clear_cache() - - project = self.create(Project) - user1 = self.create(User) - user2 = self.create(User) - lead = self.create(Lead, project=project) - kwargs = { - 'content_object': lead, - 'project': project, - 'created_for': user1, - 'created_by': user2, - } - assignment = self.create(Assignment, **kwargs) - self.create(Assignment, **kwargs) - self.create(Assignment, **kwargs) - - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['count'], 3) - - # try to put is_done for single assignment - url = f'/api/v1/assignments/{assignment.id}/' - data = { - 'is_done': 'true', - } - self.authenticate(user1) - response = self.client.put(url, data) - self.assert_200(response) - self.assertEqual(response.data['is_done'], True) - - url = '/api/v1/assignments/bulk-mark-as-done/' - data = { - 'is_done': 'true', - } - response = self.client.post(url, data) - self.assert_200(response) - self.assertEqual(response.data['assignment_updated'], 2) - - # test for is_done is true - url = '/api/v1/assignments/' - self.authenticate(user1) - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['results'][1]['is_done'], True) - self.assertEqual(response.data['results'][2]['is_done'], True) diff --git a/apps/notification/views.py b/apps/notification/views.py index 49498b2c76..294e56cdc8 100644 --- a/apps/notification/views.py +++ b/apps/notification/views.py @@ -1,12 +1,10 @@ import django_filters from rest_framework.decorators import action -from .serializers import NotificationSerializer, AssignmentSerializer -from .models import Notification, Assignment -from deep.paginations import AssignmentPagination +from .serializers import NotificationSerializer +from .models import Notification from notification.filter_set import ( NotificationFilterSet, - AssignmentFilterSet ) from rest_framework import ( @@ -75,29 +73,3 @@ def get_count(self, request, version=None): 'total': total, } return response.Response(result) - - -class AssignmentViewSet(viewsets.ModelViewSet): - serializer_class = AssignmentSerializer - permission_classes = [permissions.IsAuthenticated] - filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) - filterset_class = AssignmentFilterSet - pagination_class = AssignmentPagination - - def get_queryset(self): - return Assignment.get_for(self.request.user).select_related( - 'project', 'created_by', 'content_type', - ).order_by('-created_at') - - @action( - detail=False, - methods=['POST'], - permission_classes=[permissions.IsAuthenticated], - url_path='bulk-mark-as-done' - ) - def status(self, request, version=None): - queryset = self.filter_queryset(self.get_queryset()).filter(is_done=False) - updated_rows_count = queryset.update(is_done=True) - return response.Response({ - 'assignment_updated': updated_rows_count, - }) diff --git a/apps/organization/serializers.py b/apps/organization/serializers.py index 3f4df84f6e..b31adee892 100644 --- a/apps/organization/serializers.py +++ b/apps/organization/serializers.py @@ -2,16 +2,9 @@ from user_resource.serializers import UserResourceSerializer from drf_dynamic_fields import DynamicFieldsMixin -from geo.serializers import SimpleRegionSerializer -from deep.serializers import URLCachedFileField, RemoveNullFieldsMixin +from deep.serializers import URLCachedFileField -from .models import Organization, OrganizationType - - -class OrganizationTypeSerializer(serializers.ModelSerializer): - class Meta: - model = OrganizationType - fields = ('__all__') +from .models import Organization class MergedAsOrganizationSerializer(serializers.ModelSerializer): @@ -31,35 +24,6 @@ class Meta: fields = ('id', 'title', 'short_name', 'merged_as', 'logo') -class OrganizationSerializer( - DynamicFieldsMixin, RemoveNullFieldsMixin, UserResourceSerializer, -): - organization_type_display = OrganizationTypeSerializer( - source='organization_type', read_only=True, - ) - regions_display = SimpleRegionSerializer( - source='regions', read_only=True, many=True, - ) - logo_url = URLCachedFileField(source='logo.file', allow_null=True, required=False) - merged_as = MergedAsOrganizationSerializer(source='parent', read_only=True) - client_id = None - - class Meta: - model = Organization - exclude = ('parent',) - read_only_fields = ('verified', 'logo_url',) - - def create(self, validated_data): - organization = super().create(validated_data) - organization.created_by = organization.modified_by = self.context['request'].user - return organization - - -def get_merged_as(self, obj): - if obj and obj.parent_id: - return OrganizationSerializer(obj.parent, context=self.context).data - - class ArySourceOrganizationSerializer(DynamicFieldsMixin, UserResourceSerializer): logo = URLCachedFileField(source='logo.file', allow_null=True) key = serializers.IntegerField(source='pk') diff --git a/apps/organization/views.py b/apps/organization/views.py index 6b0f34919a..e69de29bb2 100644 --- a/apps/organization/views.py +++ b/apps/organization/views.py @@ -1,46 +0,0 @@ -from rest_framework import viewsets, mixins, permissions, filters - -import django_filters - -from deep.paginations import AutocompleteSetPagination -from deep.authentication import CSRFExemptSessionAuthentication - -from .serializers import ( - OrganizationSerializer, - OrganizationTypeSerializer, -) -from .models import ( - Organization, - OrganizationType, -) - - -class OrganizationTypeViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = OrganizationTypeSerializer - permission_classes = [permissions.IsAuthenticated] - queryset = OrganizationType.objects.all() - filter_backends = (django_filters.rest_framework.DjangoFilterBackend, - filters.SearchFilter, filters.OrderingFilter) - search_fields = ('title', 'description',) - - -class OrganizationViewSet( - mixins.CreateModelMixin, - mixins.RetrieveModelMixin, - mixins.ListModelMixin, - viewsets.GenericViewSet, -): - serializer_class = OrganizationSerializer - permission_classes = [permissions.IsAuthenticated] - pagination_class = AutocompleteSetPagination - authentication_classes = [CSRFExemptSessionAuthentication] - filter_backends = (django_filters.rest_framework.DjangoFilterBackend, - filters.SearchFilter, filters.OrderingFilter) - search_fields = ('title', 'short_name', 'long_name', 'url',) - filterset_fields = ('verified',) - ordering = ('title',) - - def get_queryset(self): - if self.kwargs.get('pk'): - return Organization.objects.prefetch_related('parent') - return Organization.objects.filter(parent=None) diff --git a/apps/project/serializers.py b/apps/project/serializers.py index 4e50d4a555..a11dfa1033 100644 --- a/apps/project/serializers.py +++ b/apps/project/serializers.py @@ -49,7 +49,6 @@ SimpleOrganizationSerializer ) -from .permissions import PROJECT_PERMISSIONS from .activity import project_activity_log @@ -66,55 +65,6 @@ class Meta: fields = ('id', 'title') -class ProjectRoleSerializer(RemoveNullFieldsMixin, - DynamicFieldsMixin, - serializers.ModelSerializer): - lead_permissions = serializers.SerializerMethodField() - entry_permissions = serializers.SerializerMethodField() - setup_permissions = serializers.SerializerMethodField() - export_permissions = serializers.SerializerMethodField() - assessment_permissions = serializers.SerializerMethodField() - - class Meta: - model = ProjectRole - fields = '__all__' - - def get_lead_permissions(self, roleobj): - return [ - k - for k, v in PROJECT_PERMISSIONS['lead'].items() - if roleobj.lead_permissions & v != 0 - ] - - def get_entry_permissions(self, roleobj): - return [ - k - for k, v in PROJECT_PERMISSIONS['entry'].items() - if roleobj.entry_permissions & v != 0 - ] - - def get_setup_permissions(self, roleobj): - return [ - k - for k, v in PROJECT_PERMISSIONS['setup'].items() - if roleobj.setup_permissions & v != 0 - ] - - def get_export_permissions(self, roleobj): - return [ - k - for k, v in PROJECT_PERMISSIONS['export'].items() - if roleobj.export_permissions & v != 0 - ] - - def get_assessment_permissions(self, roleobj): - return [ - k - for k, v in PROJECT_PERMISSIONS['assessment'].items() - if roleobj.assessment_permissions & v != 0 - ] - - class SimpleProjectRoleSerializer(RemoveNullFieldsMixin, DynamicFieldsMixin, serializers.ModelSerializer): diff --git a/apps/project/tests/test_apis.py b/apps/project/tests/test_apis.py index bd74ee47a9..c560eb5ca4 100644 --- a/apps/project/tests/test_apis.py +++ b/apps/project/tests/test_apis.py @@ -24,7 +24,6 @@ from geo.models import Region from project.tasks import ( _generate_project_viz_stats, - _generate_project_stats_cache, ) from ary.models import AssessmentTemplate from project.models import ( @@ -1068,27 +1067,6 @@ def test_delete_project_normal(self): response = self.client.delete(url) self.assert_403(response) - def test_get_project_role(self): - project = self.create(Project, role=self.admin_role) - user = self.create(User) - project.add_member(user) - - url = '/api/v1/project-roles/' - - self.authenticate() - - response = self.client.get(url) - self.assert_200(response) - - data = response.json() - assert "results" in data - for x in data["results"]: - assert "setupPermissions" in x - assert "assessmentPermissions" in x - assert "entryPermissions" in x - assert "leadPermissions" in x - assert "exportPermissions" in x - def test_can_modify(self): project = self.create(Project, role=self.admin_role) test_user = self.create(User) @@ -1395,230 +1373,6 @@ def test_project_memberships_in_particluar_project(self): self.assertEqual(response.data['id'], project1.id) self.assertNotIn('memberships', response.data) # `membership` field shouldnot be present - def test_project_summary_api(self): - user = self.create_user() - - project1 = self.create_project(title='Project 1') - project2 = self.create_project(title='Project 2') - project3 = self.create_project(title='Project 3') - project4 = self.create_project(title='Project 4') - project5 = self.create_project(title='Project 5') - project1.add_member(user) - project2.add_member(user) - project3.add_member(user) - project4.add_member(user) - project5.add_member(user) - - lead1 = self.create_lead(project=project1) - lead2 = self.create_lead(project=project1) - lead3 = self.create_lead(project=project2) - lead4 = self.create_lead(project=project3) - lead5 = self.create_lead(project=project4) - lead6 = self.create_lead(project=project4) - lead7 = self.create_lead(project=project5) - self.create_lead(project=project1) - self.create_lead(project=project5) - - data = [ - { - "lead": lead1, - "controlled": True, - "months": -3, - "days": -1 - }, - { - "lead": lead1, - "controlled": True, - "months": -2, - "days": -1 - }, - { - "lead": lead2, - "controlled": False, - "months": -3, - "days": -1 - }, - { - "lead": lead2, - "controlled": True, - "months": -3, - "days": -1 - }, - { - "lead": lead2, - "controlled": True, - "months": -3, - "days": -1 - }, - { - "lead": lead3, - "controlled": True, - "months": -1, - "days": -10 - }, - { - "lead": lead3, - "controlled": True, - "months": -1, - "days": -20 - }, - { - "lead": lead3, - "controlled": True, - "months": -1, - "days": -30 - }, - { - "lead": lead3, - "controlled": True, - "months": -1, - "days": -40 - }, - { - "lead": lead4, - "controlled": False, - "months": -3, - "days": -1 - }, - { - "lead": lead5, - "controlled": True, - "months": -3, - "days": -1, - }, - { - "lead": lead5, - "controlled": True, - "months": -2, - "days": -1, - }, - { - "lead": lead6, - "controlled": True, - "months": -3, - "days": -1 - }, - { - "lead": lead7, - "controlled": True, - "months": -3, - "days": 0, - }, - ] - now = timezone.now() - for item in data: - self.update_obj( - self.create_entry(lead=item['lead'], controlled=item['controlled'], created_by=user), - created_at=now + relativedelta(months=item['months'], days=item['days']) - ) - - # Run the caching process - _generate_project_stats_cache() - - self.authenticate(user) - url = '/api/v1/projects-stat/summary/' - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(response.data['projects_count'], 5) - self.assertEqual(response.data['total_leads_count'], 9) - self.assertEqual(response.data['total_leads_tagged_count'], 7) - self.assertEqual(response.data['total_leads_tagged_and_controlled_count'], 5) - self.assertEqual(len(response.data['recent_entries_activity']['projects']), 3) - self.assertEqual(response.data['recent_entries_activity']['projects'][0]['id'], project1.id) - self.assertEqual(response.data['recent_entries_activity']['projects'][0]['count'], 1) - self.assertEqual(response.data['recent_entries_activity']['projects'][1]['id'], project2.id) - self.assertEqual(response.data['recent_entries_activity']['projects'][1]['count'], 4) - self.assertEqual(len(response.data['recent_entries_activity']['activities']), 6) - - def test_project_recent_api(self): - user = self.create_user() - - project1 = self.create_project(title='Project 1') - project2 = self.create_project(title='Project 2') - project3 = self.create_project(title='Project 3') - project4 = self.create_project(title='Project 4') - project1.add_member(user) - project2.add_member(user) - project3.add_member(user) - - lead1 = self.create_lead(project=project1, created_by=user) - lead2 = self.create_lead(project=project2, created_by=user) - self.create_entry(lead=lead1, controlled=False, created_by=user) - self.create_lead(project=project3, created_by=user) - self.create_lead(project=project4, created_by=user) - - self.authenticate(user) - url = '/api/v1/projects-stat/recent/' - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(len(response.data), 3) - self.assertEqual(response.data[0]['id'], project3.pk) - self.assertEqual(response.data[1]['id'], project1.pk) - self.assertEqual(response.data[2]['id'], project2.pk) - - lead2.modified_by = user - lead2.save() - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(len(response.data), 3) - self.assertEqual(response.data[0]['id'], project2.pk) - - def test_project_stats_api(self): - user = self.create_user() - - project1 = self.create_project(title='Project 1') - project2 = self.create_project(title='Project 2') - project3 = self.create_project(title='Project 3') - project1.add_member(user) - project2.add_member(user) - - now = timezone.now() - lead1_1 = self.update_obj(self.create_lead(project=project1), created_at=now + relativedelta(months=-1)) - lead1_2 = self.update_obj(self.create_lead(project=project1), created_at=now + relativedelta(months=-2)) - lead1_3 = self.update_obj(self.create_lead(project=project1), created_at=now + relativedelta(months=-2)) - lead1_4 = self.update_obj(self.create_lead(project=project1), created_at=now + relativedelta(months=-1)) - self.update_obj(self.create_lead(project=project1), created_at=now + relativedelta(months=-1)) - - self.update_obj(self.create_entry(lead=lead1_1, controlled=False), created_at=now + relativedelta(months=-1)) - self.update_obj(self.create_entry(lead=lead1_1, controlled=False), created_at=now + relativedelta(months=-1)) - self.update_obj(self.create_entry(lead=lead1_2, controlled=True), created_at=now + relativedelta(months=-3)) - self.update_obj(self.create_entry(lead=lead1_2, controlled=True), created_at=now + relativedelta(months=-2)) - self.update_obj(self.create_entry(lead=lead1_2, controlled=True), created_at=now + relativedelta(months=-2)) - self.update_obj(self.create_entry(lead=lead1_3, controlled=True), created_at=now + relativedelta(months=-3)) - self.update_obj(self.create_entry(lead=lead1_3, controlled=True), created_at=now + relativedelta(months=-3)) - self.create_entry(lead=lead1_3, controlled=True) - self.create_entry(lead=lead1_4, controlled=True) - - lead2 = self.create_lead(project=project2) - lead3 = self.create_lead(project=project3) - self.create_entry(lead=lead2, controlled=False) - self.create_entry(lead=lead3, controlled=False) - - # number_of_leads_tagged - lead1_2.status = lead1_1.status = Lead.Status.TAGGED - lead1_2.save(update_fields=('status',)) - lead1_1.save(update_fields=('status',)) - - # Run the caching process - _generate_project_stats_cache() - - self.authenticate(user) - url = '/api/v1/projects-stat/?involvement=my_projects' - response = self.client.get(url) - self.assert_200(response) - self.assertEqual(len(response.data['results']), 2) - # Check response for Project 1 - project_1_data = next( - project for project in response.data['results'] if project['id'] == project1.pk - ) - self.assertEqual(project_1_data['id'], project1.pk) - self.assertEqual(project_1_data['number_of_leads'], 5) - self.assertEqual(project_1_data['number_of_leads_tagged'], 2) - self.assertEqual(project_1_data['number_of_leads_tagged_and_controlled'], 1) - self.assertEqual(project_1_data['number_of_entries'], 9) - self.assertEqual(len(project_1_data['leads_activity']), 2) - self.assertEqual(len(project_1_data['entries_activity']), 3) - def test_project_stats_public_api(self): normal_user = self.create_user() admin_user = self.create_user() diff --git a/apps/project/views.py b/apps/project/views.py index 979de1c2c0..1da3f08e2d 100644 --- a/apps/project/views.py +++ b/apps/project/views.py @@ -1,7 +1,6 @@ import logging import uuid -from dateutil.relativedelta import relativedelta import django_filters from django.conf import settings from django.http import Http404 @@ -9,8 +8,6 @@ from django.utils import timezone from django.utils.http import urlsafe_base64_decode from django.utils.encoding import force_text -from django.contrib.postgres.fields.jsonb import KeyTextTransform -from django.db.models.functions import Cast from django.template.response import TemplateResponse from deep.permalinks import Permalink from rest_framework.exceptions import PermissionDenied @@ -35,7 +32,6 @@ IsProjectMember, ) from deep.serializers import URLCachedFileField -from deep.paginations import SmallSizeSetPagination from tabular.models import Field from user.utils import send_project_join_request_emails @@ -65,8 +61,6 @@ ) from .serializers import ( ProjectSerializer, - ProjectStatSerializer, - ProjectRoleSerializer, ProjectMembershipSerializer, ProjectJoinRequestSerializer, ProjectUserGroupSerializer, @@ -666,86 +660,6 @@ def get_analysis(self, request, pk=None, version=None): }) -class ProjectStatViewSet(ProjectViewSet): - pagination_class = SmallSizeSetPagination - - def get_serializer_class(self): - return ProjectStatSerializer - - def get_queryset(self): - return get_filtered_projects( - self.request.user, self.request.GET, - annotate=True, - ).prefetch_related( - 'regions', 'organizations', - ).select_related( - 'created_by__profile', 'modified_by__profile' - ) - - @action( - detail=False, - permission_classes=[permissions.IsAuthenticated], - url_path='recent' - ) - def get_recent_projects(self, request, *args, **kwargs): - # Only pull project data for which user is member of - qs = self.get_queryset().filter(Project.get_query_for_member(request.user)) - recent_projects = Project.get_recent_active_projects(request.user, qs) - return response.Response( - self.get_serializer_class()( - recent_projects, - context=self.get_serializer_context(), - many=True, - ).data - ) - - @action( - detail=False, - permission_classes=[permissions.IsAuthenticated], - url_path='summary' - ) - def get_projects_summary(self, request, pk=None, version=None): - projects = Project.get_for_member(request.user) - # Lead stats - leads = Lead.objects.filter(project__in=projects) - total_leads_tagged_count = leads.annotate(entries_count=models.Count('entry')).filter(entries_count__gt=0).count() - total_leads_tagged_and_controlled_count = leads.annotate( - entries_count=models.Count('entry'), - controlled_entries_count=models.Count( - 'entry', filter=models.Q(entry__controlled=True) - ), - ).filter(entries_count__gt=0, entries_count=models.F('controlled_entries_count')).count() - # Entries activity - recent_projects_id = list( - projects.annotate( - entries_count=Cast(KeyTextTransform('entries_activity', 'stats_cache'), models.IntegerField()) - ).filter(entries_count__gt=0).order_by('-entries_count').values_list('id', flat=True)[:3]) - recent_entries = Entry.objects.filter( - project__in=recent_projects_id, - created_at__gte=(timezone.now() + relativedelta(months=-3)) - ) - recent_entries_activity = { - 'projects': ( - recent_entries.order_by().values('project') - .annotate(count=models.Count('*')) - .filter(count__gt=0) - .values('count', id=models.F('project'), title=models.F('project__title')) - ), - 'activities': ( - recent_entries.order_by('project', 'created_at__date').values('project', 'created_at__date') - .annotate(count=models.Count('*')) - .values('project', 'count', date=models.Func(models.F('created_at__date'), function='DATE')) - ), - } - return response.Response({ - 'projects_count': projects.count(), - 'total_leads_count': leads.count(), - 'total_leads_tagged_count': total_leads_tagged_count, - 'total_leads_tagged_and_controlled_count': total_leads_tagged_and_controlled_count, - 'recent_entries_activity': recent_entries_activity, - }) - - class ProjectMembershipViewSet(viewsets.ModelViewSet): serializer_class = ProjectMembershipSerializer permission_classes = [permissions.IsAuthenticated, @@ -913,12 +827,6 @@ def accept_project_confirm( return TemplateResponse(request, template_name, context) -class ProjectRoleViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = ProjectRoleSerializer - permission_classes = [permissions.IsAuthenticated] - queryset = ProjectRole.objects.order_by('level') - - class ProjectUserGroupViewSet(viewsets.ModelViewSet): serializer_class = ProjectUserGroupSerializer permission_classes = [permissions.IsAuthenticated, ModifyPermission] diff --git a/deep/urls.py b/deep/urls.py index 80fe97464e..ab0b575e45 100644 --- a/deep/urls.py +++ b/deep/urls.py @@ -54,15 +54,11 @@ ProjectMembershipViewSet, ProjectUserGroupViewSet, ProjectOptionsView, - ProjectRoleViewSet, ProjectViewSet, - ProjectStatViewSet, accept_project_confirm, ) from geo.views import ( - AdminLevelViewSet, RegionCloneView, - RegionViewSet, GeoAreasLoadTriggerView, GeoJsonView, GeoBoundsView, @@ -79,8 +75,6 @@ from lead.views import ( LeadGroupViewSet, LeadViewSet, - LeadBulkDeleteViewSet, - LeadPreviewViewSet, LeadOptionsView, LeadExtractionTriggerView, LeadWebsiteFetch, @@ -114,10 +108,7 @@ EntryReviewCommentViewSet, ) from analysis_framework.views import ( - AnalysisFrameworkCloneView, AnalysisFrameworkViewSet, - PrivateAnalysisFrameworkRoleViewSet, - PublicAnalysisFrameworkRoleViewSet, AnalysisFrameworkMembershipViewSet, ExportableViewSet, FilterViewSet, @@ -175,10 +166,6 @@ get_frontend_url, graphql_docs ) -from organization.views import ( - OrganizationViewSet, - OrganizationTypeViewSet, -) from lang.views import ( LanguageViewSet, ) @@ -188,7 +175,6 @@ from notification.views import ( NotificationViewSet, - AssignmentViewSet ) from jwt_auth.views import ( @@ -200,11 +186,6 @@ RenderChart, ) -from django.conf.urls import ( - handler404 - # handler403, handler400, handler500 -) - register_converter(converters.FileNameRegex, 'filename') @@ -258,20 +239,12 @@ # Project routers router.register(r'projects', ProjectViewSet, basename='project') -router.register(r'projects-stat', ProjectStatViewSet, - basename='project-stat') -router.register(r'project-roles', ProjectRoleViewSet, - basename='project_role') router.register(r'projects/(?P\d+)/project-memberships', ProjectMembershipViewSet, basename='project_membership') router.register(r'projects/(?P\d+)/project-usergroups', ProjectUserGroupViewSet, basename='project_usergroup') # Geo routers -router.register(r'regions', RegionViewSet, - basename='region') -router.register(r'admin-levels', AdminLevelViewSet, - basename='admin_level') router.register(r'projects/(?P\d+)/geo-area', GeoAreaView, basename='geo_area') @@ -280,10 +253,6 @@ basename='lead_group') router.register(r'leads', LeadViewSet, basename='lead') -router.register(r'project/(?P\d+)/leads', LeadBulkDeleteViewSet, - basename='leads-bulk') -router.register(r'lead-previews', LeadPreviewViewSet, - basename='lead_preview') # Questionnaire routers router.register(r'questionnaires/(?P\d+)/questions', @@ -337,11 +306,6 @@ basename='analysis_framework_exportable') router.register(r'framework-memberships', AnalysisFrameworkMembershipViewSet, basename='framework_memberships') -router.register(r'private-framework-roles', PrivateAnalysisFrameworkRoleViewSet, - basename='framework_roles') -router.register(r'public-framework-roles', PublicAnalysisFrameworkRoleViewSet, - basename='framework_roles') - # Assessment registry router.register(r'assessments', AssessmentViewSet, basename='assessment') @@ -369,19 +333,12 @@ router.register(r'connector-projects', ConnectorProjectViewSet, basename='connector_projects') -# Organization routers -router.register(r'organizations', OrganizationViewSet, basename='organization') -router.register(r'organization-types', OrganizationTypeViewSet, basename='organization-type') - # Export routers router.register(r'exports', ExportViewSet, basename='export') # Notification routers router.register(r'notifications', NotificationViewSet, basename='notification') -router.register(r'assignments', - AssignmentViewSet, basename='assignments') - # Language routers router.register(r'languages', LanguageViewSet, basename='language') @@ -553,8 +510,6 @@ def get_api_path(path): # Clone apis re_path(get_api_path(r'clone-region/(?P\d+)/$'), RegionCloneView.as_view()), - re_path(get_api_path(r'clone-analysis-framework/(?P\d+)/$'), - AnalysisFrameworkCloneView.as_view()), re_path(get_api_path(r'clone-category-editor/(?P\d+)/$'), CategoryEditorCloneView.as_view()),