Skip to content

Commit

Permalink
GH-717: Admin changes to allow multiple forms in second stage
Browse files Browse the repository at this point in the history
  • Loading branch information
Parbhat authored and frjo committed Aug 26, 2019
1 parent 07bab6b commit b4b0cfd
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 11 deletions.
33 changes: 32 additions & 1 deletion opentech/apply/funds/admin_forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import Counter
from wagtail.admin.forms import WagtailAdminPageForm

from .workflow import WORKFLOWS
Expand All @@ -10,12 +11,42 @@ def clean(self):
workflow = WORKFLOWS[cleaned_data['workflow_name']]
application_forms = self.formsets['forms']
review_forms = self.formsets['review_forms']
number_of_stages = len(workflow.stages)

self.validate_stages_equal_forms(workflow, application_forms)
self.validate_application_forms(workflow, application_forms)
if number_of_stages == 1:
self.validate_stages_equal_forms(workflow, application_forms)
self.validate_stages_equal_forms(workflow, review_forms, form_type="Review form")

return cleaned_data

def validate_application_forms(self, workflow, forms):
"""
Application forms are not equal to the number of stages like review forms.
Now, staff can select a proposal form from multiple forms list in stage 2.
"""
if forms.is_valid():
valid_forms = [form for form in forms if not form.cleaned_data['DELETE']]
forms_stages = [form.cleaned_data['stage'] for form in valid_forms]
stages_counter = Counter(forms_stages)

number_of_stages = len(workflow.stages)
error_list = []

for stage in range(1, number_of_stages + 1):
is_form_present = True if stages_counter.get(stage, 0) > 0 else False
if not is_form_present:
error_list.append(f'Please provide form for Stage {stage}.')

if stage == 1 and stages_counter.get(stage, 0) > 1:
error_list.append('Only 1 form can be selected for 1st Stage.')

if error_list:
self.add_error(
None,
error_list,
)

def validate_stages_equal_forms(self, workflow, forms, form_type="form"):
if forms.is_valid():
valid_forms = [form for form in forms if not form.cleaned_data['DELETE']]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 2.0.13 on 2019-08-05 07:43

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('funds', '0065_applicationsubmission_meta_categories'),
]

