Skip to content

Commit

Permalink
✨ [#1451] -- incorporate form logic in the submission renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-maertens committed Apr 29, 2022
1 parent 385d573 commit 0e18ad2
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/openforms/submissions/form_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def evaluate_form_logic(
action["form_step"]
)
submission_step_to_modify._is_applicable = False
if submission_step_to_modify == step:
step._is_applicable = False

if dirty:
# only keep the changes in the data, so that old values do not overwrite otherwise
Expand Down
16 changes: 12 additions & 4 deletions src/openforms/submissions/rendering/nodes.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import logging
from dataclasses import dataclass
from typing import Iterator

from ..models import SubmissionStep
from .base import Node
from .constants import RenderModes

logger = logging.getLogger(__name__)


class FormNode(Node):
"""
Expand Down Expand Up @@ -48,10 +51,15 @@ class SubmissionStepNode(Node):
def is_visible(self) -> bool:
# determine if the step as a whole is relevant or not. The stap may be not
# applicable because of form logic.
# logic_evaluated = getattr(self.step, "_form_logic_evaluated", False)
# assert (
# logic_evaluated
# ), "You should ensure that the form logic is evaluated before rendering steps!"
logic_evaluated = getattr(self.step, "_form_logic_evaluated", False)
if not logic_evaluated:
logger.warning(
"You should ensure that the form logic is evaluated before rendering "
"steps! Submission ID: %s, renderer: %r, step ID: %s",
self.step.submission.uuid,
self.renderer,
self.step.uuid,
)
return self.step.is_applicable

def render(self) -> str:
Expand Down
19 changes: 17 additions & 2 deletions src/openforms/submissions/rendering/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

from openforms.forms.models import Form

from ..form_logic import evaluate_form_logic
from ..models import Submission
from .base import Node
from .constants import RenderModes # noqa
from .nodes import FormNode, SubmissionStepNode
from .utils import get_request


@dataclass
Expand All @@ -31,6 +33,9 @@ class Renderer:
mode: str
as_html: bool

def __post_init__(self):
self.dummy_request = get_request()

@property
def form(self) -> Form:
"""
Expand All @@ -52,9 +57,19 @@ def get_children(self) -> Iterator["SubmissionStepNode"]:
"""
Produce only the direct child nodes.
"""
common_kwargs = {"renderer": self}
for step in self.steps:
submission_step_node = SubmissionStepNode(step=step, **common_kwargs)
new_configuration = evaluate_form_logic(
submission=self.submission,
step=step,
data=self.submission.data,
dirty=False,
request=self.dummy_request,
)
# update the configuration for introspection - note that we are mutating
# an instance here without persisting it to the backend on purpose!
# this replicates the run-time behaviour while filling out the form
step.form_step.form_definition.configuration = new_configuration
submission_step_node = SubmissionStepNode(renderer=self, step=step)
if not submission_step_node.is_visible:
continue

Expand Down
21 changes: 21 additions & 0 deletions src/openforms/submissions/rendering/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from urllib.parse import urlsplit

from django.conf import settings
from django.http import HttpRequest
from django.test import RequestFactory
from django.urls import reverse


def get_request() -> HttpRequest:
"""
Build a mock request.
:meta private:
"""
base = urlsplit(settings.BASE_URL)
request = RequestFactory().get(
reverse("core:form-list"),
HTTP_HOST=base.netloc,
**{"wsgi.url_scheme": base.scheme},
)
return request
66 changes: 64 additions & 2 deletions src/openforms/submissions/tests/renderer/test_renderer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from django.test import TestCase

from rest_framework.reverse import reverse

from openforms.forms.tests.factories import FormFactory, FormStepFactory

from ...rendering import Renderer, RenderModes
from ...rendering.nodes import FormNode, SubmissionStepNode
from ..factories import SubmissionFactory, SubmissionStepFactory
from ..form_logic.factories import FormLogicFactory


class FormNodeTests(TestCase):
Expand All @@ -16,8 +19,30 @@ def setUpTestData(cls):
name="public name",
internal_name="internal name",
)
step1 = FormStepFactory.create(form=form, form_definition__name="Step 1")
step2 = FormStepFactory.create(form=form, form_definition__name="Step 2")
step1 = FormStepFactory.create(
form=form,
form_definition__name="Step 1",
form_definition__configuration={
"components": [
{
"type": "textfield",
"key": "input1",
}
]
},
)
step2 = FormStepFactory.create(
form=form,
form_definition__name="Step 2",
form_definition__configuration={
"components": [
{
"type": "textfield",
"key": "input2",
}
]
},
)
submission = SubmissionFactory.create(form=form)
sstep1 = SubmissionStepFactory.create(submission=submission, form_step=step1)
sstep2 = SubmissionStepFactory.create(submission=submission, form_step=step2)
Expand All @@ -41,3 +66,40 @@ def test_renderer_build_nodelist(self):
self.assertEqual(len(nodes), 3)
rendered = [node.render() for node in nodes]
self.assertEqual(rendered, ["public name", "Step 1", "Step 2"])

def test_renderer_with_form_logic_and_disabled_step(self):
# set up logic
form = self.submission.form
form_step2_path = reverse(
"api:form-steps-detail",
kwargs={"form_uuid_or_slug": form.uuid, "uuid": self.sstep2.form_step.uuid},
)
FormLogicFactory.create(
form=form,
json_logic_trigger={"==": [{"var": "input1"}, "disabled-step-2"]},
actions=[
{
"form_step": f"http://example.com{form_step2_path}",
"action": {
"name": "Step is not applicable",
"type": "step-not-applicable",
},
}
],
)
# set up data that marks step as not-applicable
self.sstep1.data = {"input1": "disabled-step-2"}
self.sstep1.save()
renderer = Renderer(
submission=self.submission, mode=RenderModes.pdf, as_html=True
)

nodes = [
node
for node in renderer
if isinstance(node, (FormNode, SubmissionStepNode))
]

self.assertEqual(len(nodes), 2)
enabled_step_node = nodes[1]
self.assertEqual(enabled_step_node.step, self.sstep1)

0 comments on commit 0e18ad2

Please sign in to comment.