diff --git a/opentech/apply/funds/migrations/0045_new_workflow.py b/opentech/apply/funds/migrations/0045_new_workflow.py new file mode 100644 index 0000000000000000000000000000000000000000..21dab3f122ff1246d1caff6010534b328b94c5ad --- /dev/null +++ b/opentech/apply/funds/migrations/0045_new_workflow.py @@ -0,0 +1,33 @@ +# Generated by Django 2.0.8 on 2018-10-24 12:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0044_add_named_blocks'), + ] + + operations = [ + migrations.AlterField( + model_name='applicationbase', + name='workflow_name', + field=models.CharField(choices=[('single', 'Request'), ('single_ext', 'Request with external review'), ('double', 'Concept & Proposal')], default='single', max_length=100, verbose_name='Workflow'), + ), + migrations.AlterField( + model_name='applicationsubmission', + name='workflow_name', + field=models.CharField(choices=[('single', 'Request'), ('single_ext', 'Request with external review'), ('double', 'Concept & Proposal')], default='single', max_length=100, verbose_name='Workflow'), + ), + migrations.AlterField( + model_name='labbase', + name='workflow_name', + field=models.CharField(choices=[('single', 'Request'), ('single_ext', 'Request with external review'), ('double', 'Concept & Proposal')], default='single', max_length=100, verbose_name='Workflow'), + ), + migrations.AlterField( + model_name='roundbase', + name='workflow_name', + field=models.CharField(choices=[('single', 'Request'), ('single_ext', 'Request with external review'), ('double', 'Concept & Proposal')], default='single', max_length=100, verbose_name='Workflow'), + ), + ] diff --git a/opentech/apply/funds/tests/factories/models.py b/opentech/apply/funds/tests/factories/models.py index 36e233f24ce381b036f2191b24d8f666f8d96dd0..e678dd8d7adce29b65d2d4322d9f96ccbb69e0af 100644 --- a/opentech/apply/funds/tests/factories/models.py +++ b/opentech/apply/funds/tests/factories/models.py @@ -21,6 +21,8 @@ from opentech.apply.funds.models.forms import ( RoundBaseForm, RoundBaseReviewForm, ) +from opentech.apply.funds.workflow import ConceptProposal, Request + from opentech.apply.users.tests.factories import StaffFactory, UserFactory from opentech.apply.stream_forms.testing.factories import FormDataFactory from opentech.apply.home.factories import ApplyHomePageFactory @@ -49,7 +51,10 @@ __all__ = [ def workflow_for_stages(stages): - return list(FundType.WORKFLOW_CHOICES.keys())[stages - 1] + return { + 1: Request.admin_name, + 2: ConceptProposal.admin_name, + }[stages] class AbstractApplicationFactory(wagtail_factories.PageFactory): diff --git a/opentech/apply/funds/tests/test_admin_form.py b/opentech/apply/funds/tests/test_admin_form.py index 8c4336f10dc74e8e2b6ba78b75f1bee723feb2bf..93413519a472dc54e3a217bad6d76e9588e9c533 100644 --- a/opentech/apply/funds/tests/test_admin_form.py +++ b/opentech/apply/funds/tests/test_admin_form.py @@ -34,13 +34,13 @@ def formset_base(field, total, delete, factory, same=False): return base_data -def form_data(number_forms=0, delete=0, stages=None, same_forms=False): +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) form_data.update(review_form_data) fund_data = factory.build(dict, FACTORY_CLASS=FundTypeFactory) - fund_data['workflow_name'] = workflow_for_stages(stages or number_forms) + fund_data['workflow_name'] = workflow_for_stages(stages) form_data.update(fund_data) return form_data @@ -66,7 +66,7 @@ class TestWorkflowFormAdminForm(TestCase): 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, stages=1)) + form = self.submit_data(form_data(2)) self.assertFalse(form.is_valid()) self.assertTrue(form.errors['__all__']) formset_errors = form.formsets['forms'].errors @@ -76,5 +76,5 @@ class TestWorkflowFormAdminForm(TestCase): self.assertTrue(formset_errors[1]['form']) def test_can_save_two_forms(self): - form = self.submit_data(form_data(2)) + form = self.submit_data(form_data(2, stages=2)) self.assertTrue(form.is_valid()) diff --git a/opentech/apply/funds/tests/test_admin_views.py b/opentech/apply/funds/tests/test_admin_views.py index b065ad10da7a3acf82b850bb41ce1d33478af9da..3469b0448bbab7eb7bdf37c197628fa22d2ee2d4 100644 --- a/opentech/apply/funds/tests/test_admin_views.py +++ b/opentech/apply/funds/tests/test_admin_views.py @@ -17,7 +17,7 @@ class TestFundCreationView(TestCase): self.client.force_login(self.user) url = reverse('wagtailadmin_pages:add', args=('funds', 'fundtype', self.home.id)) - data = form_data(forms, same_forms=same_forms) + data = form_data(forms, same_forms=same_forms, stages=forms) data['action-publish'] = True response = self.client.post(url, data=data, secure=True, follow=True) diff --git a/opentech/apply/funds/workflow.py b/opentech/apply/funds/workflow.py index b0e408b6878faf1a158d087d5a4e030dc6fff15a..f5fc72d52b3ec96443808ed4bf3f4013f7e91eae 100644 --- a/opentech/apply/funds/workflow.py +++ b/opentech/apply/funds/workflow.py @@ -137,6 +137,8 @@ class CanEditPermissions(DefaultPermissions): Request = Stage('Request', False) +RequestExt = Stage('RequestExt', True) + Concept = Stage('Concept', False) Proposal = Stage('Proposal', True) @@ -217,6 +219,112 @@ SingleStageDefinition = { }, } +SingleStageExternalDefinition = { + INITIAL_STATE: { + 'transitions': { + 'ext_internal_review': 'Open Review', + 'ext_rejected': {'display': 'Reject', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_more_info': 'Request More Information', + }, + 'display': 'Under Discussion', + 'stage': RequestExt, + 'permissions': DefaultPermissions(), + 'step': 0, + }, + 'ext_more_info': { + 'transitions': { + INITIAL_STATE: { + 'display': 'Submit', + 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'method': 'create_revision', + }, + }, + 'display': 'More information required', + 'stage': RequestExt, + 'permissions': CanEditPermissions(), + 'step': 0, + }, + 'ext_internal_review': { + 'transitions': { + 'ext_post_review_discussion': 'Close Review', + }, + 'display': 'Internal Review', + 'stage': RequestExt, + 'permissions': DefaultPermissions(), + 'step': 1, + }, + 'ext_post_review_discussion': { + 'transitions': { + 'ext_external_review': 'Open AC review', + 'ext_rejected': {'display': 'Reject', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_post_review_more_info': 'Request More Information', + }, + 'display': 'Under Discussion', + 'stage': RequestExt, + 'permissions': DefaultPermissions(), + 'step': 2, + }, + 'ext_post_review_more_info': { + 'transitions': { + 'ext_post_review_discussion': { + 'display': 'Submit', + 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'method': 'create_revision', + }, + }, + 'display': 'More information required', + 'stage': RequestExt, + 'permissions': CanEditPermissions(), + 'step': 2, + }, + 'ext_external_review': { + 'transitions': { + 'ext_post_external_review_discussion': 'Close Review', + }, + 'display': 'Advisory Council Review', + 'stage': RequestExt, + 'permissions': ReviewerReviewPermissions(), + 'step': 3, + }, + 'ext_post_external_review_discussion': { + 'transitions': { + 'ext_accepted': {'display': 'Accept', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_rejected': {'display': 'Reject', 'permissions': {UserPermissions.ADMIN, UserPermissions.LEAD}}, + 'ext_post_external_review_more_info': 'Request More Information', + }, + 'display': 'Under Discussion', + 'stage': RequestExt, + 'permissions': DefaultPermissions(), + 'step': 4, + }, + 'ext_post_external_review_more_info': { + 'transitions': { + 'ext_post_external_review_discussion': { + 'display': 'Submit', + 'permissions': {UserPermissions.APPLICANT, UserPermissions.LEAD, UserPermissions.ADMIN}, + 'method': 'create_revision', + }, + }, + 'display': 'More information required', + 'stage': RequestExt, + 'permissions': CanEditPermissions(), + 'step': 4, + }, + + 'ext_accepted': { + 'display': 'Accepted', + 'stage': RequestExt, + 'permissions': NoPermissions(), + 'step': 5, + }, + 'ext_rejected': { + 'display': 'Rejected', + 'stage': RequestExt, + 'permissions': NoPermissions(), + 'step': 5, + }, +} + DoubleStageDefinition = { INITIAL_STATE: { @@ -416,6 +524,10 @@ Request = Workflow('Request', 'single', **{ for phase_name, phase_data in SingleStageDefinition.items() }) +RequestExternal = Workflow('Request with external review', 'single_ext', **{ + phase_name: Phase(phase_name, **phase_data) + for phase_name, phase_data in SingleStageExternalDefinition.items() +}) ConceptProposal = Workflow('Concept & Proposal', 'double', **{ phase_name: Phase(phase_name, **phase_data) @@ -425,6 +537,7 @@ ConceptProposal = Workflow('Concept & Proposal', 'double', **{ WORKFLOWS = { Request.admin_name: Request, + RequestExternal.admin_name: RequestExternal, ConceptProposal.admin_name: ConceptProposal, } @@ -479,6 +592,7 @@ DETERMINATION_RESPONSE_PHASES = [ 'post_review_discussion', 'concept_review_discussion', 'post_external_review_discussion', + 'ext_post_external_review_discussion', ]