Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple proposal forms for Double stage funds #1412

Merged
merged 5 commits into from
Aug 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion opentech/apply/determinations/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,26 @@ class BaseProposalDeterminationForm(forms.Form):


class ConceptDeterminationForm(BaseConceptDeterminationForm, BaseNormalDeterminationForm):
pass
def __init__(self, *args, submission, user, initial={}, instance=None, **kwargs):
super().__init__(*args, submission=submission, user=user, initial={}, instance=None, **kwargs)

action = kwargs.get('action')
stages_num = len(submission.workflow.stages)

if stages_num > 1 and action == 'invited_to_proposal':
second_stage_forms = submission.get_from_parent('forms').filter(stage=2)
if second_stage_forms.count() > 1:
proposal_form_choices = [
(index, form.form.name)
for index, form in enumerate(second_stage_forms)
]
self.fields['proposal_form'] = forms.ChoiceField(
label='Proposal Form',
choices=proposal_form_choices,
help_text='Select the proposal form to use for proposal stage.',
)
self.fields['proposal_form'].group = 1
self.fields.move_to_end('proposal_form', last=False)


class ProposalDeterminationForm(BaseProposalDeterminationForm, BaseNormalDeterminationForm):
Expand Down
1 change: 0 additions & 1 deletion opentech/apply/determinations/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def test_cant_resubmit_determination(self):

def test_can_edit_draft_determination(self):
submission = ApplicationSubmissionFactory(status='post_review_discussion', lead=self.user)
DeterminationFactory(submission=submission, author=self.user)
response = self.post_page(submission, {
'data': 'value',
'outcome': ACCEPTED,
Expand Down
4 changes: 3 additions & 1 deletion opentech/apply/determinations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ def form_valid(self, form):
submission=self.object.submission,
related=self.object,
)
proposal_form = form.cleaned_data.get('proposal_form')
transition = transition_from_outcome(form.cleaned_data.get('outcome'), self.submission)

if self.object.outcome == NEEDS_MORE_INFO:
Expand All @@ -260,7 +261,8 @@ def form_valid(self, form):
related_object=self.object,
)

self.submission.perform_transition(transition, self.request.user, request=self.request, notify=False)
self.submission.perform_transition(
transition, self.request.user, request=self.request, notify=False, proposal_form=proposal_form)

return HttpResponseRedirect(self.submission.get_absolute_url())

Expand Down
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
10 changes: 6 additions & 4 deletions opentech/apply/funds/models/submissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,15 @@ def perform_transition(self, action, user, request=None, **kwargs):
transition(by=user, request=request, **kwargs)
self.save(update_fields=['status'])

self.progress_stage_when_possible(user, request)
self.progress_stage_when_possible(user, request, **kwargs)

attrs['perform_transition'] = perform_transition

def progress_stage_when_possible(self, user, request):
def progress_stage_when_possible(self, user, request, notify=None, **kwargs):
# Check to see if we can progress to a new stage from the current status
for stage_transition in STAGE_CHANGE_ACTIONS:
try:
self.perform_transition(stage_transition, user, request=request, notify=False)
self.perform_transition(stage_transition, user, request=request, notify=False, **kwargs)
except PermissionDenied:
pass

Expand Down Expand Up @@ -513,7 +513,9 @@ def progress_application(self, **kwargs):
prev_meta_categories = submission_in_db.meta_categories.all()

self.id = None
self.form_fields = self.get_from_parent('get_defined_fields')(target)
proposal_form = kwargs.get('proposal_form')
proposal_form = int(proposal_form) if proposal_form else 0
self.form_fields = self.get_from_parent('get_defined_fields')(target, form_index=proposal_form)

self.live_revision = None
self.draft_revision = None
Expand Down
8 changes: 4 additions & 4 deletions opentech/apply/funds/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ class WorkflowStreamForm(WorkflowHelpers, AbstractStreamForm): # type: ignore
class Meta:
abstract = True

def get_defined_fields(self, stage=None):
def get_defined_fields(self, stage=None, form_index=0):
if not stage:
form_index = 0
stage_num = 1
else:
form_index = self.workflow.stages.index(stage)
return self.forms.all()[form_index].fields
stage_num = self.workflow.stages.index(stage) + 1
return self.forms.filter(stage=stage_num)[form_index].fields

def render_landing_page(self, request, form_submission=None, *args, **kwargs):
# We only reach this page after creation of a new submission
Expand Down
9 changes: 6 additions & 3 deletions opentech/apply/funds/tests/factories/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ def parent(self, create, extracted_parent, **parent_kwargs):
@factory.post_generation
def forms(self, create, extracted, **kwargs):
if create:
for _ in self.workflow.stages:
for index, _ in enumerate(self.workflow.stages, 1):
# Generate a form based on all defined fields on the model
ApplicationBaseFormFactory(
application=self,
stage=index,
**kwargs,
)
ApplicationBaseReviewForm(
Expand Down Expand Up @@ -160,10 +161,11 @@ def parent(self, create, extracted_parent, **parent_kwargs):
@factory.post_generation
def forms(self, create, extracted, **kwargs):
if create:
for _ in self.workflow.stages:
for index, _ in enumerate(self.workflow.stages, 1):
# Generate a form based on all defined fields on the model
RoundBaseFormFactory(
round=self,
stage=index,
**kwargs,
)
RoundBaseReviewFormFactory(
Expand Down Expand Up @@ -197,10 +199,11 @@ class Meta:
@factory.post_generation
def forms(self, create, extracted, **kwargs):
if create:
for _ in self.workflow.stages:
for index, _ in enumerate(self.workflow.stages, 1):
# Generate a form based on all defined fields on the model
LabBaseFormFactory(
lab=self,
stage=index,
**kwargs,
)
LabBaseReviewFormFactory(
Expand Down
31 changes: 23 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,20 @@ 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(num_appl_forms=0, num_review_forms=0, delete=0, stages=1, same_forms=False, form_stage_info=[1]):
form_data = formset_base(
'forms', num_appl_forms, delete, same=same_forms, factory=ApplicationFormFactory,
form_stage_info=form_stage_info)
review_form_data = formset_base('review_forms', num_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 +64,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 +82,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__'])
Loading