From 00f61aadeafb62e27a3c119348b57e5b9289d5c0 Mon Sep 17 00:00:00 2001 From: abhishek Date: Wed, 7 Feb 2024 12:57:35 +0530 Subject: [PATCH 1/6] [#1147] null administration endpoiont trigering changed value to id --- frontend/src/components/EditableCell.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/EditableCell.jsx b/frontend/src/components/EditableCell.jsx index 8c56aea51..ab36ac176 100644 --- a/frontend/src/components/EditableCell.jsx +++ b/frontend/src/components/EditableCell.jsx @@ -60,7 +60,7 @@ const EditableCell = ({ if (typeof record.value === "string") { setLocationName(record.value); } else { - config.fn.administration(record.value, false).then((res) => { + config.fn.administration(record.id, false).then((res) => { const locName = res; setLocationName(locName?.full_name); }); From 6f7c9aed5e4d23d1f91ad9c8c3ef55aed082d7b7 Mon Sep 17 00:00:00 2001 From: dedenbangkit Date: Thu, 8 Feb 2024 12:08:12 +0700 Subject: [PATCH 2/6] [#1157] Remove form approval rule endpoint, test --- backend/api/v1/v1_forms/serializers.py | 76 +-------------- .../v1/v1_forms/tests/tests_form_approval.py | 26 +---- .../v1_forms/tests/tests_form_submission.py | 95 ++++++++++--------- backend/api/v1/v1_forms/urls.py | 11 +-- backend/api/v1/v1_forms/views.py | 69 +------------- 5 files changed, 58 insertions(+), 219 deletions(-) diff --git a/backend/api/v1/v1_forms/serializers.py b/backend/api/v1/v1_forms/serializers.py index e5952c28a..b0984fbbb 100644 --- a/backend/api/v1/v1_forms/serializers.py +++ b/backend/api/v1/v1_forms/serializers.py @@ -1,22 +1,19 @@ import numpy as np from collections import OrderedDict -from django.db.models import Q from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field, inline_serializer from rest_framework import serializers from api.v1.v1_forms.constants import QuestionTypes, AttributeTypes, FormTypes from api.v1.v1_forms.models import Forms, QuestionGroup, Questions, \ - QuestionOptions, QuestionAttribute, \ - FormApprovalRule, FormApprovalAssignment + QuestionOptions, QuestionAttribute from api.v1.v1_profile.constants import UserRoleTypes -from api.v1.v1_profile.models import Administration, Levels, Entity +from api.v1.v1_profile.models import Administration, Entity from api.v1.v1_users.models import SystemUser from rtmis.settings import FORM_GEO_VALUE from utils.custom_serializer_fields import CustomChoiceField, \ - CustomPrimaryKeyRelatedField, CustomListField, \ - CustomMultipleChoiceField + CustomPrimaryKeyRelatedField, CustomMultipleChoiceField from utils.default_serializers import CommonDataSerializer, \ GeoFormatSerializer @@ -369,73 +366,6 @@ class Meta: fields = ['id', 'name', 'question_group', 'approval_instructions'] -class EditFormTypeSerializer(serializers.ModelSerializer): - form_id = CustomPrimaryKeyRelatedField(queryset=Forms.objects.none()) - type = CustomChoiceField(choices=list(FormTypes.FieldStr.keys())) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.fields.get('form_id').queryset = Forms.objects.all() - - def create(self, validated_data): - form: Forms = validated_data.get('form_id') - form.type = validated_data.get('type') - form.save() - return form - - class Meta: - model = Forms - fields = ['form_id', 'type', 'approval_instructions'] - - -class EditFormApprovalSerializer(serializers.ModelSerializer): - form_id = CustomPrimaryKeyRelatedField(queryset=Forms.objects.none(), - source='form') - level_id = CustomListField( - child=CustomPrimaryKeyRelatedField(queryset=Levels.objects.none()), - source='levels') - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.fields.get('form_id').queryset = Forms.objects.all() - self.fields.get('level_id').child.queryset = Levels.objects.all() - - def create(self, validated_data): - administration = self.context.get('user').user_access.administration - FormApprovalRule.objects.filter( - form=validated_data.get('form'), - administration=administration).delete() - - validated_data['administration'] = administration - rule: FormApprovalRule = super(EditFormApprovalSerializer, - self).create(validated_data) - if administration.path: - path = f"{administration.path}{administration.id}." - else: - path = f"{administration.id}." - - # Get descendants of current admin with selected level - descendants = list( - Administration.objects.filter( - path__startswith=path, - level_id__in=rule.levels.all().values_list( - 'id', flat=True)).values_list('id', flat=True)) - # Delete assignment for the removed levels - FormApprovalAssignment.objects.filter( - ~Q(administration_id__in=descendants), form=rule.form).delete() - return rule - - class Meta: - model = FormApprovalRule - fields = ['form_id', 'level_id'] - - -class FormApprovalLevelListSerializer(serializers.ModelSerializer): - class Meta: - model = FormApprovalRule - fields = ['form_id', 'levels'] - - class FormApproverRequestSerializer(serializers.Serializer): administration_id = CustomPrimaryKeyRelatedField( queryset=Administration.objects.none()) diff --git a/backend/api/v1/v1_forms/tests/tests_form_approval.py b/backend/api/v1/v1_forms/tests/tests_form_approval.py index 1422ec91c..3637282c0 100644 --- a/backend/api/v1/v1_forms/tests/tests_form_approval.py +++ b/backend/api/v1/v1_forms/tests/tests_form_approval.py @@ -23,30 +23,6 @@ def test_approval_endpoint(self): header = { 'HTTP_AUTHORIZATION': f'Bearer {token}' } - response = self.client.get('/api/v1/form/approval-level', - content_type='application/json', - **header) - self.assertEqual(403, response.status_code) - call_command("fake_user_seeder", "-r", 10) - admin_user = SystemUser.objects.filter( - user_access__role=UserRoleTypes.admin).first() - if admin_user: - t = RefreshToken.for_user(admin_user) - header = { - 'HTTP_AUTHORIZATION': f'Bearer {t.access_token}' - } - response = self.client.get('/api/v1/form/approval-level/', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - header = { - 'HTTP_AUTHORIZATION': f'Bearer {token}' - } - - response = self.client.get('/api/v1/form/approval-level/1', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) response = self.client.get( '/api/v1/form/approver/?administration_id=1&form_id=1', content_type='application/json', @@ -60,7 +36,7 @@ def test_approval_endpoint(self): user_access__role=UserRoleTypes.user).first() if user: user = RefreshToken.for_user(user) - token = user.access_token + header = { 'HTTP_AUTHORIZATION': f'Bearer {token}' } diff --git a/backend/api/v1/v1_forms/tests/tests_form_submission.py b/backend/api/v1/v1_forms/tests/tests_form_submission.py index 8e393cd9d..f59dab596 100644 --- a/backend/api/v1/v1_forms/tests/tests_form_submission.py +++ b/backend/api/v1/v1_forms/tests/tests_form_submission.py @@ -1,9 +1,12 @@ from django.core.management import call_command from django.test import TestCase +from django.utils import timezone from django.test.utils import override_settings from api.v1.v1_forms.models import Forms -from api.v1.v1_profile.models import Administration, Levels +from api.v1.v1_profile.models import Administration, \ + Levels, Access +from api.v1.v1_users.models import Organisation, SystemUser def seed_administration_test(): @@ -112,7 +115,8 @@ def test_form_data_endpoint(self): self.assertEqual(len(question_group), 1) self.assertEqual(question_group[0].get("name"), "Question Group 01") - def test_edit_form_type(self): + def test_edit_form_approval(self): + call_command("fake_organisation_seeder", "--repeat", 3) call_command("administration_seeder", "--test") call_command("form_seeder", "--test") user_payload = {"email": "admin@rush.com", "password": "Test105*"} @@ -121,52 +125,55 @@ def test_edit_form_type(self): content_type='application/json') user = user_response.json() token = user.get('token') - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} - - form = Forms.objects.first() - payload = [{"form_id": form.id, "type": 3}] - response = self.client.post('/api/v1/form/type', - payload, - content_type='application/json', - **header) - self.assertEqual(400, response.status_code) - - payload = [{"form_id": form.id, "type": 1}] - - response = self.client.post('/api/v1/form/type', - payload, - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(response.json().get('message'), - 'Forms updated successfully') - - def test_edit_form_approval(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") user_payload = {"email": "admin@rush.com", "password": "Test105*"} user_response = self.client.post('/api/v1/login', user_payload, content_type='application/json') + org = Organisation.objects.order_by('?').first() + form = Forms.objects.filter(type=1).first() user = user_response.json() token = user.get('token') + email = "test_approver@example.com" + admin = Administration.objects.filter( + level=Levels.objects.filter(level=3).first() + ).order_by('?').first() + payload = { + "first_name": "Test", + "last_name": "Approver", + "email": email, + "administration": admin.id, + "organisation": org.id, + "role": 3, + "forms": [form.id], + "trained": True, + } header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} - - form = Forms.objects.first() - payload = [{"form_id": form.id, "level_id": 3}] - response = self.client.put('/api/v1/form/approval', - payload, - content_type='application/json', - **header) - - self.assertEqual(400, response.status_code) - level = Levels.objects.first() - payload = [{"form_id": form.id, "level_id": [level.id]}] - - response = self.client.put('/api/v1/form/approval', - payload, - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(response.json().get('message'), - 'Forms updated successfully') + add_response = self.client.post("/api/v1/user", + payload, + content_type='application/json', + **header) + user = SystemUser.objects.filter(email=email).first() + user.set_password("Test105*") + user.updated = timezone.now() + access = Access.objects.filter(user=user).first() + self.assertEqual(access.administration, admin) + self.assertEqual(add_response.status_code, 200) + approval_response = self.client.get( + "/api/v1/form/approver/?administration_id={}&form_id={}".format( + admin.parent.id, form.id + ), + content_type='application/json', + **header) + self.assertEqual(approval_response.status_code, 200) + self.assertEqual(approval_response.json(), [{ + 'administration': { + 'id': admin.id, + 'name': admin.name, + }, + 'user': { + 'id': user.id, + 'email': email, + 'first_name': 'Test', + 'last_name': 'Approver', + } + }]) diff --git a/backend/api/v1/v1_forms/urls.py b/backend/api/v1/v1_forms/urls.py index 54dbc3d4b..3b2ac2c32 100644 --- a/backend/api/v1/v1_forms/urls.py +++ b/backend/api/v1/v1_forms/urls.py @@ -1,8 +1,7 @@ from django.urls import re_path from api.v1.v1_forms.views import web_form_details, list_form, form_data, \ - edit_form_type, edit_form_approval, check_form_approver, \ - form_approval_level, form_approval_level_administration, form_approver + check_form_approver, form_approver urlpatterns = [ @@ -12,14 +11,6 @@ re_path(r'^(?P(v1))/form/web/(?P[0-9]+)', web_form_details), re_path(r'^(?P(v1))/form/approver', form_approver), - - re_path(r'^(?P(v1))/form/approval-level/' - r'(?P[0-9]+)', - form_approval_level_administration), - re_path(r'^(?P(v1))/form/approval-level', form_approval_level), - re_path(r'^(?P(v1))/form/approval', edit_form_approval), - re_path(r'^(?P(v1))/form/type', edit_form_type), - re_path(r'^(?P(v1))/form/check-approver/(?P[0-9]+)', check_form_approver), ] diff --git a/backend/api/v1/v1_forms/views.py b/backend/api/v1/v1_forms/views.py index 10f1f0aad..14bc8e16d 100644 --- a/backend/api/v1/v1_forms/views.py +++ b/backend/api/v1/v1_forms/views.py @@ -10,18 +10,16 @@ from rest_framework.response import Response from api.v1.v1_forms.constants import FormTypes -from api.v1.v1_forms.models import Forms, FormApprovalRule, \ +from api.v1.v1_forms.models import Forms, \ FormApprovalAssignment from api.v1.v1_forms.serializers import ListFormSerializer, \ WebFormDetailSerializer, FormDataSerializer, ListFormRequestSerializer, \ - EditFormTypeSerializer, EditFormApprovalSerializer, \ - FormApprovalLevelListSerializer, FormApproverRequestSerializer, \ + FormApproverRequestSerializer, \ FormApproverResponseSerializer from api.v1.v1_profile.models import Administration from api.v1.v1_data.functions import get_cache, create_cache from utils.custom_permissions import IsSuperAdmin, IsAdmin from utils.custom_serializer_fields import validate_serializers_message -from utils.default_serializers import DefaultResponseSerializer @extend_schema(responses={200: ListFormSerializer(many=True)}, @@ -84,69 +82,6 @@ def form_data(request, version, form_id): return Response(instance, status=status.HTTP_200_OK) -@extend_schema(request=EditFormTypeSerializer(many=True), - responses={200: DefaultResponseSerializer}, - tags=['Form'], - summary='To update the form type') -@api_view(['POST']) -@permission_classes([IsAuthenticated, IsSuperAdmin]) -def edit_form_type(request, version): - serializer = EditFormTypeSerializer(data=request.data, many=True) - if not serializer.is_valid(): - return Response( - {'message': validate_serializers_message(serializer.errors)}, - status=status.HTTP_400_BAD_REQUEST) - serializer.save() - return Response({'message': 'Forms updated successfully'}, - status=status.HTTP_200_OK) - - -@extend_schema(request=EditFormApprovalSerializer(many=True), - responses={200: DefaultResponseSerializer}, - tags=['Form'], - summary='To update form approval rule levels') -@api_view(['PUT']) -@permission_classes([IsAuthenticated, IsSuperAdmin | IsAdmin]) -def edit_form_approval(request, version): - serializer = EditFormApprovalSerializer(data=request.data, - many=True, - context={'user': request.user}) - if not serializer.is_valid(): - return Response( - {'message': validate_serializers_message(serializer.errors)}, - status=status.HTTP_400_BAD_REQUEST) - serializer.save() - return Response({'message': 'Forms updated successfully'}, - status=status.HTTP_200_OK) - - -@extend_schema(responses={200: FormApprovalLevelListSerializer(many=True)}, - tags=['Form'], - summary='To check the approval level assigned to fom') -@api_view(['GET']) -@permission_classes([IsAuthenticated, IsAdmin]) -def form_approval_level(request, version): - instance = FormApprovalRule.objects.filter( - administration=request.user.user_access.administration) - return Response(FormApprovalLevelListSerializer(instance=instance, - many=True).data, - status=status.HTTP_200_OK) - - -@extend_schema(responses={200: FormApprovalLevelListSerializer(many=True)}, - tags=['Form'], - summary='SuperAdmin: To check the approval level assigned' - ' to fom by administration') -@api_view(['GET']) -@permission_classes([IsAuthenticated, IsSuperAdmin]) -def form_approval_level_administration(request, version, administration_id): - administration = get_object_or_404(Administration, pk=administration_id) - instance = FormApprovalRule.objects.filter(administration=administration) - return Response(FormApprovalLevelListSerializer(instance=instance, - many=True).data, - status=status.HTTP_200_OK) - - @extend_schema( parameters=[ OpenApiParameter( From bc9bc936ea8795382eb00ee3b4ed151a6ac9a5fc Mon Sep 17 00:00:00 2001 From: dedenbangkit Date: Thu, 8 Feb 2024 12:55:09 +0700 Subject: [PATCH 3/6] [#1157] Fix demo approval workflow --- .../management/commands/demo_approval_flow.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/api/v1/v1_users/management/commands/demo_approval_flow.py b/backend/api/v1/v1_users/management/commands/demo_approval_flow.py index 2af1013e6..d514dada2 100644 --- a/backend/api/v1/v1_users/management/commands/demo_approval_flow.py +++ b/backend/api/v1/v1_users/management/commands/demo_approval_flow.py @@ -1,16 +1,16 @@ import re +import random from django.core.management import BaseCommand from api.v1.v1_profile.constants import UserRoleTypes from api.v1.v1_profile.models import Administration, Access, Levels from api.v1.v1_users.models import SystemUser, Organisation from api.v1.v1_forms.models import Forms, UserForms -from api.v1.v1_forms.models import FormApprovalRule, FormApprovalAssignment +from api.v1.v1_forms.models import FormApprovalAssignment from api.v1.v1_forms.constants import FormTypes class Command(BaseCommand): def handle(self, *args, **options): - FormApprovalRule.objects.all().delete() form = Forms.objects.filter( type=FormTypes.county).order_by('?').first() print(f"\nForm Name: {form.name}\n\n") @@ -19,14 +19,6 @@ def handle(self, *args, **options): administration = Administration.objects.filter( level=last_level).order_by('?').first() ancestors = administration.ancestors - # check if approval level rule are available - first_level = ancestors.filter(level__level=1).first() - approval_rule, created = FormApprovalRule.objects.get_or_create( - form=form, administration=first_level) - if created: - approval_rule.save() - approval_rule.levels.set(Levels.objects.filter( - level__gte=1, level__lt=4)) # union the current administration also ancestors |= Administration.objects.filter(id=administration.id) print("Approvers:") @@ -36,9 +28,11 @@ def handle(self, *args, **options): assignment = FormApprovalAssignment.objects.filter( form=form, administration=ancestor).first() if not assignment: - email = ("{}{}@test.com").format( + email = ("{}{}.{}@test.com").format( re.sub('[^A-Za-z0-9]+', '', ancestor.name.lower()), - ancestor.id) + ancestor.id, + random.randint(1, 1000) + ) last_name = "Approver" role = UserRoleTypes.approver if ancestor.level.level == 1: @@ -64,6 +58,12 @@ def handle(self, *args, **options): print(f"- Administration Name: {ancestor.name}") print("- Approver: {} ({})".format(assignment.user.email, last_name)) + else: + print("Level: {} ({})".format(ancestor.level.level, + ancestor.level.name)) + print(f"- Administration Name: {ancestor.name}") + print("- Approver: {} ({})".format(assignment.user.email, + assignment.user.last_name)) # create user email = ("{}{}@user.com").format( re.sub('[^A-Za-z0-9]+', '', administration.name.lower()), From e080725e4eb07673518f50bf42492a41d2044144 Mon Sep 17 00:00:00 2001 From: dedenbangkit Date: Thu, 8 Feb 2024 14:39:03 +0700 Subject: [PATCH 4/6] =?UTF-8?q?[#1157]=20=F0=9F=94=A5=20=F0=9F=94=A5=20?= =?UTF-8?q?=F0=9F=94=A5=20Remove=20Unused=20ApprovalRule=20Models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commands/fake_pending_data_seeder.py | 25 +- .../form_approval_assignment_seeder.py | 59 ---- .../v1/v1_data/tests/tests_pending_data.py | 297 ++++++++---------- .../commands/form_approval_seeder.py | 38 --- .../0026_delete_formapprovalrule.py | 16 + backend/api/v1/v1_forms/models.py | 18 -- .../v1/v1_forms/tests/tests_form_approval.py | 119 +++++-- .../v1_forms/tests/tests_form_submission.py | 111 +------ backend/api/v1/v1_forms/views.py | 6 +- .../v1_users/tests/tests_user_invitation.py | 152 +++------ backend/seeder.sh | 2 - 11 files changed, 310 insertions(+), 533 deletions(-) delete mode 100644 backend/api/v1/v1_data/management/commands/form_approval_assignment_seeder.py delete mode 100644 backend/api/v1/v1_forms/management/commands/form_approval_seeder.py create mode 100644 backend/api/v1/v1_forms/migrations/0026_delete_formapprovalrule.py diff --git a/backend/api/v1/v1_data/management/commands/fake_pending_data_seeder.py b/backend/api/v1/v1_data/management/commands/fake_pending_data_seeder.py index 8d27793f4..75c352b8a 100644 --- a/backend/api/v1/v1_data/management/commands/fake_pending_data_seeder.py +++ b/backend/api/v1/v1_data/management/commands/fake_pending_data_seeder.py @@ -1,5 +1,4 @@ import re -import random from datetime import timedelta import pandas as pd @@ -10,7 +9,7 @@ from api.v1.v1_data.models import PendingFormData, \ PendingAnswers, PendingDataApproval, PendingDataBatch from api.v1.v1_forms.constants import QuestionTypes, FormTypes -from api.v1.v1_forms.models import FormApprovalRule, FormApprovalAssignment +from api.v1.v1_forms.models import FormApprovalAssignment from api.v1.v1_forms.models import Forms, UserForms from api.v1.v1_profile.constants import UserRoleTypes from api.v1.v1_profile.management.commands.administration_seeder import ( @@ -129,22 +128,10 @@ def assign_batch_for_approval(batch, user, test): administration = user.user_access.administration complete_path = '{0}{1}'.format(administration.path, administration.id) complete_path = complete_path.split('.')[1:] - approval_rule = FormApprovalRule.objects.filter( - administration_id=complete_path[0], form=batch.form).first() - levels = None - if approval_rule: - levels = approval_rule.levels.all() - if not approval_rule: - randoms = Levels.objects.filter(level__gt=1).count() - randoms = [n + 1 for n in range(randoms)] - limit = random.choices(randoms) - levels = Levels.objects.filter(level__gt=1).order_by('?')[:limit[0]] - levels |= Levels.objects.filter(level=1) - rule = FormApprovalRule.objects.create( - form=batch.form, - administration=Administration.objects.filter( - id=complete_path[0]).first()) - rule.levels.set(levels) + randoms = Levels.objects.filter(level__gt=1).count() + randoms = [n + 1 for n in range(randoms)] + levels = Levels.objects.filter( + level__lte=MAX_LEVEL_IN_SOURCE_FILE).order_by('-level').all() administrations = Administration.objects.filter(id__in=complete_path, level__in=levels).all() for administration in administrations: @@ -229,7 +216,7 @@ def handle(self, *args, **options): PendingDataBatch.objects.all().delete() PendingFormData.objects.all().delete() fake_geo = pd.read_csv("./source/kenya_random_points.csv") - forms = Forms.objects.all() + forms = Forms.objects.filter(type=FormTypes.county).all() user = None if options.get('email'): # if user type is 'user' -> seed county form only diff --git a/backend/api/v1/v1_data/management/commands/form_approval_assignment_seeder.py b/backend/api/v1/v1_data/management/commands/form_approval_assignment_seeder.py deleted file mode 100644 index 64a123048..000000000 --- a/backend/api/v1/v1_data/management/commands/form_approval_assignment_seeder.py +++ /dev/null @@ -1,59 +0,0 @@ -from django.core.management import BaseCommand -from faker import Faker - -from api.v1.v1_forms.constants import FormTypes -from api.v1.v1_forms.models import FormApprovalRule, FormApprovalAssignment -from api.v1.v1_profile.constants import UserRoleTypes -from api.v1.v1_profile.models import Administration, Access -from api.v1.v1_users.models import SystemUser, Organisation - - -class Command(BaseCommand): - def handle(self, *args, **options): - FormApprovalAssignment.objects.all().delete() - organisation = Organisation.objects.first() - for rule in FormApprovalRule.objects.all(): - if rule.form.type == FormTypes.county: - previous = None - for level in rule.levels.all().order_by('level'): - if previous: - filter_path = '{0}{1}.'.format(previous.path, - previous.id) - else: - filter_path = '{0}'.format(rule.administration.path) - administration = Administration.objects.filter( - path__startswith=filter_path, level=level).first() - previous = administration - user = SystemUser.objects.filter( - user_access__administration=administration, - user_access__role__in=[ - UserRoleTypes.approver, UserRoleTypes.admin - ]).first() - if not user: - fake = Faker() - profile = fake.profile() - name = profile.get("name").split(" ") - user = SystemUser.objects.create_user( - organisation=organisation, - email=profile.get("mail"), - first_name=name[0], - last_name=name[1]) - user.set_password('Test105*') - user.save() - role = UserRoleTypes.approver - if administration.level.level == 1: - role = UserRoleTypes.admin - Access.objects.create(user=user, - role=role, - administration=administration) - FormApprovalAssignment.objects.create( - form=rule.form, - administration=administration, - user=user) - else: - user = SystemUser.objects.filter( - user_access__administration=rule.administration).first() - FormApprovalAssignment.objects.create( - form=rule.form, - administration=user.user_access.administration, - user=user) diff --git a/backend/api/v1/v1_data/tests/tests_pending_data.py b/backend/api/v1/v1_data/tests/tests_pending_data.py index 0e0346d76..5494f6f79 100644 --- a/backend/api/v1/v1_data/tests/tests_pending_data.py +++ b/backend/api/v1/v1_data/tests/tests_pending_data.py @@ -18,18 +18,18 @@ @override_settings(USE_TZ=False) class PendingDataTestCase(TestCase): - def tests_pending_data(self): + def setUp(self) -> None: call_command("administration_seeder", "--test") call_command("form_seeder", "--test") - super_admin = {"email": "admin@rush.com", "password": "Test105*"} - self.client.post('/api/v1/login', - super_admin, - content_type='application/json') + admin_payload = {"email": "admin@rush.com", "password": "Test105*"} + user_response = self.client.post('/api/v1/login', + admin_payload, + content_type='application/json') + self.token = user_response.json().get('token') + call_command('demo_approval_flow') - call_command("fake_user_seeder", "-r", 100) - call_command('form_approval_seeder') - call_command('form_approval_assignment_seeder') + def tests_pending_data(self): call_command('fake_pending_data_seeder', '-r', 1, '-t', True, '-b', 1) admin_user = SystemUser.objects.filter( @@ -115,167 +115,148 @@ def tests_pending_data(self): ]) def test_pending_batch_list(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") - - super_admin = {"email": "admin@rush.com", "password": "Test105*"} - self.client.post('/api/v1/login', - super_admin, - content_type='application/json') - - call_command("fake_user_seeder", "-r", 100) - call_command('form_approval_seeder') - call_command('form_approval_assignment_seeder') call_command('fake_pending_data_seeder', '-r', 5, '-t', True, '-b', 5) # get the lowest level approver approval: Union[PendingDataApproval, None] = PendingDataApproval\ .objects.filter(level__level=MAX_LEVEL_IN_SOURCE_FILE)\ .first() - if approval: - t_child = RefreshToken.for_user(approval.user) - header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} - # subordinate = false, approved = false - response = self.client.get('/api/v1/form-pending-batch?page=1', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual( - response.json().get('batch')[0]['approver']['status'], - DataApprovalStatus.pending) - self.assertEqual( - response.json().get('batch')[0]['approver']['allow_approve'], - True) - self.assertIn( - 'approval_instructions', - response.json().get('batch')[0]['form']) - # subordinate = true - response = self.client.get( - '/api/v1/form-pending-batch?page=1&subordinate=true', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(0, len(response.json().get('batch'))) - - # get parent level user - p_approval = PendingDataApproval.objects.filter( - batch_id=approval.batch_id, - level__level__lt=approval.level.level).order_by( - '-level__level').first() - t_parent = RefreshToken.for_user(p_approval.user) - header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} - # subordinate = false, approved = false - response = self.client.get('/api/v1/form-pending-batch?page=1', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(0, len(response.json().get('batch'))) - # subordinate = true - response = self.client.get( - '/api/v1/form-pending-batch?page=1&subordinate=true', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertGreaterEqual(len(response.json().get('batch')), 1) - - # approve data with child - payload = { - 'batch': approval.batch_id, - 'status': DataApprovalStatus.approved, - 'comment': 'Approved comment' - } - header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} - response = self.client.post('/api/v1/pending-data/approve', - payload, - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - # approved = true - response = self.client.get( - '/api/v1/form-pending-batch?page=1&approved=true', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertGreaterEqual(len(response.json().get('batch')), 1) + t_child = RefreshToken.for_user(approval.user) + header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} + # subordinate = false, approved = false + response = self.client.get('/api/v1/form-pending-batch?page=1', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertEqual( + response.json().get('batch')[0]['approver']['status'], + DataApprovalStatus.pending) + self.assertFalse( + response.json().get('batch')[0]['approver']['allow_approve'], + "Should not allow approve") + self.assertIn( + 'approval_instructions', + response.json().get('batch')[0]['form']) + # subordinate = true + response = self.client.get( + '/api/v1/form-pending-batch?page=1&subordinate=true', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertEqual(0, len(response.json().get('batch'))) + + # get parent level user + p_approval = PendingDataApproval.objects.filter( + batch_id=approval.batch_id, + level__level__lt=approval.level.level).order_by( + '-level__level').first() + t_parent = RefreshToken.for_user(p_approval.user) + header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} + # subordinate = false, approved = false + response = self.client.get( + '/api/v1/form-pending-batch?page=1', + content_type='application/json', + **header + ) + self.assertEqual(200, response.status_code) + self.assertEqual(0, len(response.json().get('batch'))) + # subordinate = true + response = self.client.get( + '/api/v1/form-pending-batch?page=1&subordinate=true', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertGreaterEqual(len(response.json().get('batch')), 1) - # subordinate = false, approved = false - header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} - response = self.client.get('/api/v1/form-pending-batch?page=1', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertGreaterEqual(len(response.json().get('batch')), 1) - - # reject data with child - payload = { - 'batch': approval.batch_id, - 'status': DataApprovalStatus.rejected, - 'comment': 'Rejected' - } - header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} - response = self.client.post('/api/v1/pending-data/approve', - payload, - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) + # approve data with child + payload = { + 'batch': approval.batch_id, + 'status': DataApprovalStatus.approved, + 'comment': 'Approved comment' + } + header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} + response = self.client.post('/api/v1/pending-data/approve', + payload, + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + # approved = true + response = self.client.get( + '/api/v1/form-pending-batch?page=1&approved=true', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertGreaterEqual(len(response.json().get('batch')), 1) - # check rejected in list. subordinate = true, approved = false - header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} - response = self.client.get( - '/api/v1/form-pending-batch?page=1&subordinate=true', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertGreaterEqual(len(response.json().get('batch')), 1) - status = response.json().get('batch')[0].get('approver').get( - 'status') - self.assertEqual(DataApprovalStatus.rejected, status) - - # update rejected data - batch_id = response.json().get('batch')[0]['id'] - pending_data = PendingFormData.objects.filter( - batch=batch_id).first() - question = Questions.objects.filter( - form=pending_data.form.id, type=QuestionTypes.text).first() - payload = [{ - "question": question.id, - "value": "Update after rejection" - }] - data = self.client.put( - '/api/v1/form-pending-data/{0}?pending_data_id={1}' - .format(pending_data.form.id, pending_data.id), - payload, - content_type='application/json', - **header) - self.assertEqual(data.status_code, 200) - data = data.json() - self.assertEqual(data, {"message": "update success"}) - - # check pending in list. subordinate = true, approved = false - header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} - response = self.client.get( - '/api/v1/form-pending-batch?page=1&subordinate=true', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertGreaterEqual(len(response.json().get('batch')), 1) - status = response.json().get('batch')[0].get('approver').get( - 'status') - self.assertEqual(DataApprovalStatus.pending, status) + # subordinate = false, approved = false + header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} + response = self.client.get( + '/api/v1/form-pending-batch?page=1', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertGreaterEqual(len(response.json().get('batch')), 1) + + # reject data with child + payload = { + 'batch': approval.batch_id, + 'status': DataApprovalStatus.rejected, + 'comment': 'Rejected' + } + header = {'HTTP_AUTHORIZATION': f'Bearer {t_child.access_token}'} + response = self.client.post('/api/v1/pending-data/approve', + payload, + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + + # check rejected in list. subordinate = true, approved = false + header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} + response = self.client.get( + '/api/v1/form-pending-batch?page=1&subordinate=true', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertGreaterEqual(len(response.json().get('batch')), 1) + status = response.json().get('batch')[0].get('approver').get( + 'status') + self.assertEqual(DataApprovalStatus.pending, status) + + # update rejected data + batch_id = response.json().get('batch')[0]['id'] + pending_data = PendingFormData.objects.filter( + batch=batch_id).first() + question = Questions.objects.filter( + form=pending_data.form.id, type=QuestionTypes.text).first() + payload = [{ + "question": question.id, + "value": "Update after rejection" + }] + data = self.client.put( + '/api/v1/form-pending-data/{0}?pending_data_id={1}' + .format(pending_data.form.id, pending_data.id), + payload, + content_type='application/json', + **header) + self.assertEqual(data.status_code, 200) + data = data.json() + self.assertEqual(data, {"message": "update success"}) + + # check pending in list. subordinate = true, approved = false + header = {'HTTP_AUTHORIZATION': f'Bearer {t_parent.access_token}'} + response = self.client.get( + '/api/v1/form-pending-batch?page=1&subordinate=true', + content_type='application/json', + **header) + self.assertEqual(200, response.status_code) + self.assertGreaterEqual(len(response.json().get('batch')), 1) + status = response.json().get('batch')[0].get('approver').get( + 'status') + self.assertEqual(DataApprovalStatus.pending, status) def test_batch_summary(self): - call_command("administration_seeder", "--test") - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - call_command("form_seeder", "--test") - call_command("fake_user_seeder", "-r", 100) - call_command('form_approval_seeder') - call_command('form_approval_assignment_seeder') call_command('fake_pending_data_seeder', '-r', 5, '-t', True, '-b', 5) - token = user_response.json().get('token') - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} + header = {'HTTP_AUTHORIZATION': f'Bearer {self.token}'} response = self.client.get('/api/v1/batch/summary/{0}'.format( PendingDataBatch.objects.first().id), follow=True, diff --git a/backend/api/v1/v1_forms/management/commands/form_approval_seeder.py b/backend/api/v1/v1_forms/management/commands/form_approval_seeder.py deleted file mode 100644 index 9462a8b03..000000000 --- a/backend/api/v1/v1_forms/management/commands/form_approval_seeder.py +++ /dev/null @@ -1,38 +0,0 @@ -import random - -from django.core.management import BaseCommand - -from api.v1.v1_forms.constants import FormTypes -from api.v1.v1_forms.models import Forms, FormApprovalRule -from api.v1.v1_profile.models import Access, Levels - - -class Command(BaseCommand): - def handle(self, *args, **options): - FormApprovalRule.objects.all().delete() - for form in Forms.objects.all(): - if form.type == FormTypes.county: - for user in Access.objects.filter( - administration__level=Levels.objects.filter( - level=1).first()).distinct('administration_id'): - # only use levels 2 and 3 - randoms = Levels.objects.filter( - level__gt=1, level__lt=4).count() - randoms = [n + 1 for n in range(randoms)] - limit = random.choices(randoms) - # only use levels 2 and 3 - levels = Levels.objects.filter( - level__gt=1, level__lt=4).order_by('?')[:limit[0]] - levels |= Levels.objects.filter(level=1) - rule = FormApprovalRule.objects.create( - form=form, administration=user.administration) - rule.levels.set(levels) - rule.save() - else: - for user in Access.objects.filter( - administration__level=Levels.objects.filter( - level=0).first()).distinct('administration_id'): - rule = FormApprovalRule.objects.create( - form=form, administration=user.administration) - rule.levels.set(Levels.objects.filter(level=0)) - rule.save() diff --git a/backend/api/v1/v1_forms/migrations/0026_delete_formapprovalrule.py b/backend/api/v1/v1_forms/migrations/0026_delete_formapprovalrule.py new file mode 100644 index 000000000..fba4b5e11 --- /dev/null +++ b/backend/api/v1/v1_forms/migrations/0026_delete_formapprovalrule.py @@ -0,0 +1,16 @@ +# Generated by Django 4.0.4 on 2024-02-08 07:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('v1_forms', '0025_questions_meta_uuid'), + ] + + operations = [ + migrations.DeleteModel( + name='FormApprovalRule', + ), + ] diff --git a/backend/api/v1/v1_forms/models.py b/backend/api/v1/v1_forms/models.py index 4406e3f01..218930f54 100644 --- a/backend/api/v1/v1_forms/models.py +++ b/backend/api/v1/v1_forms/models.py @@ -25,24 +25,6 @@ class Meta: db_table = 'form' -class FormApprovalRule(models.Model): - form = models.ForeignKey(to=Forms, - on_delete=models.CASCADE, - related_name='form_form_approval_rule') - administration = models.ForeignKey( - to=Administration, - on_delete=models.PROTECT, - related_name='administration_form_approval') # noqa - levels = models.ManyToManyField(to=Levels, - related_name='levels_form_approval') - - def __str__(self): - return self.form.name - - class Meta: - db_table = 'form_approval_rule' - - class FormApprovalAssignment(models.Model): form = models.ForeignKey(to=Forms, on_delete=models.CASCADE, diff --git a/backend/api/v1/v1_forms/tests/tests_form_approval.py b/backend/api/v1/v1_forms/tests/tests_form_approval.py index 3637282c0..c44b29f2a 100644 --- a/backend/api/v1/v1_forms/tests/tests_form_approval.py +++ b/backend/api/v1/v1_forms/tests/tests_form_approval.py @@ -1,48 +1,99 @@ +from django.utils import timezone from django.core.management import call_command from django.test import TestCase from django.test.utils import override_settings -from rest_framework_simplejwt.tokens import RefreshToken - +from api.v1.v1_forms.models import Forms from api.v1.v1_profile.constants import UserRoleTypes -from api.v1.v1_users.models import SystemUser +from api.v1.v1_profile.models import Administration, Levels, Access +from api.v1.v1_users.models import SystemUser, Organisation @override_settings(USE_TZ=False) class FormApprovalTestCase(TestCase): - - def test_approval_endpoint(self): + def setUp(self): call_command("form_seeder", "--test") call_command("administration_seeder", "--test") - call_command("form_approval_seeder") - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - user = user_response.json() - token = user.get('token') - header = { - 'HTTP_AUTHORIZATION': f'Bearer {token}' + call_command("fake_organisation_seeder", "--repeat", 3) + user = {"email": "admin@rush.com", "password": "Test105*"} + user = self.client.post('/api/v1/login', + user, + content_type='application/json') + user = user.json() + token = user.get("token") + self.header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} + self.org = Organisation.objects.order_by('?').first() + # call_command("demo_approval_flow") + self.form = Forms.objects.filter(type=1).first() + + def test_add_approval(self): + email = "test_approver@example.com" + admin = Administration.objects.filter( + level=Levels.objects.filter(level=3).first() + ).order_by('?').first() + payload = { + "first_name": "Test", + "last_name": "Approver", + "email": email, + "administration": admin.id, + "organisation": self.org.id, + "role": UserRoleTypes.approver, + "forms": [self.form.id], + "trained": True, } - response = self.client.get( - '/api/v1/form/approver/?administration_id=1&form_id=1', + add_response = self.client.post("/api/v1/user", + payload, + content_type='application/json', + **self.header) + user = SystemUser.objects.filter(email=email).first() + user.set_password("Test105*") + user.updated = timezone.now() + user.date_joined = timezone.now() + user.save() + access = Access.objects.filter(user=user).first() + self.assertEqual(access.administration, admin) + self.assertEqual(add_response.status_code, 200) + approval_response = self.client.get( + "/api/v1/form/approver/?administration_id={}&form_id={}".format( + admin.parent.id, self.form.id + ), content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(list(response.json()[0]), - ['user', 'administration']) + **self.header) + self.assertEqual(approval_response.status_code, 200) + self.assertEqual(approval_response.json(), [{ + 'administration': { + 'id': admin.id, + 'name': admin.name, + }, + 'user': { + 'id': user.id, + 'email': email, + 'first_name': 'Test', + 'last_name': 'Approver', + } + }]) - # check form approval endpoint - user = SystemUser.objects.filter( - user_access__role=UserRoleTypes.user).first() - if user: - user = RefreshToken.for_user(user) + # check approver + response = self.client.get( + f"/api/v1/form/check-approver/{self.form.id}", + content_type='application/json', + **self.header) + self.assertEqual(404, response.status_code) + self.assertEqual( + response.json(), + {'message': 'National level does not have an approver'} + ) - header = { - 'HTTP_AUTHORIZATION': f'Bearer {token}' - } - response = self.client.get( - '/api/v1/form/check-approver/1', - content_type='application/json', - **header) - self.assertEqual(200, response.status_code) - self.assertEqual(list(response.json()), ['count']) + # check approver + login_as_approver = self.client.post( + '/api/v1/login', + {"email": email, "password": "Test105*"}, + content_type='application/json') + login_as_approver = login_as_approver.json() + approver_token = login_as_approver.get("token") + approver_header = {'HTTP_AUTHORIZATION': f'Bearer {approver_token}'} + response = self.client.get( + f"/api/v1/form/check-approver/{self.form.id}", + content_type='application/json', + **approver_header) + self.assertEqual(200, response.status_code) + self.assertEqual(list(response.json()), ['count']) diff --git a/backend/api/v1/v1_forms/tests/tests_form_submission.py b/backend/api/v1/v1_forms/tests/tests_form_submission.py index f59dab596..30f3dbd64 100644 --- a/backend/api/v1/v1_forms/tests/tests_form_submission.py +++ b/backend/api/v1/v1_forms/tests/tests_form_submission.py @@ -1,34 +1,16 @@ from django.core.management import call_command from django.test import TestCase -from django.utils import timezone from django.test.utils import override_settings from api.v1.v1_forms.models import Forms -from api.v1.v1_profile.models import Administration, \ - Levels, Access -from api.v1.v1_users.models import Organisation, SystemUser - - -def seed_administration_test(): - level = Levels(name="country", level=1) - level.save() - administration = Administration(id=1, - name="Indonesia", - parent=None, - level=level) - administration.save() - administration = Administration(id=2, - name="Jakarta", - parent=administration, - level=level) - administration.save() +from api.v1.v1_users.models import Organisation @override_settings(USE_TZ=False) class FormSubmissionTestCase(TestCase): - def test_webform_endpoint(self): - self.maxDiff = None - seed_administration_test() + def setUp(self): + call_command("administration_seeder", "--test") + call_command("fake_organisation_seeder", "--repeat", 3) call_command("form_seeder", "--test") user = {"email": "admin@rush.com", "password": "Test105*"} user = self.client.post('/api/v1/login', @@ -36,11 +18,14 @@ def test_webform_endpoint(self): content_type='application/json') user = user.json() token = user.get("token") - self.assertTrue(token) + self.header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} + self.org = Organisation.objects.order_by('?').first() + + def test_webform_endpoint(self): webform = self.client.get("/api/v1/form/web/1", follow=True, content_type='application/json', - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) webform = webform.json() self.assertEqual(webform.get("name"), "Test Form") question_group = webform.get("question_group") @@ -48,14 +33,6 @@ def test_webform_endpoint(self): self.assertEqual(question_group[0].get("name"), "Question Group 01") def test_create_new_submission(self): - self.maxDiff = None - seed_administration_test() - user = {"email": "admin@rush.com", "password": "Test105*"} - user = self.client.post('/api/v1/login', - user, - content_type='application/json') - user = user.json() - call_command("form_seeder", "--test") form = Forms.objects.first() self.assertEqual(form.name, "Test Form") payload = { @@ -84,12 +61,10 @@ def test_create_new_submission(self): "value": ["Parent", "Children"] }] } - token = user.get("token") - self.assertTrue(token) data = self.client.post('/api/v1/form-data/1/', payload, content_type='application/json', - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) self.assertEqual(data.status_code, 200) data = data.json() self.assertEqual(data, {"message": "ok"}) @@ -97,7 +72,7 @@ def test_create_new_submission(self): data = self.client.post('/api/v1/form-data/1/', payload, content_type='application/json', - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) self.assertEqual(data.status_code, 400) data = data.json() self.assertEqual( @@ -106,8 +81,6 @@ def test_create_new_submission(self): "|Value is required for Question:101") def test_form_data_endpoint(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") webform = self.client.get("/api/v1/form/1", follow=True) webform = webform.json() self.assertEqual(webform.get("name"), "Test Form") @@ -115,65 +88,3 @@ def test_form_data_endpoint(self): self.assertEqual(len(question_group), 1) self.assertEqual(question_group[0].get("name"), "Question Group 01") - def test_edit_form_approval(self): - call_command("fake_organisation_seeder", "--repeat", 3) - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - user = user_response.json() - token = user.get('token') - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - org = Organisation.objects.order_by('?').first() - form = Forms.objects.filter(type=1).first() - user = user_response.json() - token = user.get('token') - email = "test_approver@example.com" - admin = Administration.objects.filter( - level=Levels.objects.filter(level=3).first() - ).order_by('?').first() - payload = { - "first_name": "Test", - "last_name": "Approver", - "email": email, - "administration": admin.id, - "organisation": org.id, - "role": 3, - "forms": [form.id], - "trained": True, - } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} - add_response = self.client.post("/api/v1/user", - payload, - content_type='application/json', - **header) - user = SystemUser.objects.filter(email=email).first() - user.set_password("Test105*") - user.updated = timezone.now() - access = Access.objects.filter(user=user).first() - self.assertEqual(access.administration, admin) - self.assertEqual(add_response.status_code, 200) - approval_response = self.client.get( - "/api/v1/form/approver/?administration_id={}&form_id={}".format( - admin.parent.id, form.id - ), - content_type='application/json', - **header) - self.assertEqual(approval_response.status_code, 200) - self.assertEqual(approval_response.json(), [{ - 'administration': { - 'id': admin.id, - 'name': admin.name, - }, - 'user': { - 'id': user.id, - 'email': email, - 'first_name': 'Test', - 'last_name': 'Approver', - } - }]) diff --git a/backend/api/v1/v1_forms/views.py b/backend/api/v1/v1_forms/views.py index 14bc8e16d..ec4576e08 100644 --- a/backend/api/v1/v1_forms/views.py +++ b/backend/api/v1/v1_forms/views.py @@ -127,8 +127,10 @@ def check_form_approver(request, form_id, version): form = get_object_or_404(Forms, pk=form_id) # find administration id from logged in user if not request.user.user_access.administration.path: - return Response({'message': 'National level does not have approver'}, - status=status.HTTP_404_NOT_FOUND) + return Response( + {'message': 'National level does not have an approver'}, + status=status.HTTP_404_NOT_FOUND + ) adm_ids = request.user.user_access.administration.path[:-1].split('.') adm_ids += [request.user.user_access.administration_id] adm_ids = [int(adm) for adm in adm_ids] diff --git a/backend/api/v1/v1_users/tests/tests_user_invitation.py b/backend/api/v1/v1_users/tests/tests_user_invitation.py index 8bdfd9d4c..8361780ad 100644 --- a/backend/api/v1/v1_users/tests/tests_user_invitation.py +++ b/backend/api/v1/v1_users/tests/tests_user_invitation.py @@ -14,20 +14,24 @@ @override_settings(USE_TZ=False) class UserInvitationTestCase(TestCase): - def test_user_list(self): + def setUp(self): call_command("administration_seeder", "--test") call_command("fake_organisation_seeder") + call_command("form_seeder", "--test") user_payload = {"email": "admin@rush.com", "password": "Test105*"} user_response = self.client.post('/api/v1/login', user_payload, content_type='application/json') user = user_response.json() - token = user.get('token') + self.token = user.get('token') + self.header = {'HTTP_AUTHORIZATION': f'Bearer {self.token}'} + self.org = Organisation.objects.order_by('?').first() + + def test_user_list(self): response = self.client.get("/api/v1/users?administration=1&role=1", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) users = response.json() - self.assertEqual(response.status_code, 200) self.assertEqual(users['data'][0]['first_name'], 'Admin') self.assertEqual(users['data'][0]['last_name'], 'RUSH') @@ -45,7 +49,7 @@ def test_user_list(self): call_command("fake_user_seeder", "-r", 100) response = self.client.get("/api/v1/users?page=3", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) users = response.json() self.assertEqual(len(users['data']), 10) self.assertEqual([ @@ -65,21 +69,21 @@ def test_user_list(self): ], list(users['data'][0])) response = self.client.get("/api/v1/users?pending=true", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) self.assertGreater(len(response.json().get('data')), 0) self.assertEqual(response.status_code, 200) # test trained filter response = self.client.get("/api/v1/users?trained=true", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json().get('data')), 0) # search by fullname response = self.client.get("/api/v1/users?search=admin rush", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) users = response.json() self.assertEqual(response.status_code, 200) self.assertEqual(users['data'][0]['email'], 'admin@rush.com') @@ -88,7 +92,7 @@ def test_user_list(self): # search by email response = self.client.get("/api/v1/users?search=admin@rush", follow=True, - **{'HTTP_AUTHORIZATION': f'Bearer {token}'}) + **self.header) users = response.json() self.assertEqual(response.status_code, 200) self.assertEqual(users['data'][0]['email'], 'admin@rush.com') @@ -110,59 +114,45 @@ def test_user_list(self): ['current', 'data', 'total', 'total_page']) def test_add_edit_user(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") - call_command("fake_organisation_seeder", "--repeat", 3) - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - org = Organisation.objects.order_by('?').first() - user = user_response.json() - token = user.get('token') payload = { "first_name": "John", "last_name": "Doe", "email": "john@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "forms": [1], "trained": True, "inform_user": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 400) payload["role"] = UserRoleTypes.admin add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User added successfully'}) - org = Organisation.objects.order_by('?').first() edit_payload = { "first_name": "Joe", "last_name": "Doe", "email": "john@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "trained": False, "role": 6, "forms": [1, 2], "inform_user": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} - list_response = self.client.get("/api/v1/users?pending=true", follow=True, - **header) + **self.header) users = list_response.json() fl = list( filter(lambda x: x['email'] == 'john@example.com', users['data'])) @@ -170,19 +160,19 @@ def test_add_edit_user(self): add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 400) edit_payload["role"] = UserRoleTypes.user add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 400) edit_payload["role"] = UserRoleTypes.admin add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User updated successfully'}) @@ -191,7 +181,7 @@ def test_add_edit_user(self): add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User updated successfully'}) @@ -200,14 +190,14 @@ def test_add_edit_user(self): add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User updated successfully'}) get_response = self.client.get("/api/v1/user/{0}".format(fl[0]['id']), content_type='application/json', - **header) + **self.header) self.assertEqual(get_response.status_code, 200) responses = get_response.json() self.assertEqual([ @@ -222,13 +212,13 @@ def test_add_edit_user(self): add_response = self.client.put("/api/v1/user/{0}".format(fl[0]['id']), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User updated successfully'}) get_response = self.client.get("/api/v1/user/{0}".format(fl[0]['id']), content_type='application/json', - **header) + **self.header) self.assertEqual(get_response.status_code, 200) responses = get_response.json() self.assertEqual([ @@ -250,7 +240,7 @@ def test_add_edit_user(self): "last_name": find_user.last_name, "email": find_user.email, "administration": find_user.user_access.administration_id + 1, - "organisation": org.id, + "organisation": self.org.id, "trained": False, "role": find_user.user_access.role, "forms": [fr.form_id for fr in find_user.user_form.all()], @@ -259,35 +249,24 @@ def test_add_edit_user(self): response = self.client.put("/api/v1/user/{0}".format(find_user.id), edit_payload, content_type='application/json', - **header) + **self.header) self.assertEqual(response.status_code, 403) def test_add_admin_user(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") - call_command("fake_organisation_seeder", "--repeat", 3) - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - org = Organisation.objects.order_by('?').first() - user = user_response.json() - token = user.get('token') payload = { "first_name": "County", "last_name": "Admin", "email": "county_admin@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "role": 2, "forms": [1], "trained": False, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User added successfully'}) @@ -302,16 +281,15 @@ def test_add_admin_user(self): "last_name": "Admin", "email": "county_admin2@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "role": 2, "forms": [1], "trained": False, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 403) # Add user for different administration payload = { @@ -319,44 +297,32 @@ def test_add_admin_user(self): "last_name": "Admin", "email": "county_admin3@example.com", "administration": 3, - "organisation": org.id, + "organisation": self.org.id, "role": 2, "forms": [1], "trained": False, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) def test_add_aprroval_user(self): - call_command("administration_seeder", "--test") - call_command("form_seeder", "--test") - call_command("fake_organisation_seeder", "--repeat", 3) - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - org = Organisation.objects.order_by('?').first() - user = user_response.json() - token = user.get('token') payload = { "first_name": "Test", "last_name": "Approver", "email": "test_approver@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "role": 3, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) self.assertEqual(add_response.json(), {'message': 'User added successfully'}) @@ -371,16 +337,15 @@ def test_add_aprroval_user(self): "last_name": "Approver", "email": "test2_approver@example.com", "administration": 2, - "organisation": org.id, + "organisation": self.org.id, "role": 3, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 403) # Add user for different administration payload = { @@ -388,16 +353,15 @@ def test_add_aprroval_user(self): "last_name": "Approver", "email": "test3_approver@example.com", "administration": 3, - "organisation": org.id, + "organisation": self.org.id, "role": 3, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) # Add another role with same form and administration payload = { @@ -405,16 +369,15 @@ def test_add_aprroval_user(self): "last_name": "Entry", "email": "data_entry@example.com", "administration": 3, - "organisation": org.id, + "organisation": self.org.id, "role": 4, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) user = SystemUser.objects.filter( email="data_entry@example.com").first() @@ -427,16 +390,15 @@ def test_add_aprroval_user(self): "last_name": "Entry", "email": "data_entry2@example.com", "administration": 3, - "organisation": org.id, + "organisation": self.org.id, "role": 4, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) # Add national super admin approver @@ -444,16 +406,15 @@ def test_add_aprroval_user(self): "first_name": "National Approver", "last_name": "Entry", "email": "national_approver@example.com", - "organisation": org.id, + "organisation": self.org.id, "role": 1, "forms": [1], "trained": True, } - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 400) self.assertEqual( add_response.json(), @@ -462,21 +423,13 @@ def test_add_aprroval_user(self): add_response = self.client.post("/api/v1/user", payload, content_type='application/json', - **header) + **self.header) self.assertEqual(add_response.status_code, 200) def test_get_user_profile(self): - call_command("administration_seeder", "--test") - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - user_response = self.client.post('/api/v1/login', - user_payload, - content_type='application/json') - user = user_response.json() - token = user.get('token') - header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} response = self.client.get("/api/v1/profile", content_type='application/json', - **header) + **self.header) self.assertEqual(response.status_code, 200) self.assertEqual([ 'email', 'name', 'administration', 'trained', @@ -495,11 +448,6 @@ def test_get_user_roles(self): # def test_verify_invite(self): - call_command("administration_seeder", "--test") - user_payload = {"email": "admin@rush.com", "password": "Test105*"} - self.client.post('/api/v1/login', - user_payload, - content_type='application/json') user = SystemUser.objects.first() invite_payload = 'dummy-token' invite_response = self.client.get( @@ -515,7 +463,6 @@ def test_verify_invite(self): self.assertEqual(invite_response.status_code, 200) def test_set_user_password(self): - call_command("administration_seeder", "--test") user_payload = {"email": "admin@rush.com", "password": "Test105*"} self.client.post('/api/v1/login', user_payload, @@ -541,7 +488,6 @@ def test_set_user_password(self): self.assertEqual(invite_response.status_code, 200) def test_list_administration(self): - call_command("administration_seeder", "--test") administration = self.client.get('/api/v1/administration/1', content_type='application/json') self.assertEqual(administration.status_code, 200) @@ -623,7 +569,7 @@ def test_delete_user(self): token = user.get('token') header = {'HTTP_AUTHORIZATION': f'Bearer {token}'} call_command("fake_user_seeder") - call_command("fake_approver_seeder") + call_command("demo_approval_flow") u = SystemUser.objects.filter( user_access__role__in=[ UserRoleTypes.approver, UserRoleTypes.user], diff --git a/backend/seeder.sh b/backend/seeder.sh index 0b3b73654..623ba4fa7 100755 --- a/backend/seeder.sh +++ b/backend/seeder.sh @@ -52,6 +52,4 @@ if [[ "${seed_organization}" == 'y' || "${seed_organization}" == 'Y' ]]; then fi # python manage.py fake_approver_seeder -# python manage.py form_approval_seeder -# python manage.py form_approval_assignment_seeder # python manage.py fake_data_seeder From d5e4fa0a00629d682ac8bfddb42e406309f71cdf Mon Sep 17 00:00:00 2001 From: dedenbangkit Date: Thu, 8 Feb 2024 14:43:35 +0700 Subject: [PATCH 5/6] [#1157] Fix flake8 --- backend/api/v1/v1_forms/models.py | 2 +- backend/api/v1/v1_forms/tests/tests_form_submission.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/api/v1/v1_forms/models.py b/backend/api/v1/v1_forms/models.py index 218930f54..0d55e3672 100644 --- a/backend/api/v1/v1_forms/models.py +++ b/backend/api/v1/v1_forms/models.py @@ -5,7 +5,7 @@ # Create your models here. from api.v1.v1_forms.constants import QuestionTypes, \ FormTypes, AttributeTypes -from api.v1.v1_profile.models import Administration, Levels +from api.v1.v1_profile.models import Administration from api.v1.v1_users.models import SystemUser diff --git a/backend/api/v1/v1_forms/tests/tests_form_submission.py b/backend/api/v1/v1_forms/tests/tests_form_submission.py index 30f3dbd64..f796842f4 100644 --- a/backend/api/v1/v1_forms/tests/tests_form_submission.py +++ b/backend/api/v1/v1_forms/tests/tests_form_submission.py @@ -87,4 +87,3 @@ def test_form_data_endpoint(self): question_group = webform.get("question_group") self.assertEqual(len(question_group), 1) self.assertEqual(question_group[0].get("name"), "Question Group 01") - From afbf622450f37d788f0b7518d697bd4a487397c5 Mon Sep 17 00:00:00 2001 From: abhishek Date: Thu, 8 Feb 2024 14:04:53 +0530 Subject: [PATCH 6/6] [#1147] administration api call with null value fixed --- frontend/src/components/EditableCell.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/EditableCell.jsx b/frontend/src/components/EditableCell.jsx index ab36ac176..bbb4f5a74 100644 --- a/frontend/src/components/EditableCell.jsx +++ b/frontend/src/components/EditableCell.jsx @@ -60,10 +60,14 @@ const EditableCell = ({ if (typeof record.value === "string") { setLocationName(record.value); } else { - config.fn.administration(record.id, false).then((res) => { - const locName = res; - setLocationName(locName?.full_name); - }); + if (record.value) { + config.fn.administration(record.value, false).then((res) => { + const locName = res; + setLocationName(locName?.full_name); + }); + } else { + setLocationName(null); + } } } }, [record, locationName]);