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