From f86fd8bb5754b023b2aa60504ae03eb200a403fa Mon Sep 17 00:00:00 2001 From: sks444 <krishnasingh.ss30@gmail.com> Date: Thu, 22 Oct 2020 00:10:56 +0530 Subject: [PATCH] Change submission screening flow --- hypha/apply/api/v1/screening/__init__.py | 0 hypha/apply/api/v1/screening/filters.py | 9 ++ hypha/apply/api/v1/screening/serializers.py | 17 ++++ hypha/apply/api/v1/screening/views.py | 83 +++++++++++++++++++ hypha/apply/api/v1/urls.py | 2 + hypha/apply/funds/admin.py | 1 + hypha/apply/funds/admin_views.py | 2 +- hypha/apply/funds/forms.py | 2 +- .../commands/export_submissions_csv.py | 2 +- .../0080_add_screening_statuses_field.py | 18 ++++ ..._screening_status_to_screening_statuses.py | 26 ++++++ .../0082_remove_screening_status_field.py | 17 ++++ .../migrations/0083_auto_20201021_1129.py | 23 +++++ hypha/apply/funds/models/screening.py | 1 - hypha/apply/funds/models/submissions.py | 8 +- .../funds/includes/admin_primary_actions.html | 2 +- .../funds/includes/screening_form.html | 2 +- .../includes/screening_status_block.html | 2 +- .../funds/templates/funds/tables/table.html | 2 +- hypha/apply/funds/views.py | 1 + 20 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 hypha/apply/api/v1/screening/__init__.py create mode 100644 hypha/apply/api/v1/screening/filters.py create mode 100644 hypha/apply/api/v1/screening/serializers.py create mode 100644 hypha/apply/api/v1/screening/views.py create mode 100644 hypha/apply/funds/migrations/0080_add_screening_statuses_field.py create mode 100644 hypha/apply/funds/migrations/0081_migrate_screening_status_to_screening_statuses.py create mode 100644 hypha/apply/funds/migrations/0082_remove_screening_status_field.py create mode 100644 hypha/apply/funds/migrations/0083_auto_20201021_1129.py diff --git a/hypha/apply/api/v1/screening/__init__.py b/hypha/apply/api/v1/screening/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/hypha/apply/api/v1/screening/filters.py b/hypha/apply/api/v1/screening/filters.py new file mode 100644 index 000000000..51da1140a --- /dev/null +++ b/hypha/apply/api/v1/screening/filters.py @@ -0,0 +1,9 @@ +from django_filters import rest_framework as filters + +from hypha.apply.funds.models import ScreeningStatus + + +class ScreeningStatusFilter(filters.FilterSet): + class Meta: + model = ScreeningStatus + fields = ['yes', 'default'] diff --git a/hypha/apply/api/v1/screening/serializers.py b/hypha/apply/api/v1/screening/serializers.py new file mode 100644 index 000000000..03f78157b --- /dev/null +++ b/hypha/apply/api/v1/screening/serializers.py @@ -0,0 +1,17 @@ +from rest_framework import serializers + +from hypha.apply.funds.models import ScreeningStatus + + +class ScreeningStatusListSerializer(serializers.ModelSerializer): + class Meta: + model = ScreeningStatus + fields = ('title', 'yes', 'default') + + +class ScreeningStatusSerializer(serializers.Serializer): + title = serializers.CharField() + + +class ScreeningStatusDefaultSerializer(serializers.Serializer): + yes = serializers.BooleanField() diff --git a/hypha/apply/api/v1/screening/views.py b/hypha/apply/api/v1/screening/views.py new file mode 100644 index 000000000..baa152b08 --- /dev/null +++ b/hypha/apply/api/v1/screening/views.py @@ -0,0 +1,83 @@ +from django.shortcuts import get_object_or_404 + +from rest_framework import viewsets, permissions, mixins, status +from rest_framework.response import Response +from rest_framework.exceptions import ValidationError +from rest_framework.decorators import action + +from django_filters import rest_framework as filters + +from rest_framework_api_key.permissions import HasAPIKey + +from hypha.apply.funds.models import ScreeningStatus + +from ..pagination import StandardResultsSetPagination +from ..permissions import IsApplyStaffUser +from ..mixin import SubmissionNestedMixin + +from .filters import ScreeningStatusFilter +from .serializers import ( + ScreeningStatusListSerializer, + ScreeningStatusSerializer, + ScreeningStatusDefaultSerializer +) + + +class ScreeningStatusViewSet(viewsets.ReadOnlyModelViewSet): + permission_classes = ( + HasAPIKey | permissions.IsAuthenticated, HasAPIKey | IsApplyStaffUser, + ) + filter_backends = (filters.DjangoFilterBackend,) + filter_class = ScreeningStatusFilter + pagination_class = StandardResultsSetPagination + queryset = ScreeningStatus.objects.all() + serializer_class = ScreeningStatusListSerializer + + +class SubmissionScreeningStatusViewSet( + SubmissionNestedMixin, + mixins.ListModelMixin, + mixins.CreateModelMixin, + viewsets.GenericViewSet +): + permission_classes = ( + HasAPIKey | permissions.IsAuthenticated, HasAPIKey | IsApplyStaffUser, + ) + serializer_class = ScreeningStatusSerializer + + def get_queryset(self): + submission = self.get_submission_object() + return submission.screening_statuses.objects.all() + + def create(self, request, *args, **kwargs): + ser = self.get_serializer(data=request.data) + ser.is_valid(raise_exception=True) + submission = self.get_submission_object() + screening_status = get_object_or_404( + ScreeningStatus, title=ser.validated_data['title'] + ) + if not submission.screening_statuses.filter(default=True).exists(): + raise ValidationError({'detail': "Can't set screening status without default being set"}) + if ( + submission.screening_statuses.exists() and + submission.screening_statuses.first().yes != screening_status.yes + ): + raise ValidationError({'detail': "Can't set screening status for both yes and no"}) + submission.screening_statuses.add( + screening_status + ) + return Response(status=status.HTTP_201_CREATED) + + @action(detail=False, methods=['post']) + def default(self, request, *args, **kwargs): + ser = ScreeningStatusDefaultSerializer(data=request.data) + ser.is_valid(raise_exception=True) + yes = ser.validated_data['yes'] + submission = self.get_submission_object() + if submission.screening_statuses.filter(default=False).exists(): + raise ValidationError({ + 'detail': "Can't set default as more than one screening status is already set." + }) + screening_status = ScreeningStatus.objects.get(default=True, yes=yes) + submission.screening_statuses.add(screening_status) + return Response(status=status.HTTP_201_CREATED) diff --git a/hypha/apply/api/v1/urls.py b/hypha/apply/api/v1/urls.py index e8a7635ec..673a4daa9 100644 --- a/hypha/apply/api/v1/urls.py +++ b/hypha/apply/api/v1/urls.py @@ -3,6 +3,7 @@ from rest_framework_nested import routers from hypha.apply.api.v1.determination.views import SubmissionDeterminationViewSet from hypha.apply.api.v1.review.views import SubmissionReviewViewSet +from hypha.apply.api.v1.screening.views import ScreeningStatusViewSet from .views import ( CommentViewSet, @@ -20,6 +21,7 @@ router = routers.SimpleRouter() router.register(r'submissions', SubmissionViewSet, basename='submissions') router.register(r'comments', CommentViewSet, basename='comments') router.register(r'rounds', RoundViewSet, basename='rounds') +router.register(r'screening_statuses', ScreeningStatusViewSet, basename='screenings') submission_router = routers.NestedSimpleRouter(router, r'submissions', lookup='submission') submission_router.register(r'actions', SubmissionActionViewSet, basename='submission-actions') diff --git a/hypha/apply/funds/admin.py b/hypha/apply/funds/admin.py index f12075832..66a63180a 100644 --- a/hypha/apply/funds/admin.py +++ b/hypha/apply/funds/admin.py @@ -115,6 +115,7 @@ class ScreeningStatusAdmin(ModelAdmin): menu_icon = 'tag' list_display = ('title', 'yes', 'default') permission_helper_class = ScreeningStatusPermissionHelper + list_display = ('title', 'yes', 'default') class SealedRoundAdmin(BaseRoundAdmin): diff --git a/hypha/apply/funds/admin_views.py b/hypha/apply/funds/admin_views.py index 1991566d6..8c2371495 100644 --- a/hypha/apply/funds/admin_views.py +++ b/hypha/apply/funds/admin_views.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _ from wagtail.admin import messages from wagtail.admin.forms.pages import CopyForm from wagtail.admin.views.pages import get_valid_next_url_from_request -from wagtail.contrib.modeladmin.views import CreateView +from wagtail.contrib.modeladmin.views import CreateView, EditView from wagtail.core import hooks from wagtail.core.models import Page diff --git a/hypha/apply/funds/forms.py b/hypha/apply/funds/forms.py index 1b9674410..4b4d4edb7 100644 --- a/hypha/apply/funds/forms.py +++ b/hypha/apply/funds/forms.py @@ -95,7 +95,7 @@ class ScreeningSubmissionForm(ApplicationSubmissionModelForm): class Meta: model = ApplicationSubmission - fields = ('screening_status',) + fields = ('screening_statuses',) def __init__(self, *args, **kwargs): self.user = kwargs.pop('user') diff --git a/hypha/apply/funds/management/commands/export_submissions_csv.py b/hypha/apply/funds/management/commands/export_submissions_csv.py index cf2bb59e0..90ef23cf0 100644 --- a/hypha/apply/funds/management/commands/export_submissions_csv.py +++ b/hypha/apply/funds/management/commands/export_submissions_csv.py @@ -44,5 +44,5 @@ class Command(BaseCommand): submission_value = submission.value except KeyError: submission_value = 0 - + import ipdb; ipdb.set_trace() writer.writerow([submission.id, submission.title, submission.full_name, submission.email, submission_value, submission.duration, submission_reapplied, submission.stage, submission.phase, submission.screening_status, submission.submit_time.strftime('%Y-%m-%d'), submission_region, submission_country, submission_focus, submission_type]) diff --git a/hypha/apply/funds/migrations/0080_add_screening_statuses_field.py b/hypha/apply/funds/migrations/0080_add_screening_statuses_field.py new file mode 100644 index 000000000..dfe45884b --- /dev/null +++ b/hypha/apply/funds/migrations/0080_add_screening_statuses_field.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.16 on 2020-10-21 10:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0079_add_reviewer_settings_for_submission_access'), + ] + + operations = [ + migrations.AddField( + model_name='applicationsubmission', + name='screening_statuses', + field=models.ManyToManyField(blank=True, related_name='submissions', to='funds.ScreeningStatus'), + ), + ] diff --git a/hypha/apply/funds/migrations/0081_migrate_screening_status_to_screening_statuses.py b/hypha/apply/funds/migrations/0081_migrate_screening_status_to_screening_statuses.py new file mode 100644 index 000000000..61fbf8316 --- /dev/null +++ b/hypha/apply/funds/migrations/0081_migrate_screening_status_to_screening_statuses.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.16 on 2020-10-21 10:46 + +from django.db import migrations + + +def make_many_screening_statuses(apps, schema_editor): + """ + Adds the ScreeningStatus object in ApplicationSubmission.screening_status + to the many-to-many relationship in ApplicationSubmission.screening_statuses + """ + ApplicationSubmission = apps.get_model('funds', 'ApplicationSubmission') + + for submission in ApplicationSubmission.objects.all(): + if submission.screening_status: + submission.screening_statuses.add(submission.screening_status) + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0080_add_screening_statuses_field'), + ] + + operations = [ + migrations.RunPython(make_many_screening_statuses), + ] diff --git a/hypha/apply/funds/migrations/0082_remove_screening_status_field.py b/hypha/apply/funds/migrations/0082_remove_screening_status_field.py new file mode 100644 index 000000000..80e7c7a7f --- /dev/null +++ b/hypha/apply/funds/migrations/0082_remove_screening_status_field.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.16 on 2020-10-21 10:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0081_migrate_screening_status_to_screening_statuses'), + ] + + operations = [ + migrations.RemoveField( + model_name='applicationsubmission', + name='screening_status', + ), + ] diff --git a/hypha/apply/funds/migrations/0083_auto_20201021_1129.py b/hypha/apply/funds/migrations/0083_auto_20201021_1129.py new file mode 100644 index 000000000..2444167f0 --- /dev/null +++ b/hypha/apply/funds/migrations/0083_auto_20201021_1129.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.16 on 2020-10-21 11:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0082_remove_screening_status_field'), + ] + + operations = [ + migrations.AddField( + model_name='screeningstatus', + name='default', + field=models.BooleanField(default=False, verbose_name='Default Yes/No'), + ), + migrations.AddField( + model_name='screeningstatus', + name='yes', + field=models.BooleanField(default=False, verbose_name='Yes/No'), + ), + ] diff --git a/hypha/apply/funds/models/screening.py b/hypha/apply/funds/models/screening.py index 3c0084f55..f60be6017 100644 --- a/hypha/apply/funds/models/screening.py +++ b/hypha/apply/funds/models/screening.py @@ -1,5 +1,4 @@ from django.db import models - from ..admin_forms import ScreeningStatusAdminForm diff --git a/hypha/apply/funds/models/submissions.py b/hypha/apply/funds/models/submissions.py index e2e6e892d..e0612b8e5 100644 --- a/hypha/apply/funds/models/submissions.py +++ b/hypha/apply/funds/models/submissions.py @@ -453,12 +453,10 @@ class ApplicationSubmission( # Workflow inherited from WorkflowHelpers status = FSMField(default=INITIAL_STATE, protected=True) - screening_status = models.ForeignKey( + screening_statuses = models.ManyToManyField( 'funds.ScreeningStatus', - related_name='+', - on_delete=models.SET_NULL, - verbose_name='screening status', - null=True, + related_name='submissions', + blank=True ) is_draft = False diff --git a/hypha/apply/funds/templates/funds/includes/admin_primary_actions.html b/hypha/apply/funds/templates/funds/includes/admin_primary_actions.html index 17956259b..900d4bef0 100644 --- a/hypha/apply/funds/templates/funds/includes/admin_primary_actions.html +++ b/hypha/apply/funds/templates/funds/includes/admin_primary_actions.html @@ -12,7 +12,7 @@ {% endif %} {% endif %} -{% if not object.screening_status %} +{% if not object.screening_statuses.exists %} <a data-fancybox data-src="#screen-application" class="button button--bottom-space button--primary button--full-width is-not-disabled" href="#">Screen application</a> {% endif %} diff --git a/hypha/apply/funds/templates/funds/includes/screening_form.html b/hypha/apply/funds/templates/funds/includes/screening_form.html index bb2a46df4..6e6375088 100644 --- a/hypha/apply/funds/templates/funds/includes/screening_form.html +++ b/hypha/apply/funds/templates/funds/includes/screening_form.html @@ -1,7 +1,7 @@ {% if screening_form.should_show %} <div class="modal" id="screen-application"> <h4 class="modal__header-bar">Update status</h4> - <p>Current status: {{ object.screening_status }}</p> + <p>Current status: {% if object.screening_statuses.exists %}{{ object.screening_statuses }}{% endif %}</p> {% include 'funds/includes/delegated_form_base.html' with form=screening_form value='Screen'%} </div> {% endif %} diff --git a/hypha/apply/funds/templates/funds/includes/screening_status_block.html b/hypha/apply/funds/templates/funds/includes/screening_status_block.html index 6026ffa86..8600cd5f3 100644 --- a/hypha/apply/funds/templates/funds/includes/screening_status_block.html +++ b/hypha/apply/funds/templates/funds/includes/screening_status_block.html @@ -1,6 +1,6 @@ <div class="sidebar__inner"> <h5>Screening Status</h5> <p> - {{ object.screening_status|default:"Awaiting Screen status" }} <a data-fancybox data-src="#screen-application" class="link link--secondary-change" href="#">Change</a> + {% if object.screening_statuses.exists %}{{ object.screening_statuses|default:"Awaiting Screen status" }}{% endif %} <a data-fancybox data-src="#screen-application" class="link link--secondary-change" href="#">Change</a> </p> </div> diff --git a/hypha/apply/funds/templates/funds/tables/table.html b/hypha/apply/funds/templates/funds/tables/table.html index e7048089c..1a51c0527 100644 --- a/hypha/apply/funds/templates/funds/tables/table.html +++ b/hypha/apply/funds/templates/funds/tables/table.html @@ -34,7 +34,7 @@ {% endif %} </td> <td> - <strong>{{ submission.screening_status|default:"Awaiting Screen status" }}</strong> + <strong>{% if object.screening_statuses.exists %}{{ object.screening_statuses|default:"Awaiting Screen status" }}{% endif %}</strong> </td> <td> diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py index 0358d7cea..0904840eb 100644 --- a/hypha/apply/funds/views.py +++ b/hypha/apply/funds/views.py @@ -577,6 +577,7 @@ class ScreeningSubmissionView(DelegatedViewMixin, UpdateView): context_name = 'screening_form' def form_valid(self, form): + import ipdb; ipdb.set_trace() old = copy(self.get_object()) response = super().form_valid(form) # Record activity -- GitLab