From 283994c615e235281086d582755afa4d429ec735 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Mon, 18 Dec 2017 16:40:48 +0000 Subject: [PATCH] Defer the addition of forms to the workflow within the view --- opentech/apply/tests/factories.py | 30 +++++++++++++++++++++++++-- opentech/apply/tests/test_workflow.py | 14 +++++++------ opentech/apply/views.py | 10 ++++++--- opentech/apply/workflow.py | 23 ++++++++++++++------ 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/opentech/apply/tests/factories.py b/opentech/apply/tests/factories.py index bd237ca95..cbaf9722b 100644 --- a/opentech/apply/tests/factories.py +++ b/opentech/apply/tests/factories.py @@ -49,6 +49,12 @@ class PhaseFactory(factory.Factory): new_class = type(model_class.__name__, (model_class,), {'actions': actions}) return new_class(*args, **kwargs) + @classmethod + def _build(cls, model_class, *args, **kwargs): + # defer to create because parent uses build + return cls._create(model_class, *args, **kwargs) + + class StageFactory(factory.Factory): class Meta: model = Stage @@ -63,18 +69,38 @@ class StageFactory(factory.Factory): @classmethod def _create(cls, model_class, *args, **kwargs): + # Returns a new class + phases = kwargs.pop('phases') + name = kwargs.pop('name') + return type(model_class.__name__, (model_class,), {'phases': phases, 'name': name}) + + @classmethod + def _build(cls, model_class, *args, **kwargs): + # returns an instance of the stage class phases = kwargs.pop('phases') - new_class = type(model_class.__name__, (model_class,), {'phases': phases}) + name = kwargs.pop('name') + new_class = type(model_class.__name__, (model_class,), {'phases': phases, 'name': name}) return new_class(*args, **kwargs) class WorkflowFactory(factory.Factory): class Meta: model = Workflow - inline_args = ('name', 'stages',) + rename = {'stages': 'stage_classes'} class Params: num_stages = factory.Faker('random_int', min=1, max=3) name = factory.Faker('word') stages = ListSubFactory(StageFactory, count=factory.SelfAttribute('num_stages')) + + @factory.LazyAttribute + def forms(self): + return [Form() for _ in range(self.num_stages)] + + @classmethod + def _create(cls, model_class, *args, **kwargs): + name = kwargs.pop('name') + stages = kwargs.pop('stage_classes') + new_class = type(model_class.__name__, (model_class,), {'name': name, 'stage_classes': stages}) + return new_class(*args, **kwargs) diff --git a/opentech/apply/tests/test_workflow.py b/opentech/apply/tests/test_workflow.py index 5770b0d66..54b5caa6e 100644 --- a/opentech/apply/tests/test_workflow.py +++ b/opentech/apply/tests/test_workflow.py @@ -8,11 +8,13 @@ from .factories import ActionFactory, PhaseFactory, StageFactory, WorkflowFactor class TestWorkflowCreation(SimpleTestCase): def test_can_create_workflow(self): - name = 'single_stage' stage = StageFactory() - workflow = Workflow(name, [stage]) - self.assertEqual(workflow.name, name) - self.assertCountEqual(workflow.stages, [stage]) + class NewWorkflow(Workflow): + name = 'single_stage' + stage_classes = [stage] + workflow = NewWorkflow([Form()]) + self.assertEqual(workflow.name, NewWorkflow.name) + self.assertEqual(len(workflow.stages), 1) def test_returns_first_phase_if_no_arg(self): workflow = WorkflowFactory(num_stages=1, stages__num_phases=1) @@ -49,11 +51,11 @@ class TestStageCreation(SimpleTestCase): self.assertEqual(stage.form, form) def test_can_get_next_phase(self): - stage = StageFactory(num_phases=2) + stage = StageFactory.build(num_phases=2) self.assertEqual(stage.next(stage.phases[0]), stage.phases[1]) def test_get_none_if_no_next_phase(self): - stage = StageFactory(num_phases=1) + stage = StageFactory.build(num_phases=1) self.assertEqual(stage.next(stage.phases[0]), None) diff --git a/opentech/apply/views.py b/opentech/apply/views.py index 8d8d5097c..a2b1c043a 100644 --- a/opentech/apply/views.py +++ b/opentech/apply/views.py @@ -1,14 +1,18 @@ +from django.forms import Form from django.shortcuts import render from django.template.response import TemplateResponse -from .workflow import single_stage, two_stage +from .workflow import SingleStage, DoubleStage -workflows = [single_stage, two_stage] +workflows = [SingleStage, DoubleStage] def demo_workflow(request, wf_id): - workflow = workflows[int(wf_id)-1] + wf = int(wf_id) + workflow_class = workflows[wf-1] + workflow = workflow_class([Form()] * wf) + current_phase = request.POST.get('current') if request.POST: phase = workflow.process(current_phase, request.POST['action']) diff --git a/opentech/apply/workflow.py b/opentech/apply/workflow.py index c7ff5b158..7ab64787b 100644 --- a/opentech/apply/workflow.py +++ b/opentech/apply/workflow.py @@ -1,15 +1,20 @@ import copy -from typing import Dict, Iterator, Iterable, List, Sequence, Tuple, Union +from typing import List, Sequence, Type, Union from django.forms import Form from django.utils.text import slugify class Workflow: - def __init__(self, name: str, stages: Sequence['Stage']) -> None: - self.name = name - self.stages = stages + name: str = '' + stage_classes: Sequence[Type['Stage']] = list() + + def __init__(self, forms: Sequence[Form]) -> None: + if len(self.stage_classes) != len(forms): + raise ValueError('Number of forms does not equal the number of stages') + + self.stages = [stage(form) for stage, form in zip(self.stage_classes, forms)] def current(self, current_phase: Union[str, 'Phase']) -> Union['Phase', None]: if isinstance(current_phase, Phase): @@ -230,6 +235,12 @@ class ProposalStage(Stage): rejected, ] -single_stage = Workflow('Single Stage', [ConceptStage(Form())]) -two_stage = Workflow('Two Stage', [ConceptStage(Form()), ProposalStage(Form())]) +class SingleStage(Workflow): + name = 'Single Stage' + stage_classes = [ConceptStage] + + +class DoubleStage(Workflow): + name = 'Two Stage' + stage_classes = [ConceptStage, ProposalStage] -- GitLab