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

Feature/5074 transform selectboxes data #5076

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
12 changes: 12 additions & 0 deletions src/openforms/forms/models/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from openforms.authentication.registry import register as authentication_register
from openforms.config.models import GlobalConfiguration
from openforms.data_removal.constants import RemovalMethods
from openforms.formio.datastructures import FormioConfigurationWrapper
from openforms.formio.validators import variable_key_validator
from openforms.payments.fields import PaymentBackendChoiceField
from openforms.payments.registry import register as payment_register
Expand Down Expand Up @@ -609,6 +610,17 @@ def iter_components(self, recursive=True):
for form_step in self.formstep_set.select_related("form_definition"):
yield from form_step.iter_components(recursive=recursive)

def get_total_configuration(self) -> FormioConfigurationWrapper:
"""
Return the total joined FormIO configuration of all FormSteps belonging to this Form
"""
raw_configs = self.formstep_set.select_related("form_definition").values_list(
"form_definition__configuration", flat=True
)
configs = [FormioConfigurationWrapper(config) for config in raw_configs]
configuration = sum(configs, FormioConfigurationWrapper({"components": []}))
return configuration

@transaction.atomic
def restore_old_version(
self, form_version_uuid: str, user: User | None = None
Expand Down
106 changes: 106 additions & 0 deletions src/openforms/registrations/tests/test_registration_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,112 @@ def test_registration_hook_with_failed_and_completed_payments(self):
failed_payments = submission.payments.filter(status=PaymentStatus.failed)
self.assertEqual(failed_payments.count(), 1)

def test_register_submission_transform_selectboxes_data(self):
register = Registry()

submission = SubmissionFactory.from_components(
components_list=[
{
"key": "selectboxes",
"label": "Selectboxes",
"type": "selectboxes",
"openForms": {
"dataSrc": "manual",
"translations": {},
"transformData": True,
},
"values": [
{"value": "foo", "label": "foo"},
{"value": "bar", "label": "bar"},
{"value": "baz", "label": "baz"},
],
}
],
submitted_data={"selectboxes": {"foo": True, "bar": False, "baz": True}},
completed=True,
form__registration_backend="callback",
form__registration_backend_options={
"string": "some-option",
"service": self.service.id,
},
)

# register the callback, including the assertions
test_closure = self

@register("callback")
class Plugin(BasePlugin):
verbose_name = "Assertion callback"
configuration_options = OptionsSerializer

def register_submission(self, submission, options):
state = submission.load_submission_value_variables_state()
data_to_submit = state.get_data()
test_closure.assertEqual(
data_to_submit, {"selectboxes": ["baz", "foo"]}
)
return {"result": "ok"}

# call the hook for the submission, while patching the model field registry
model_field = FormRegistrationBackend._meta.get_field("backend")
with patch_registry(model_field, register):
register_submission(submission.id, PostSubmissionEvents.on_completion)

submission.refresh_from_db()
self.assertEqual(
submission.registration_result,
{"result": "ok"},
)

def test_register_submission_transform_data_function_undefined(self):
register = Registry()

submission = SubmissionFactory.from_components(
components_list=[
{
"key": "textfield",
"label": "Field that has no transform function defined",
"type": "text",
"openForms": {
"translations": {},
"transformData": True,
},
}
],
submitted_data={"textfield": "foo"},
completed=True,
form__registration_backend="callback",
form__registration_backend_options={
"string": "some-option",
"service": self.service.id,
},
)

# register the callback, including the assertions
test_closure = self

@register("callback")
class Plugin(BasePlugin):
verbose_name = "Assertion callback"
configuration_options = OptionsSerializer

def register_submission(self, submission, options):
state = submission.load_submission_value_variables_state()
data_to_submit = state.get_data()
test_closure.assertEqual(data_to_submit, {"textfield": "foo"})
return {"result": "ok"}

# call the hook for the submission, while patching the model field registry
model_field = FormRegistrationBackend._meta.get_field("backend")
with patch_registry(model_field, register):
register_submission(submission.id, PostSubmissionEvents.on_completion)

submission.refresh_from_db()
self.assertEqual(
submission.registration_result,
{"result": "ok"},
)


class NumRegistrationsTest(TestCase):
@patch("openforms.plugins.plugin.GlobalConfiguration.get_solo")
Expand Down
27 changes: 26 additions & 1 deletion src/openforms/submissions/models/submission_value_variable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from dataclasses import dataclass, field
from datetime import date, datetime, time
from typing import TYPE_CHECKING, Any, Literal, overload
Expand All @@ -11,6 +12,8 @@
from django.utils.functional import empty
from django.utils.translation import gettext_lazy as _

from glom import glom

from openforms.formio.service import FormioData
from openforms.forms.models.form_variable import FormVariable
from openforms.typing import DataMapping, JSONEncodable, JSONObject, JSONSerializable
Expand All @@ -19,11 +22,14 @@
from openforms.variables.service import VariablesRegistry, get_static_variables

from ..constants import SubmissionValueVariableSources
from ..transform_data import TRANSFORM_DATA_MAPPING
from .submission import Submission

if TYPE_CHECKING:
from .submission_step import SubmissionStep

logger = logging.getLogger(__name__)


class ValueEncoder(DjangoJSONEncoder):
def default(self, obj: JSONEncodable | JSONSerializable) -> JSONEncodable:
Expand Down Expand Up @@ -94,6 +100,8 @@ def get_data(
)

formio_data = FormioData()
configuration = self.submission.form.get_total_configuration()

for variable_key, variable in submission_variables.items():
if (
variable.value is None
Expand All @@ -104,7 +112,24 @@ def get_data(
continue

if variable.source != SubmissionValueVariableSources.sensitive_data_cleaner:
formio_data[variable_key] = variable.value
component_configuration = configuration[variable.key]

if glom(
component_configuration, "openForms.transformData", default=None
):
transform_function = TRANSFORM_DATA_MAPPING.get(
component_configuration["type"]
)
if not transform_function:
logger.warning(
"Incorrect configuration, component of type `%s` has `openForms.transformData` "
"set to true, but no transform function is defined in TRANSFORM_DATA_MAPPING",
component_configuration["type"],
)
transform_function = lambda value: value
formio_data[variable_key] = transform_function(variable.value)
else:
formio_data[variable_key] = variable.value
return formio_data if as_formio_data else formio_data.data

def get_variables_in_submission_step(
Expand Down
8 changes: 8 additions & 0 deletions src/openforms/submissions/transform_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def transform_selectboxes_data(value: dict[str, bool] | None) -> list[str] | None:
if value:
return sorted([key for key, value in value.items() if value])


TRANSFORM_DATA_MAPPING = {
"selectboxes": transform_selectboxes_data,
}
Loading