operations = [
migrations.AddField(
model_name='applicationbaseform',
name='stage',
field=models.PositiveSmallIntegerField(choices=[(1, '1st Stage'), (2, '2nd Stage')], default=1),
preserve_default=False,
),
migrations.AddField(
model_name='labbaseform',
name='stage',
field=models.PositiveSmallIntegerField(choices=[(1, '1st Stage'), (2, '2nd Stage')], default=1),
preserve_default=False,
),
migrations.AddField(
model_name='roundbaseform',
name='stage',
field=models.PositiveSmallIntegerField(choices=[(1, '1st Stage'), (2, '2nd Stage')], default=1),
preserve_default=False,
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 2.0.13 on 2019-08-05 08:25

from django.db import migrations


def increment_stage_in_forms(forms):
"""
The current system has assumption that there is one application form per stage.
To replicate the current behaviour new stage field should be equal to the index of the form.
"""
for index, form in enumerate(forms.all(), 1):
form.stage = index
form.save(update_fields=['stage'])


def one_application_form_per_stage(apps, schema_editor):
Fund = apps.get_model('funds', 'FundType')
RequestForPartners = apps.get_model('funds', 'RequestForPartners')
Round = apps.get_model('funds', 'Round')
SealedRound = apps.get_model('funds', 'SealedRound')
LabType = apps.get_model('funds', 'LabType')

for fund in Fund.objects.all():
if fund.forms.count() > 1:
increment_stage_in_forms(fund.forms)

for rfp in RequestForPartners.objects.all():
if rfp.forms.count() > 1:
increment_stage_in_forms(rfp.forms)

for round_ in Round.objects.all():
if round_.forms.count() > 1:
increment_stage_in_forms(round_.forms)

for sealed_round in SealedRound.objects.all():
if sealed_round.forms.count() > 1:
increment_stage_in_forms(sealed_round.forms)

for lab in LabType.objects.all():
if lab.forms.count() > 1:
increment_stage_in_forms(lab.forms)


class Migration(migrations.Migration):

dependencies = [
('funds', '0066_add_stage_to_selected_forms'),
]

operations = [
migrations.RunPython(one_application_form_per_stage, migrations.RunPython.noop),
]
5 changes: 4 additions & 1 deletion opentech/apply/funds/models/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,10 @@ def _copy_form(self, form, new_class):
new_form.id = None
new_form.name = '{} for {} ({})'.format(new_form.name, self.title, self.get_parent().title)
new_form.save()
new_class.objects.create(round=self, form=new_form)
if hasattr(form, 'stage'):
new_class.objects.create(round=self, form=new_form, stage=form.stage)
else:
new_class.objects.create(round=self, form=new_form)

def get_submit_meta_data(self, **kwargs):
return super().get_submit_meta_data(
Expand Down
10 changes: 9 additions & 1 deletion opentech/apply/funds/models/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ def __str__(self):


class AbstractRelatedForm(Orderable):
FIRST_STAGE = 1
SECOND_STAGE = 2
STAGE_CHOICES = [
(FIRST_STAGE, '1st Stage'),
(SECOND_STAGE, '2nd Stage'),
]
form = models.ForeignKey('ApplicationForm', on_delete=models.PROTECT)
stage = models.PositiveSmallIntegerField(choices=STAGE_CHOICES)

panels = [
FilteredFieldPanel('form', filter_query={'roundbaseform__isnull': True})
FilteredFieldPanel('form', filter_query={'roundbaseform__isnull': True}),
FieldPanel('stage'),
]

@property
Expand Down
29 changes: 21 additions & 8 deletions opentech/apply/funds/tests/test_admin_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from opentech.apply.review.tests.factories import ReviewFormFactory


def formset_base(field, total, delete, factory, same=False):
def formset_base(field, total, delete, factory, same=False, form_stage_info=None):
base_data = {
f'{field}-TOTAL_FORMS': total + delete,
f'{field}-INITIAL_FORMS': 0,
Expand All @@ -29,14 +29,18 @@ def formset_base(field, total, delete, factory, same=False):
f'{field}-{i}-ORDER': i,
f'{field}-{i}-DELETE': should_delete,
})
if form_stage_info:
# form_stage_info contains stage number for selected application forms
stage = form_stage_info[i]
base_data[f'{field}-{i}-stage'] = stage
deleted += 1

return base_data


def form_data(number_forms=0, delete=0, stages=1, same_forms=False):
form_data = formset_base('forms', number_forms, delete, same=same_forms, factory=ApplicationFormFactory)
review_form_data = formset_base('review_forms', number_forms, False, same=same_forms, factory=ReviewFormFactory)
def form_data(number_application_forms=0, number_review_forms=0, delete=0, stages=1, same_forms=False, form_stage_info=[1]):
form_data = formset_base('forms', number_application_forms, delete, same=same_forms, factory=ApplicationFormFactory, form_stage_info=form_stage_info)
review_form_data = formset_base('review_forms', number_review_forms, False, same=same_forms, factory=ReviewFormFactory)
form_data.update(review_form_data)

fund_data = factory.build(dict, FACTORY_CLASS=FundTypeFactory)
Expand All @@ -58,15 +62,15 @@ def test_doesnt_validates_with_no_form(self):
self.assertTrue(form.errors['__all__'])

def test_validates_with_one_form_one_stage(self):
form = self.submit_data(form_data(1))
form = self.submit_data(form_data(1, 1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_validates_with_one_form_one_stage_with_deleted(self):
form = self.submit_data(form_data(1, delete=1))
form = self.submit_data(form_data(1, 1, delete=1, form_stage_info=[2, 1]))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_doesnt_validates_with_two_forms_one_stage(self):
form = self.submit_data(form_data(2))
form = self.submit_data(form_data(2, 2, form_stage_info=[1, 2]))
self.assertFalse(form.is_valid())
self.assertTrue(form.errors['__all__'])
formset_errors = form.formsets['forms'].errors
Expand All @@ -76,5 +80,14 @@ def test_doesnt_validates_with_two_forms_one_stage(self):
self.assertTrue(formset_errors[1]['form'])

def test_can_save_two_forms(self):
form = self.submit_data(form_data(2, stages=2))
form = self.submit_data(form_data(2, 2, stages=2, form_stage_info=[1, 2]))
self.assertTrue(form.is_valid())

def test_can_save_multiple_forms_stage_two(self):
form = self.submit_data(form_data(3, 2, stages=2, form_stage_info=[1, 2, 2]))
self.assertTrue(form.is_valid())

def test_doesnt_validates_with_two_first_stage_forms_in_two_stage(self):
form = self.submit_data(form_data(2, 2, stages=2, form_stage_info=[1, 1]))
self.assertFalse(form.is_valid())
self.assertTrue(form.errors['__all__'])

0 comments on commit b4b0cfd

Please sign in to comment.