diff --git a/app/all_questions/metadata_utils.py b/app/all_questions/metadata_utils.py
index f3656de6..3ec28f55 100644
--- a/app/all_questions/metadata_utils.py
+++ b/app/all_questions/metadata_utils.py
@@ -368,17 +368,22 @@ def build_components_from_page(
condition_name = next_config["condition"]
condition_config = next(fc for fc in form_conditions if fc["name"] == condition_name)
destination = index_of_printed_headers[next_config["path"]]["heading_number"]
- condition_value = next(
+ text_with_coordinators = ""
+ for condition in [
cc for cc in condition_config["value"]["conditions"] if cc["field"]["name"] == c["name"]
- )["value"]["value"]
- condition_text = determine_display_value_for_condition(
- condition_value,
- list_name=c["list"] if "list" in c else None,
- form_lists=form_lists,
- lang=lang,
- )
+ ]:
+ condition_value = condition["value"]["value"]
+ condition_text = determine_display_value_for_condition(
+ condition_value,
+ list_name=c["list"] if "list" in c else None,
+ form_lists=form_lists,
+ lang=lang,
+ )
+ if condition.get("coordinator"):
+ text_with_coordinators += f" {condition.get('coordinator')} "
+ text_with_coordinators += f"'{condition_text}'"
text.append(
- f"If '{condition_text}', go to {destination}"
+ f"If {text_with_coordinators}, go to {destination}"
if lang == "en"
else (f"Os '{condition_text}', ewch i {destination}")
)
diff --git a/app/db/queries/application.py b/app/db/queries/application.py
index 3bcfde6e..cbe7496d 100644
--- a/app/db/queries/application.py
+++ b/app/db/queries/application.py
@@ -32,16 +32,6 @@ def get_form_for_component(component: Component) -> Form:
return form
-def get_template_page_by_display_path(display_path: str) -> Page:
- page = (
- db.session.query(Page)
- .where(Page.display_path == display_path)
- .where(Page.is_template == True) # noqa:E712
- .one_or_none()
- )
- return page
-
-
def get_form_by_id(form_id: str) -> Form:
form = db.session.query(Form).where(Form.form_id == form_id).one_or_none()
return form
@@ -62,6 +52,11 @@ def get_list_by_id(list_id: str) -> Lizt:
return lizt
+def get_list_by_name(list_name: str) -> Lizt:
+ lizt = db.session.query(Lizt).filter_by(name=list_name).first()
+ return lizt
+
+
def _initiate_cloned_component(to_clone: Component, new_page_id=None, new_theme_id=None):
clone = Component(**to_clone.as_dict())
@@ -746,3 +741,22 @@ def move_form_up(section_id, form_index_to_move_up: int):
section.forms = swap_elements_in_list(section.forms, list_index_to_move_up, list_index_to_move_up - 1)
db.session.commit()
+
+
+def insert_list(list_config: dict, do_commit: bool = True) -> Lizt:
+ new_list = Lizt(
+ is_template=True,
+ name=list_config.get("name"),
+ title=list_config.get("title"),
+ type=list_config.get("type"),
+ items=list_config.get("items"),
+ )
+ try:
+ db.session.add(new_list)
+ except Exception as e:
+ print(e)
+ raise e
+ if do_commit:
+ db.session.commit()
+ db.session.flush() # flush to get the list id
+ return new_list
diff --git a/app/export_config/generate_form.py b/app/export_config/generate_form.py
index b08e0036..31bc9d34 100644
--- a/app/export_config/generate_form.py
+++ b/app/export_config/generate_form.py
@@ -1,4 +1,5 @@
import copy
+from dataclasses import asdict
from app.db.models import Component
from app.db.models import Form
@@ -6,7 +7,7 @@
from app.db.models.application_config import READ_ONLY_COMPONENTS
from app.db.models.application_config import ComponentType
from app.db.queries.application import get_list_by_id
-from app.db.queries.application import get_template_page_by_display_path
+from app.shared.data_classes import ConditionValue
BASIC_FORM_STRUCTURE = {
"startPage": None,
@@ -43,34 +44,26 @@ def build_conditions(component: Component) -> list:
"""
results = []
for condition in component.conditions:
- condition_entry = {
- "field": {
- "name": component.runner_component_name,
- "type": component.type.value,
- "display": component.title,
- },
- "operator": condition["operator"],
- "value": condition["value"],
- }
-
- # Add 'coordinator' only if it exists
- if condition.get("coordinator"):
- condition_entry["coordinator"] = condition.get("coordinator")
-
- if condition["name"] in [c["name"] for c in results]:
- # If this condition already exists, add it to the existing condition
- existing_condition = next(c for c in results if c["name"] == condition["name"])
- existing_condition["value"]["conditions"].append(condition_entry)
- continue
-
result = {
"displayName": condition["display_name"],
"name": condition["name"],
- "value": {
- "name": condition["display_name"],
- "conditions": [condition_entry],
- },
+ "value": asdict(
+ ConditionValue(
+ name=condition["value"]["name"],
+ conditions=[],
+ )
+ ),
}
+ for sc in condition["value"]["conditions"]:
+ sub_condition = {
+ "field": sc["field"],
+ "operator": sc["operator"],
+ "value": sc["value"],
+ }
+ # only add coordinator if it exists
+ if "coordinator" in sc and sc.get("coordinator") is not None:
+ sub_condition["coordinator"] = sc.get("coordinator", None)
+ result["value"]["conditions"].append(sub_condition)
results.append(result)
@@ -118,19 +111,12 @@ def build_component(component: Component) -> dict:
return built_component
-def build_page(page: Page = None, page_display_path: str = None) -> dict:
+def build_page(page: Page = None) -> dict:
"""
- Builds the form runner JSON structure for the supplied page. If that page is None, retrieves a template
- page with the display_path matching page_display_path.
-
- This accounts for conditional logic where the destination target will be the display path of a template
- page, but that page does not actually live in the main hierarchy as branching logic uses a fixed set of
- conditions at this stage.
+ Builds the form runner JSON structure for the supplied page.
Then builds all the components on this page and adds them to the page json structure
"""
- if not page:
- page = get_template_page_by_display_path(page_display_path)
built_page = copy.deepcopy(BASIC_PAGE_STRUCTURE)
built_page.update(
{
@@ -173,26 +159,12 @@ def build_navigation(partial_form_json: dict, input_pages: list[Page]) -> dict:
for component in page.components:
if not component.conditions:
continue
- form_json_conditions = build_conditions(component)
has_conditions = True
+ form_json_conditions = build_conditions(component)
partial_form_json["conditions"].extend(form_json_conditions)
for condition in component.conditions:
- if condition["destination_page_path"] == "CONTINUE":
- destination_path = f"/{next_path}"
- else:
- destination_path = f"/{condition['destination_page_path'].lstrip('/')}"
- # TODO No longer needed since db schema change?
- # If this points to a pre-built page flow, add that in now (it won't be in the input)
- # if (
- # destination_path not in [page["path"] for page in partial_form_json["pages"]]
- # and not destination_path == "/summary"
- # ):
- # sub_page = build_page(page_display_path=destination_path[1:])
- # if not sub_page.get("next", None):
- # sub_page["next"] = [{"path": f"/{next_path}"}]
-
- # partial_form_json["pages"].append(sub_page)
+ destination_path = f"/{condition['destination_page_path'].lstrip('/')}"
this_page_in_results["next"].append(
{
@@ -245,20 +217,26 @@ def build_start_page(content: str, form: Form) -> dict:
"title": form.name_in_apply_json["en"],
"path": f"/intro-{human_to_kebab_case(form.name_in_apply_json['en'])}",
"controller": "./pages/start.js",
- "next": [{"path": f"/{form.pages[0].display_path}"}],
}
)
- ask_about = "
We will ask you about:
"
- for page in form.pages:
- ask_about += f"- {page.name_in_apply_json['en']}
"
- ask_about += "
"
+ ask_about = None
+ if len(form.pages) > 0:
+ ask_about = 'We will ask you about:
'
+ for page in form.pages:
+ ask_about += f"- {page.name_in_apply_json['en']}
"
+ ask_about += "
"
+ start_page.update(
+ {
+ "next": [{"path": f"/{form.pages[0].display_path}"}],
+ }
+ )
start_page["components"].append(
{
"name": "start-page-content",
"options": {},
"type": "Html",
- "content": f"{content}
{ask_about}",
+ "content": f'{content or ""}
{ask_about or ""}',
"schema": {},
}
)
diff --git a/app/import_config/files_to_import/coinditions.json b/app/import_config/files_to_import/coinditions.json
new file mode 100644
index 00000000..c476eb9d
--- /dev/null
+++ b/app/import_config/files_to_import/coinditions.json
@@ -0,0 +1,183 @@
+{
+ "metadata": {},
+ "startPage": "/first-page",
+ "pages": [
+ {
+ "title": "First page",
+ "path": "/first-page",
+ "components": [
+ {
+ "name": "YFTKhr",
+ "options": {},
+ "type": "RadiosField",
+ "title": "org type",
+ "list": "PaUvtG"
+ }
+ ],
+ "next": [
+ {
+ "path": "/org-type-a",
+ "condition": "GDqNNS"
+ },
+ {
+ "path": "/org-type-b",
+ "condition": "aTqAcd"
+ },
+ {
+ "path": "/org-type-c",
+ "condition": "LQOPpJ"
+ }
+ ]
+ },
+ {
+ "title": "Summary",
+ "path": "/summary",
+ "controller": "./pages/summary.js",
+ "components": []
+ },
+ {
+ "path": "/org-type-a",
+ "title": "Org Type A",
+ "components": [],
+ "next": [
+ {
+ "path": "/summary"
+ }
+ ]
+ },
+ {
+ "path": "/org-type-b",
+ "title": "Org type B",
+ "components": [],
+ "next": [
+ {
+ "path": "/summary"
+ }
+ ]
+ },
+ {
+ "path": "/org-type-c",
+ "title": "Org type C",
+ "components": [],
+ "next": [
+ {
+ "path": "/summary"
+ }
+ ]
+ }
+ ],
+ "lists": [
+ {
+ "title": "org type",
+ "name": "PaUvtG",
+ "type": "string",
+ "items": [
+ {
+ "text": "A",
+ "value": "A"
+ },
+ {
+ "text": "B",
+ "value": "B"
+ },
+ {
+ "text": "C1",
+ "value": "C1"
+ },
+ {
+ "text": "C2",
+ "value": "C2"
+ }
+ ]
+ }
+ ],
+ "sections": [],
+ "conditions": [
+ {
+ "displayName": "org type a",
+ "name": "GDqNNS",
+ "value": {
+ "name": "org type a",
+ "conditions": [
+ {
+ "field": {
+ "name": "YFTKhr",
+ "type": "RadiosField",
+ "display": "org type"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "A",
+ "display": "A"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "org type b",
+ "name": "aTqAcd",
+ "value": {
+ "name": "org type b",
+ "conditions": [
+ {
+ "field": {
+ "name": "YFTKhr",
+ "type": "RadiosField",
+ "display": "org type"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "B",
+ "display": "B"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "org type c",
+ "name": "LQOPpJ",
+ "value": {
+ "name": "org type c",
+ "conditions": [
+ {
+ "field": {
+ "name": "YFTKhr",
+ "type": "RadiosField",
+ "display": "org type"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "C1",
+ "display": "C1"
+ }
+ },
+ {
+ "coordinator": "or",
+ "field": {
+ "name": "YFTKhr",
+ "type": "RadiosField",
+ "display": "org type"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "C2",
+ "display": "C2"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "fees": [],
+ "outputs": [],
+ "version": 2,
+ "skipSummary": false,
+ "feeOptions": {},
+ "markAsComplete": false
+}
diff --git a/app/import_config/files_to_import/organisation-information-cof.json b/app/import_config/files_to_import/organisation-information-cof.json
new file mode 100644
index 00000000..98ca88c7
--- /dev/null
+++ b/app/import_config/files_to_import/organisation-information-cof.json
@@ -0,0 +1,1143 @@
+{
+ "metadata": {},
+ "startPage": "/organisation-information",
+ "backLinkText": "Go back to application overview",
+ "pages": [
+ {
+ "title": "Organisation information",
+ "path": "/organisation-information",
+ "components": [
+ {
+ "name": "orUXQc",
+ "options": {},
+ "type": "Para",
+ "content": "\n\n\n
\n- unique tracker number
\n- organisation name
\n- alternative names of your organisation (if applicable)
\n- organisation purpose
\n- organisation activities
\n- previous experience of similar projects (if applicable)
\n- organisation type
\n- charity number (if applicable)
\n- organisation registration number (if applicable)
\n- organisation regulatory body
\n- parent company details (if applicable)
\n- organisation address and social media
\n- partner organisation (if applicable)
\n
\n
",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/organisation-names"
+ }
+ ],
+ "controller": "./pages/start.js"
+ },
+ {
+ "path": "/organisation-names",
+ "title": "Organisation names",
+ "components": [
+ {
+ "name": "WWWWxy",
+ "options": {
+ "classes": "govuk-!-width-full"
+ },
+ "type": "TextField",
+ "title": "Your expression of interest (EOI) application reference",
+ "hint": "This was included in the email we sent you to confirm your successful EOI.\nFor example, 'COF-EOI-##-######'
",
+ "schema": {}
+ },
+ {
+ "name": "YdtlQZ",
+ "options": {
+ "classes": "govuk-!-width-full"
+ },
+ "type": "TextField",
+ "title": "Organisation name",
+ "hint": "This must match your registered legal organisation name",
+ "schema": {}
+ },
+ {
+ "name": "iBCGxY",
+ "options": {},
+ "type": "YesNoField",
+ "title": "Does your organisation use any other names?",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/alternative-names-of-your-organisation",
+ "condition": "RzJfsD"
+ },
+ {
+ "path": "/purpose-and-activities",
+ "condition": "IshRJh"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/alternative-names-of-your-organisation",
+ "title": "Alternative names of your organisation",
+ "components": [
+ {
+ "name": "XFvKwZ",
+ "options": {},
+ "type": "Html",
+ "content": "",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/purpose-and-activities"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/purpose-and-activities",
+ "title": "Purpose and activities",
+ "components": [
+ {
+ "name": "emVGxS",
+ "options": {
+ "maxWords": "500"
+ },
+ "type": "FreeTextField",
+ "title": "What is your organisation's main purpose?",
+ "schema": {},
+ "hint": "This is what the organisation was set up to achieve."
+ },
+ {
+ "name": "tgnZof",
+ "options": {},
+ "type": "Html",
+ "content": "",
+ "schema": {}
+ },
+ {
+ "name": "BBlCko",
+ "options": {},
+ "type": "YesNoField",
+ "title": "Have you delivered projects like this before?",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/previous-projects-similar-to-this-one",
+ "condition": "iUHNKI"
+ },
+ {
+ "path": "/how-your-organisation-is-classified",
+ "condition": "RzrXoE"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/previous-projects-similar-to-this-one",
+ "title": "Previous projects similar to this one",
+ "components": [
+ {
+ "name": "SXfYwl",
+ "options": {},
+ "type": "Html",
+ "content": "",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/how-your-organisation-is-classified"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/how-your-organisation-is-classified",
+ "title": "How your organisation is classified",
+ "components": [
+ {
+ "name": "lajFtB",
+ "options": {},
+ "type": "RadiosField",
+ "title": "Type of organisation",
+ "hint": "Select one option",
+ "list": "ATpRJn",
+ "values": {
+ "type": "listRef"
+ },
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/how-your-organisation-is-classified-other",
+ "condition": "HkIMoI"
+ },
+ {
+ "path": "/company-registration-details",
+ "condition": "mNExjy"
+ },
+ {
+ "path": "/charity-registration-details",
+ "condition": "EfGsOh"
+ },
+ {
+ "path": "/trading-subsidiaries",
+ "condition": "zZSerd"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/how-your-organisation-is-classified-other",
+ "title": "How your organisation is classified",
+ "components": [
+ {
+ "name": "plmwJv",
+ "options": {
+ "classes": "govuk-input--width-20"
+ },
+ "type": "TextField",
+ "title": "Type of organisation (Other)",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/registration-details"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/registration-details",
+ "title": "Registration details",
+ "components": [
+ {
+ "name": "GvPSna",
+ "options": {},
+ "type": "RadiosField",
+ "title": "Which regulatory body is your company registered with?",
+ "hint": "Select one option",
+ "list": "jcNxEX",
+ "values": {
+ "type": "listRef"
+ },
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/about-your-organisation-eBkQGy",
+ "condition": "BiTnsS"
+ },
+ {
+ "path": "/trading-subsidiaries"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/charity-registration-details",
+ "title": "Charity registration details",
+ "components": [
+ {
+ "name": "aHIGbK",
+ "options": {
+ "classes": "govuk-!-width-two-thirds"
+ },
+ "type": "NumberField",
+ "title": "Charity number ",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/trading-subsidiaries"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/trading-subsidiaries",
+ "title": "Trading subsidiaries",
+ "components": [
+ {
+ "name": "DwfHtk",
+ "options": {},
+ "type": "YesNoField",
+ "title": "Is your organisation a trading subsidiary of a parent company?",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/parent-organisation-details",
+ "condition": "UlBxQv"
+ },
+ {
+ "path": "/organisation-address",
+ "condition": "kcWadF"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/parent-organisation-details",
+ "title": "Parent organisation details",
+ "components": [
+ {
+ "name": "MPNlZx",
+ "options": {
+ "classes": "govuk-!-width-full"
+ },
+ "type": "TextField",
+ "title": "Name of parent organisation",
+ "schema": {}
+ },
+ {
+ "name": "MyiYMw",
+ "options": {},
+ "type": "DatePartsField",
+ "title": "Date parent organisation was established",
+ "hint": "For example, 27 3 2007",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/organisation-address"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/organisation-address",
+ "title": "Organisation address",
+ "components": [
+ {
+ "name": "ZQolYb",
+ "options": {},
+ "type": "UkAddressField",
+ "title": "Organisation address",
+ "schema": {}
+ },
+ {
+ "name": "zsoLdf",
+ "options": {},
+ "type": "YesNoField",
+ "title": "Is your correspondence address different to the organisation address?",
+ "schema": {}
+ },
+ {
+ "name": "zyWOmY",
+ "options": {},
+ "type": "Html",
+ "content": "",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/correspondence-address",
+ "condition": "fvQqsO"
+ },
+ {
+ "path": "/joint-applications",
+ "condition": "MnYndb"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/correspondence-address",
+ "title": "Correspondence address",
+ "components": [
+ {
+ "name": "VhkCbM",
+ "options": {},
+ "type": "UkAddressField",
+ "title": "Correspondence address",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/joint-applications"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/joint-applications",
+ "title": "Joint applications",
+ "components": [
+ {
+ "name": "hnLurH",
+ "options": {},
+ "type": "YesNoField",
+ "title": "Is your application a joint bid in partnership with other organisations?",
+ "schema": {},
+ "values": {
+ "type": "listRef"
+ }
+ }
+ ],
+ "next": [
+ {
+ "path": "/partner-organisation-details",
+ "condition": "gCTswV"
+ },
+ {
+ "path": "/summary",
+ "condition": "erqBEX"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/partner-organisation-details",
+ "title": "Partner organisation details",
+ "components": [
+ {
+ "name": "APSjeB",
+ "options": {
+ "classes": "govuk-!-width-full"
+ },
+ "type": "TextField",
+ "title": "Partner organisation name",
+ "schema": {}
+ },
+ {
+ "name": "biTJjF",
+ "options": {},
+ "type": "UkAddressField",
+ "title": "Partner organisation address",
+ "schema": {}
+ },
+ {
+ "name": "IkmvEt",
+ "options": {
+ "maxWords": "500",
+ "hideTitle": true
+ },
+ "type": "FreeTextField",
+ "title": "Tell us about your partnership and how you plan to work together",
+ "schema": {},
+ "hint": "Tell us about your partnership and how you plan to work togetherIf you are working in partnership with more than one organisation, include their details here"
+ }
+ ],
+ "next": [
+ {
+ "path": "/summary"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/summary",
+ "title": "Check your answers",
+ "components": [],
+ "next": [],
+ "controller": "./pages/summary.js"
+ },
+ {
+ "path": "/company-registration-details",
+ "title": "Company registration details",
+ "components": [
+ {
+ "name": "GlPmCX",
+ "options": {
+ "classes": "govuk-!-width-two-thirds"
+ },
+ "type": "TextField",
+ "title": "Company registration number",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/registration-details"
+ }
+ ],
+ "section": "JBqDtK"
+ },
+ {
+ "path": "/about-your-organisation-eBkQGy",
+ "title": "About your organisation",
+ "components": [
+ {
+ "name": "nITyAP",
+ "options": {},
+ "type": "Html",
+ "content": "Registration details
",
+ "schema": {}
+ },
+ {
+ "name": "zsbmRx",
+ "options": {
+ "classes": "govuk-input--width-20"
+ },
+ "type": "TextField",
+ "title": "Which regulatory body is your company registered with? (Other)",
+ "schema": {}
+ }
+ ],
+ "next": [
+ {
+ "path": "/trading-subsidiaries"
+ }
+ ],
+ "section": "JBqDtK"
+ }
+ ],
+ "lists": [
+ {
+ "title": "Type of organisation",
+ "name": "ATpRJn",
+ "type": "string",
+ "items": [
+ {
+ "text": "Charitable incorporated organisation (CIO)",
+ "value": "CIO"
+ },
+ {
+ "text": "Co-operative, such as a community benefit society",
+ "value": "COOP"
+ },
+ {
+ "text": "Community interest company (CIC)",
+ "value": "CIC"
+ },
+ {
+ "text": "Company limited by guarantee",
+ "value": "CLG"
+ },
+ {
+ "text": "Scottish charitable incorporated organisation (SCIO)",
+ "value": "SCIO"
+ },
+ {
+ "text": "Parish, town or community council",
+ "value": "PTC"
+ },
+ {
+ "text": "Trust port",
+ "value": "Trust port"
+ },
+ {
+ "text": "Other",
+ "value": "Other"
+ }
+ ]
+ },
+ {
+ "title": "Which regulatory body is your company registered with",
+ "name": "jcNxEX",
+ "type": "string",
+ "items": [
+ {
+ "text": "Companies House",
+ "value": "Companies House"
+ },
+ {
+ "text": "Other",
+ "value": "Other"
+ }
+ ]
+ },
+ {
+ "title": "Section as Complete",
+ "name": "dwPYuR",
+ "type": "string",
+ "items": [
+ {
+ "text": "Yes, I’ve completed this section",
+ "value": "Yes"
+ },
+ {
+ "text": "No, I’ll come back to it later",
+ "value": "No"
+ }
+ ]
+ }
+ ],
+ "sections": [
+ {
+ "name": "JBqDtK",
+ "title": "Organisation information"
+ }
+ ],
+ "conditions": [
+ {
+ "displayName": "Does your organisation use any other names-yes",
+ "name": "RzJfsD",
+ "value": {
+ "name": "Does your organisation use any other names-yes",
+ "conditions": [
+ {
+ "field": {
+ "name": "iBCGxY",
+ "type": "YesNoField",
+ "display": "Does your organisation use any other names?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "true",
+ "display": "true"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Does your organisation use any other names-no",
+ "name": "IshRJh",
+ "value": {
+ "name": "Does your organisation use any other names-no",
+ "conditions": [
+ {
+ "field": {
+ "name": "iBCGxY",
+ "type": "YesNoField",
+ "display": "Does your organisation use any other names?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "false",
+ "display": "false"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Have you delivered projects like this before-main-purpose-yes",
+ "name": "iUHNKI",
+ "value": {
+ "name": "Have you delivered projects like this before-main-purpose-yes",
+ "conditions": [
+ {
+ "field": {
+ "name": "BBlCko",
+ "type": "YesNoField",
+ "display": "Have you delivered projects like this before?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "true",
+ "display": "true"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Have you delivered projects like this before-main-purpose-no",
+ "name": "RzrXoE",
+ "value": {
+ "name": "Have you delivered projects like this before-main-purpose-no",
+ "conditions": [
+ {
+ "field": {
+ "name": "BBlCko",
+ "type": "YesNoField",
+ "display": "Have you delivered projects like this before?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "false",
+ "display": "false"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Type of organisation-other",
+ "name": "HkIMoI",
+ "value": {
+ "name": "Type of organisation-other",
+ "conditions": [
+ {
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "Other",
+ "display": "Other"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your organisation a trading subsidiary of a parent company-yes",
+ "name": "UlBxQv",
+ "value": {
+ "name": "Is your organisation a trading subsidiary of a parent company-yes",
+ "conditions": [
+ {
+ "field": {
+ "name": "DwfHtk",
+ "type": "YesNoField",
+ "display": "Is your organisation a trading subsidiary of a parent company?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "true",
+ "display": "true"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your correspondence address different to the organisation address-yes",
+ "name": "fvQqsO",
+ "value": {
+ "name": "Is your correspondence address different to the organisation address-yes",
+ "conditions": [
+ {
+ "field": {
+ "name": "zsoLdf",
+ "type": "YesNoField",
+ "display": "Is your correspondence address different to the organisation address?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "true",
+ "display": "true"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your correspondence address different to the organisation address-no",
+ "name": "MnYndb",
+ "value": {
+ "name": "Is your correspondence address different to the organisation address-no",
+ "conditions": [
+ {
+ "field": {
+ "name": "zsoLdf",
+ "type": "YesNoField",
+ "display": "Is your correspondence address different to the organisation address?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "false",
+ "display": "false"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your application a joint bid in partnership with another organisation-yes",
+ "name": "gCTswV",
+ "value": {
+ "name": "Is your application a joint bid in partnership with another organisation-yes",
+ "conditions": [
+ {
+ "field": {
+ "name": "hnLurH",
+ "type": "YesNoField",
+ "display": "Is your application a joint bid in partnership with another organisation?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "true",
+ "display": "true"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your application a joint bid in partnership with another organisation-no",
+ "name": "erqBEX",
+ "value": {
+ "name": "Is your application a joint bid in partnership with another organisation-no",
+ "conditions": [
+ {
+ "field": {
+ "name": "hnLurH",
+ "type": "YesNoField",
+ "display": "Is your application a joint bid in partnership with another organisation?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "false",
+ "display": "false"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Is your organisation a trading subsidiary of a parent company-no",
+ "name": "kcWadF",
+ "value": {
+ "name": "Is your organisation a trading subsidiary of a parent company-no",
+ "conditions": [
+ {
+ "field": {
+ "name": "DwfHtk",
+ "type": "YesNoField",
+ "display": "Is your organisation a trading subsidiary of a parent company?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "false",
+ "display": "false"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Type of organisation- option(two, three, four)",
+ "name": "mNExjy",
+ "value": {
+ "name": "Type of organisation- option(two, three, four)",
+ "conditions": [
+ {
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "COOP",
+ "display": "COOP"
+ }
+ },
+ {
+ "coordinator": "or",
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "CIC",
+ "display": "CIC"
+ }
+ },
+ {
+ "coordinator": "or",
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "CLG",
+ "display": "CLG"
+ }
+ },
+ {
+ "coordinator": "or",
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "Trust port",
+ "display": "Trust port"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Type of organisation-option(one, five)",
+ "name": "EfGsOh",
+ "value": {
+ "name": "Type of organisation-option(one, five)",
+ "conditions": [
+ {
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "CIO",
+ "display": "CIO"
+ }
+ },
+ {
+ "coordinator": "or",
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "SCIO",
+ "display": "SCIO"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Which regulatory body is your company registered with-other",
+ "name": "BiTnsS",
+ "value": {
+ "name": "Which regulatory body is your company registered with-other",
+ "conditions": [
+ {
+ "field": {
+ "name": "GvPSna",
+ "type": "RadiosField",
+ "display": "Which regulatory body is your company registered with?"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "Other",
+ "display": "Other"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "displayName": "Type of organisation-parish",
+ "name": "zZSerd",
+ "value": {
+ "name": "Type of organisation-parish",
+ "conditions": [
+ {
+ "field": {
+ "name": "lajFtB",
+ "type": "RadiosField",
+ "display": "Type of organisation"
+ },
+ "operator": "is",
+ "value": {
+ "type": "Value",
+ "value": "PTC",
+ "display": "PTC"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "fees": [],
+ "outputs": [
+ {
+ "name": "update-form",
+ "title": "Update form in application store",
+ "type": "savePerPage",
+ "outputConfiguration": {
+ "savePerPageUrl": "True"
+ }
+ }
+ ],
+ "version": 2,
+ "skipSummary": false,
+ "name": "Apply for funding to save an asset in your community",
+ "feedback": {
+ "feedbackForm": false,
+ "url": ""
+ },
+ "phaseBanner": {
+ "phase": "beta"
+ }
+}
diff --git a/app/import_config/load_form_json.py b/app/import_config/load_form_json.py
index b3d47966..f5f768f7 100644
--- a/app/import_config/load_form_json.py
+++ b/app/import_config/load_form_json.py
@@ -3,6 +3,10 @@
import json
import os
import sys
+from uuid import UUID
+
+from app.db.queries.application import get_list_by_name
+from app.db.queries.application import insert_list
sys.path.insert(1, ".")
from dataclasses import asdict # noqa:E402
@@ -12,50 +16,64 @@
from app.db.models import Component # noqa:E402
from app.db.models import ComponentType # noqa:E402
from app.db.models import Form # noqa:E402
-from app.db.models import Lizt # noqa:E402
from app.db.models import Page # noqa:E402
from app.shared.data_classes import Condition # noqa:E402
+from app.shared.data_classes import ConditionValue # noqa:E402
from app.shared.helpers import find_enum # noqa:E402
-def add_conditions_to_components(db, page, conditions):
+def _build_condition(condition_data, destination_page_path) -> Condition:
+ sub_conditions = []
+ for c in condition_data["value"]["conditions"]:
+ sc = {
+ "field": c["field"],
+ "value": c["value"],
+ "operator": c["operator"],
+ }
+ if "coordinator" in c and c.get("coordinator"):
+ sc["coordinator"] = c.get("coordinator")
+ sub_conditions.append(sc)
+ condition_value = ConditionValue(name=condition_data["displayName"], conditions=sub_conditions)
+ result = Condition(
+ name=condition_data["name"],
+ display_name=condition_data["displayName"],
+ value=condition_value,
+ destination_page_path=destination_page_path,
+ )
+ return result
+
+
+def _get_component_by_runner_name(db, runner_component_name):
+
+ return db.session.query(Component).filter(Component.runner_component_name == runner_component_name).first()
+
+
+def add_conditions_to_components(db, page: dict, conditions: dict):
# Convert conditions list to a dictionary for faster lookup
conditions_dict = {cond["name"]: cond for cond in conditions}
# Initialize a cache for components to reduce database queries
components_cache = {}
- for path in page["next"]:
- if "condition" in path:
- target_condition_name = path["condition"]
- # Use the conditions dictionary for faster lookup
- if target_condition_name in conditions_dict:
- condition_data = conditions_dict[target_condition_name]
- for condition in condition_data["value"]["conditions"]:
- condition_name = condition_data["name"]
- condition_display_name = condition_data["displayName"]
- runner_component_name = condition["field"]["name"]
+ if "next" in page:
+ for path in page["next"]:
+ if "condition" in path:
+ target_condition_name = path["condition"]
+ # Use the conditions dictionary for faster lookup
+ if target_condition_name in conditions_dict:
+ condition_data = conditions_dict[target_condition_name]
+ # for condition in condition_data["value"]["conditions"]:
+ runner_component_name = condition_data["value"]["conditions"][0]["field"]["name"]
# Use the cache to reduce database queries
if runner_component_name not in components_cache:
- component_to_update = (
- db.session.query(Component)
- .filter(Component.runner_component_name == runner_component_name)
- .first()
- )
+ component_to_update = _get_component_by_runner_name(db, runner_component_name)
components_cache[runner_component_name] = component_to_update
else:
component_to_update = components_cache[runner_component_name]
# Create a new Condition instance with a different variable name
- new_condition = Condition(
- name=condition_name,
- display_name=condition_display_name,
- value=condition["value"],
- coordinator=condition.get("coordinator", None),
- operator=condition.get("operator", None),
- destination_page_path=path["path"],
- )
+ new_condition = _build_condition(condition_data, destination_page_path=path["path"])
# Add the new condition to the conditions list of the component to update
if component_to_update.conditions:
@@ -64,34 +82,27 @@ def add_conditions_to_components(db, page, conditions):
component_to_update.conditions = [asdict(new_condition)]
+def _find_list_and_create_if_not_existing(list_name: str, all_lists_in_form: list[dict]) -> UUID:
+ list_from_form = next(li for li in all_lists_in_form if li["name"] == list_name)
+
+ # Check if this list already exists in the database
+ existing_list = get_list_by_name(list_name=list_name)
+ if existing_list:
+ return existing_list.list_id
+
+ # If it doesn't, insert new list
+ new_list = insert_list(do_commit=False, list_config={"is_template": True, **list_from_form})
+ return new_list.list_id
+
+
def insert_component_as_template(component, page_id, page_index, lizts):
# if component has a list, insert the list into the database
list_id = None
component_list = component.get("list", None)
if component_list:
- for li in lizts:
- if li["name"] == component_list:
- # Check if the list already exists
- existing_list = db.session.query(Lizt).filter_by(name=li.get("name")).first()
- if existing_list is None:
- new_list = Lizt(
- is_template=True,
- name=li.get("name"),
- title=li.get("title"),
- type=li.get("type"),
- items=li.get("items"),
- )
- try:
- db.session.add(new_list)
- except Exception as e:
- print(e)
- raise e
- db.session.flush() # flush to get the list id
- list_id = new_list.list_id
- else:
- # If the list already exists, you can use its ID or handle it as needed
- list_id = existing_list.list_id
- break
+ list_id = _find_list_and_create_if_not_existing(list_name=component_list, all_lists_in_form=lizts)
+
+ # establish component type
component_type = component.get("type", None)
if component_type is None or find_enum(ComponentType, component_type) is None:
raise ValueError(f"Component type not found: {component_type}")
@@ -184,9 +195,8 @@ def insert_form_config(form_config, form_id):
def insert_form_as_template(form):
- form_name = next(p for p in form["pages"] if p.get("controller") and p.get("controller").endswith("start.js"))[
- "title"
- ]
+ start_page_path = form.get("startPage")
+ form_name = next(p for p in form["pages"] if p["path"] == start_page_path)["title"]
new_form = Form(
section_id=None,
name_in_apply_json={"en": form_name},
diff --git a/app/shared/data_classes.py b/app/shared/data_classes.py
index c58b151d..400c9b6a 100644
--- a/app/shared/data_classes.py
+++ b/app/shared/data_classes.py
@@ -4,14 +4,26 @@
from typing import Optional
+@dataclass
+class SubCondition:
+ field: dict
+ operator: str
+ value: dict
+ coordinator: Optional[str]
+
+
+@dataclass
+class ConditionValue:
+ name: str
+ conditions: list[SubCondition]
+
+
@dataclass
class Condition:
name: str
display_name: str
- value: str
- operator: str
+ value: ConditionValue
destination_page_path: str
- coordinator: Optional[str] = None
@dataclass
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
index d9382c85..56b2ead3 100644
--- a/docker-compose-dev.yml
+++ b/docker-compose-dev.yml
@@ -25,7 +25,7 @@ services:
image: postgres
container_name: fab-db-dc
ports:
- - 5432:5432
+ - 5435:5432
volumes:
- ~/apps/postgres:/var/lib/postgresql/data
environment:
diff --git a/tests/test_config_export.py b/tests/test_config_export.py
index dca3a4b1..ee3f2e42 100644
--- a/tests/test_config_export.py
+++ b/tests/test_config_export.py
@@ -184,7 +184,7 @@ def test_generate_form_jsons_for_round_valid_input(seed_dynamic_data):
"name": "start-page-content",
"options": {},
"type": "Html",
- "content": "None
"
+ "content": '
'
"We will ask you about:
",
"schema": {},
}
diff --git a/tests/test_db.py b/tests/test_db.py
index 4979482b..ea6671e9 100644
--- a/tests/test_db.py
+++ b/tests/test_db.py
@@ -7,13 +7,11 @@
from app.db.models import Form
from app.db.models import Fund
from app.db.models import Organisation
-from app.db.models import Page
from app.db.models import Round
from app.db.models import Section
from app.db.queries.application import delete_form_from_section
from app.db.queries.application import delete_section_from_round
from app.db.queries.application import get_section_by_id
-from app.db.queries.application import get_template_page_by_display_path
from app.db.queries.application import move_form_down
from app.db.queries.application import move_form_up
from app.db.queries.application import move_section_down
@@ -216,35 +214,6 @@ def test_get_round_by_id(seed_dynamic_data):
assert result.title_json["en"] == "round the first"
-@pytest.mark.seed_config(
- {
- "pages": [
- Page(
- page_id=uuid4(),
- form_id=None,
- display_path="testing_templates_path",
- is_template=True,
- name_in_apply_json={"en": "Template Path"},
- form_index=0,
- ),
- Page(
- page_id=uuid4(),
- form_id=None,
- display_path="testing_templates_path",
- is_template=False,
- name_in_apply_json={"en": "Not Template Path"},
- form_index=0,
- ),
- ]
- }
-)
-def test_get_template_page_by_display_path(seed_dynamic_data):
-
- result = get_template_page_by_display_path("testing_templates_path")
- assert result
- assert result.page_id == seed_dynamic_data["pages"][0].page_id
-
-
section_id = uuid4()
diff --git a/tests/test_form_import.py b/tests/test_form_import.py
new file mode 100644
index 00000000..78f7ca98
--- /dev/null
+++ b/tests/test_form_import.py
@@ -0,0 +1,130 @@
+from dataclasses import asdict
+from unittest import mock
+from uuid import uuid4
+
+import pytest
+
+from app.db.models.application_config import Component
+from app.db.models.application_config import Lizt
+from app.import_config.load_form_json import _build_condition
+from app.import_config.load_form_json import _find_list_and_create_if_not_existing
+from app.import_config.load_form_json import add_conditions_to_components
+from app.shared.data_classes import Condition
+from app.shared.data_classes import ConditionValue
+from app.shared.data_classes import SubCondition
+from tests.unit_test_data import test_condition_org_type_a
+from tests.unit_test_data import test_condition_org_type_c
+from tests.unit_test_data import test_form_json_condition_org_type_a
+from tests.unit_test_data import test_form_json_condition_org_type_c
+
+
+@pytest.mark.parametrize(
+ "input_condition,exp_result",
+ [
+ (
+ test_form_json_condition_org_type_a,
+ test_condition_org_type_a,
+ ),
+ (
+ test_form_json_condition_org_type_c,
+ test_condition_org_type_c,
+ ),
+ ],
+)
+def test_build_conditions(input_condition, exp_result):
+ result = _build_condition(condition_data=input_condition, destination_page_path=exp_result.destination_page_path)
+ assert result == exp_result
+
+
+@pytest.mark.parametrize(
+ "input_page, input_conditions, exp_condition_count",
+ [
+ ({"next": [{"path": "default-next"}]}, [], 0),
+ (
+ {"next": [{"path": "next-a", "condition": "condition-a"}]},
+ [
+ asdict(
+ Condition(
+ name="condition-a",
+ display_name="condition a",
+ destination_page_path="page-b",
+ value=ConditionValue(
+ name="condition a",
+ conditions=[SubCondition(field={"name": "c1"}, operator="is", value={}, coordinator=None)],
+ ),
+ )
+ )
+ ],
+ 1,
+ ),
+ (
+ {"next": [{"path": "next-a", "condition": "condition-a"}]},
+ [
+ asdict(
+ Condition(
+ name="condition-a",
+ display_name="condition a",
+ destination_page_path="page-b",
+ value=ConditionValue(
+ name="condition a",
+ conditions=[
+ SubCondition(field={"name": "c1"}, operator="is", value={}, coordinator=None),
+ SubCondition(field={"name": "c1"}, operator="is", value={}, coordinator="or"),
+ ],
+ ),
+ )
+ )
+ ],
+ 1,
+ ),
+ ],
+)
+def test_add_conditions_to_components(mocker, input_page, input_conditions, exp_condition_count):
+ mock_component = Component()
+ mocker.patch("app.import_config.load_form_json._get_component_by_runner_name", return_value=mock_component)
+ with mock.patch(
+ "app.import_config.load_form_json._build_condition",
+ return_value=Condition(name=None, display_name=None, destination_page_path=None, value=None),
+ ) as mock_build_condition:
+ add_conditions_to_components(None, input_page, input_conditions)
+ if exp_condition_count > 0:
+ assert mock_component.conditions
+ assert len(mock_component.conditions) == exp_condition_count
+ assert mock_build_condition.call_count == len(input_conditions)
+
+
+@pytest.mark.parametrize(
+ "input_list_name,input_all_lists, existing_list",
+ [
+ ("existing-list", [{"name": "existing-list"}], Lizt(list_id=uuid4())),
+ ],
+)
+def test_find_list_and_create_existing(mocker, input_list_name, input_all_lists, existing_list):
+ with (
+ mock.patch(
+ "app.import_config.load_form_json.get_list_by_name", return_value=existing_list
+ ) as get_list_by_name_mock,
+ mock.patch(
+ "app.import_config.load_form_json.insert_list", return_value=Lizt(list_id="new-list-id")
+ ) as insert_list_mock,
+ ):
+ result = _find_list_and_create_if_not_existing(list_name=input_list_name, all_lists_in_form=input_all_lists)
+ assert result == existing_list.list_id
+ assert get_list_by_name_mock.called_once_with(list_name=input_list_name)
+ insert_list_mock.assert_not_called()
+
+
+@pytest.mark.parametrize("input_list_name,input_all_lists, existing_list", [("new-list", [{"name": "new-list"}], None)])
+def test_find_list_and_create_not_existing(mocker, input_list_name, input_all_lists, existing_list):
+ with (
+ mock.patch(
+ "app.import_config.load_form_json.get_list_by_name", return_value=existing_list
+ ) as get_list_by_name_mock,
+ mock.patch(
+ "app.import_config.load_form_json.insert_list", return_value=Lizt(list_id="new-list-id")
+ ) as insert_list_mock,
+ ):
+ result = _find_list_and_create_if_not_existing(list_name=input_list_name, all_lists_in_form=input_all_lists)
+ assert result == "new-list-id"
+ assert get_list_by_name_mock.called_once_with(list_name=input_list_name)
+ insert_list_mock.called_once()
diff --git a/tests/test_generate_form.py b/tests/test_generate_form.py
index 72bcb87e..5e00ed4f 100644
--- a/tests/test_generate_form.py
+++ b/tests/test_generate_form.py
@@ -1,3 +1,6 @@
+from copy import deepcopy
+from dataclasses import asdict
+from unittest import mock
from uuid import uuid4
import pytest
@@ -6,14 +9,30 @@
from app.db.models import ComponentType
from app.db.models import Lizt
from app.db.models import Page
+from app.db.models.application_config import Form
+from app.export_config.generate_form import build_component
from app.export_config.generate_form import build_conditions
from app.export_config.generate_form import build_form_json
from app.export_config.generate_form import build_lists
from app.export_config.generate_form import build_navigation
from app.export_config.generate_form import build_page
+from app.export_config.generate_form import build_start_page
from app.export_config.generate_form import human_to_kebab_case
from tests.unit_test_data import mock_c_1
+from tests.unit_test_data import mock_c_2
from tests.unit_test_data import mock_form_1
+from tests.unit_test_data import test_condition_org_type_a
+from tests.unit_test_data import test_condition_org_type_b
+from tests.unit_test_data import test_condition_org_type_c
+from tests.unit_test_data import test_form_json_condition_org_type_a
+from tests.unit_test_data import test_form_json_condition_org_type_b
+from tests.unit_test_data import test_form_json_condition_org_type_c
+from tests.unit_test_data import test_form_json_page_org_type_a
+from tests.unit_test_data import test_form_json_page_org_type_b
+from tests.unit_test_data import test_form_json_page_org_type_c
+from tests.unit_test_data import test_page_object_org_type_a
+from tests.unit_test_data import test_page_object_org_type_b
+from tests.unit_test_data import test_page_object_org_type_c
@pytest.mark.parametrize(
@@ -80,122 +99,6 @@ def test_build_lists(mocker, pages, exp_result):
assert results[0]["name"] == "greetings_list"
-mock_lookups = {
- "organisation-name": "Organisation Name",
- "organisation-single-name": "Organisation Name",
-}
-mock_components = [
- {
- "id": "reuse-charitable-objects",
- "json_snippet": {
- "options": {"hideTitle": True, "maxWords": "500"},
- "type": "FreeTextField",
- "title": "What are your organisation’s charitable objects?",
- "hint": "You can find this in your organisation's governing document.",
- },
- },
- {
- "id": "reuse-organisation-name",
- "json_snippet": {
- "options": {"hideTitle": False, "classes": "govuk-!-width-full"},
- "type": "TextField",
- "title": "Organisation name",
- "hint": "This must match your registered legal organisation name",
- "schema": {},
- },
- },
- {
- "id": "reuse_organisation_other_names_yes_no",
- "json_snippet": {
- "options": {},
- "type": "YesNoField",
- "title": "Does your organisation use any other names?",
- "schema": {},
- },
- "conditions": [
- {
- "name": "organisation_other_names_no",
- "value": "false",
- "operator": "is",
- "destination_page": "CONTINUE",
- },
- {
- "name": "organisation_other_names_yes",
- "value": "true",
- "operator": "is",
- "destination_page": "alternative-organisation-name",
- },
- ],
- },
- {
- "id": "reuse-alt-org-name-1",
- "json_snippet": {
- "options": {"classes": "govuk-input"},
- "type": "TextField",
- "title": "Alternative name 1",
- "schema": {},
- },
- },
- {
- "id": "reuse-alt-org-name-2",
- "json_snippet": {
- "options": {"required": False, "classes": "govuk-input"},
- "type": "TextField",
- "title": "Alternative name 2",
- "schema": {},
- },
- },
- {
- "id": "reuse-alt-org-name-3",
- "json_snippet": {
- "options": {"required": False, "classes": "govuk-input"},
- "type": "TextField",
- "title": "Alternative name 3",
- "schema": {},
- },
- },
-]
-mock_pages = [
- {
- "id": "organisation-single-name",
- "builder_display_name": "Single Organisation Name",
- "form_display_name": "Organisation Name",
- "component_names": [
- "reuse-organisation-name",
- ],
- "show_in_builder": True,
- },
- {
- "id": "organisation-name",
- "builder_display_name": "Organisation Name, with Alternatives",
- "form_display_name": "Organisation Name",
- "component_names": [
- "reuse-organisation-name",
- "reuse_organisation_other_names_yes_no",
- ],
- "show_in_builder": True,
- },
- {
- "id": "organisation-charitable-objects",
- "builder_display_name": "Organisation Charitable objects",
- "form_display_name": "Organisation charitable objects",
- "component_names": ["reuse-charitable-objects"],
- "show_in_builder": True,
- },
- {
- "id": "alternative-organisation-name",
- "builder_display_name": "Alternative Organisation Names",
- "form_display_name": "Alternative names of your organisation",
- "component_names": [
- "reuse-alt-org-name-1",
- "reuse-alt-org-name-2",
- "reuse-alt-org-name-3",
- ],
- "show_in_builder": False,
- },
-]
-
-
@pytest.mark.parametrize(
"input_page, exp_result",
[
@@ -227,11 +130,68 @@ def test_build_lists(mocker, pages, exp_result):
)
],
)
-def test_build_page(mocker, input_page, exp_result):
+def test_build_page_and_components(input_page, exp_result):
result = build_page(input_page)
assert result == exp_result
+def test_build_page_controller_specified():
+ input_page: Page = Page(name_in_apply_json={"en": "Name in json"}, controller="startPageController")
+ result_page = build_page(page=input_page)
+ assert result_page
+ assert result_page["controller"] == "startPageController"
+
+
+def test_build_page_controller_not_specified():
+ input_page: Page = Page(name_in_apply_json={"en": "Name in json"}, controller=None)
+ result_page = build_page(page=input_page)
+ assert result_page
+ assert ("controller" in result_page) is False
+
+
+@pytest.mark.parametrize(
+ "input_page",
+ [
+ (
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-single-name",
+ name_in_apply_json={"en": "Organisation Name"},
+ form_index=1,
+ components=[mock_c_1],
+ )
+ ),
+ (
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-single-name",
+ name_in_apply_json={"en": "Organisation Name"},
+ form_index=1,
+ components=[mock_c_1, mock_c_2],
+ )
+ ),
+ (
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-single-name",
+ name_in_apply_json={"en": "Organisation Name"},
+ form_index=1,
+ components=[],
+ )
+ ),
+ ],
+)
+def test_build_page(input_page):
+ with mock.patch("app.export_config.generate_form.build_component", new_value=lambda c: c) as mock_build_component:
+ result_page = build_page(input_page)
+ assert result_page
+ assert mock_build_component.call_count == len(input_page.components)
+ assert len(result_page["components"]) == len(input_page.components)
+
+
id = uuid4()
id2 = uuid4()
@@ -239,104 +199,129 @@ def test_build_page(mocker, input_page, exp_result):
@pytest.mark.parametrize(
"input_component, exp_results",
[
+ # single condition
(
Component(
component_id=id,
- title="test_title",
+ title="org type",
type=ComponentType.TEXT_FIELD,
conditions=[
- {
- "name": "test_condition",
- "display_name": "display name",
- "operator": "is",
- "value": {"type": "Value", "value": "yes", "display": "yes"},
- "destination_page_path": "./who-knows",
- "coordinator": None,
- },
+ asdict(test_condition_org_type_a),
+ ],
+ runner_component_name="org_type",
+ ),
+ [test_form_json_condition_org_type_a],
+ ),
+ # 2 conditions
+ (
+ Component(
+ component_id=id2,
+ title="test_title_2",
+ type=ComponentType.TEXT_FIELD,
+ conditions=[
+ asdict(test_condition_org_type_a),
+ asdict(test_condition_org_type_b),
],
runner_component_name="test_name",
),
[
- {
- "displayName": "display name",
- "name": "test_condition",
- "value": {
- "name": "display name",
- "conditions": [
- {
- "field": {"name": "test_name", "type": "TextField", "display": "test_title"},
- "operator": "is",
- "value": {"type": "Value", "value": "yes", "display": "yes"},
- }
- ],
- },
- }
+ test_form_json_condition_org_type_a,
+ test_form_json_condition_org_type_b,
],
),
+ # single complex condition
(
Component(
component_id=id2,
title="test_title_2",
type=ComponentType.TEXT_FIELD,
- conditions=[
- {
- "name": "test_condition",
- "display_name": "display name",
- "operator": "is",
- "value": {"type": "Value", "value": "yes", "display": "yes"},
- "destination_page_path": "./who-knows",
- },
- {
- "name": "test_condition2",
- "display_name": "display name",
- "operator": "is",
- "value": {"type": "Value", "value": "no", "display": "no"},
- "destination_page_path": "./who-knows2",
- },
- ],
+ conditions=[asdict(test_condition_org_type_c)],
runner_component_name="test_name",
),
[
- {
- "displayName": "display name",
- "name": "test_condition",
- "value": {
- "name": "display name",
- "conditions": [
- {
- "field": {"name": "test_name", "type": "TextField", "display": "test_title_2"},
- "operator": "is",
- "value": {"type": "Value", "value": "yes", "display": "yes"},
- }
- ],
- },
- },
- {
- "displayName": "display name",
- "name": "test_condition2",
- "value": {
- "name": "display name",
- "conditions": [
- {
- "field": {"name": "test_name", "type": "TextField", "display": "test_title_2"},
- "operator": "is",
- "value": {"type": "Value", "value": "no", "display": "no"},
- }
- ],
- },
- },
+ test_form_json_condition_org_type_c,
],
),
],
)
-def test_build_conditions(input_component, exp_results):
+def test_build_conditions(
+ input_component, exp_results, ids=["single condition", "2 conditions", "single condition with coordinator"]
+):
results = build_conditions(input_component)
assert results == exp_results
+list_id = uuid4()
+
+
+@pytest.mark.parametrize(
+ "component_to_build, exp_result",
+ [
+ (
+ Component(
+ component_id=uuid4(),
+ type=ComponentType.TEXT_FIELD,
+ title="Test Title",
+ hint_text="This must be a hint",
+ page_id=None,
+ page_index=1,
+ theme_id=None,
+ runner_component_name="test-name",
+ options={
+ "hideTitle": False,
+ "classes": "govuk-!-width-full",
+ },
+ ),
+ {
+ "name": "test-name",
+ "options": {
+ "hideTitle": False,
+ "classes": "govuk-!-width-full",
+ },
+ "type": "TextField",
+ "title": "Test Title",
+ "hint": "This must be a hint",
+ "schema": {},
+ "metadata": {},
+ },
+ ),
+ (
+ Component(
+ component_id=uuid4(),
+ type=ComponentType.LIST_FIELD,
+ title="Test Title",
+ hint_text="This must be a hint",
+ page_id=None,
+ page_index=1,
+ theme_id=None,
+ runner_component_name="test-name",
+ options={},
+ lizt=Lizt(name="test-list", list_id=list_id),
+ list_id=list_id,
+ ),
+ {
+ "name": "test-name",
+ "options": {},
+ "type": "List",
+ "title": "Test Title",
+ "hint": "This must be a hint",
+ "schema": {},
+ "metadata": {"fund_builder_list_id": str(list_id)},
+ "list": "test-list",
+ "values": {"type": "listRef"},
+ },
+ ),
+ ],
+)
+def test_build_component(component_to_build, exp_result):
+ result = build_component(component=component_to_build)
+ assert result == exp_result
+
+
@pytest.mark.parametrize(
"input_pages,input_partial_json, exp_next",
[
+ # Simple flow of 1 page then summary (summary not in input pages)
(
[
Page(
@@ -375,6 +360,64 @@ def test_build_conditions(input_component, exp_results):
"/organisation-single-name": [{"path": "/summary"}],
},
),
+ # 1 page then summary (summary is in input pages)
+ (
+ [
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-single-name",
+ name_in_apply_json={"en": "Organisation Name"},
+ form_index=1,
+ default_next_page_id="summary-id",
+ ),
+ Page(
+ page_id="summary-id",
+ form_id=uuid4(),
+ display_path="summary-page",
+ name_in_apply_json={"en": "Summary Page"},
+ form_index=1,
+ controller="summary.js",
+ ),
+ ],
+ {
+ "conditions": [],
+ "pages": [
+ {
+ "path": "/organisation-single-name",
+ "title": "Organisation Name",
+ "components": [
+ {
+ "name": "reuse-organisation-name",
+ "options": {
+ "hideTitle": False,
+ "classes": "govuk-!-width-full",
+ },
+ "type": "TextField",
+ "title": "Organisation name",
+ "hint": "This must match your registered legal organisation name",
+ "schema": {},
+ }
+ ],
+ "next": [],
+ "options": {},
+ },
+ {
+ "path": "/summary-page",
+ "title": "Summary Page",
+ "components": [],
+ "next": [],
+ "options": {},
+ "controller": "summary.js",
+ },
+ ],
+ },
+ {
+ "/organisation-single-name": [{"path": "/summary-page"}],
+ "/summary-page": [],
+ },
+ ),
+ # Simple flow of 2 pages then summary
(
[
Page(
@@ -437,9 +480,37 @@ def test_build_conditions(input_component, exp_results):
"/organisation-charitable-objects": [{"path": "/summary"}],
},
),
+ # Just a summary page
+ (
+ [
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="summary",
+ name_in_apply_json={"en": "Summary"},
+ form_index=1,
+ controller="summary.js",
+ )
+ ],
+ {
+ "conditions": [],
+ "pages": [
+ {
+ "path": "/summary",
+ "title": "Summary",
+ "components": [],
+ "next": [],
+ "options": {},
+ },
+ ],
+ },
+ {
+ "/summary": [],
+ },
+ ),
],
)
-def test_build_navigation_no_conditions(mocker, input_partial_json, input_pages, exp_next):
+def test_build_navigation_no_conditions(input_partial_json, input_pages, exp_next):
results = build_navigation(partial_form_json=input_partial_json, input_pages=input_pages)
for page in results["pages"]:
@@ -449,161 +520,268 @@ def test_build_navigation_no_conditions(mocker, input_partial_json, input_pages,
@pytest.mark.parametrize(
- "input_pages,input_partial_json ,exp_next, exp_conditions",
+ "input_pages,input_partial_json ,exp_next",
[
+ # One page, 2 possible nexts, both based on defined conditions
(
[
Page(
page_id=uuid4(),
form_id=uuid4(),
- display_path="organisation-name",
- name_in_apply_json={"en": "Organisation Name"},
+ display_path="organisation-type",
+ name_in_apply_json={"en": "Organisation Type"},
form_index=1,
components=[
Component(
component_id=id2,
- title="test_title_2",
- type=ComponentType.TEXT_FIELD,
+ title="org_type",
+ type=ComponentType.RADIOS_FIELD,
conditions=[
- {
- "name": "orgno",
- "display_name": "organisation_other_names_no",
- "value": {"type": "Value", "value": "no", "display": "no"},
- "destination_page_path": "summary",
- "operator": "is",
- "coordinator": None,
- },
- {
- "name": "orgyes",
- "display_name": "organisation_other_names_yes",
- "operator": "is",
- "value": {"type": "Value", "value": "yes", "display": "yes"},
- "destination_page_path": "organisation-alternative-names",
- "coordinator": None,
- },
+ asdict(test_condition_org_type_c),
+ asdict(test_condition_org_type_b),
],
runner_component_name="test_c_1",
)
],
),
+ test_page_object_org_type_b,
+ test_page_object_org_type_c,
+ ],
+ {
+ "conditions": [],
+ "pages": [
+ {
+ "path": "/organisation-type",
+ "title": "Organisation Type",
+ "components": [],
+ "next": [],
+ "options": {},
+ },
+ deepcopy(test_form_json_page_org_type_b),
+ deepcopy(test_form_json_page_org_type_c),
+ ],
+ },
+ {
+ "/organisation-type": [
+ {
+ "path": "/org-type-c",
+ "condition": "org_type_c",
+ },
+ {
+ "path": "/org-type-b",
+ "condition": "org_type_b",
+ },
+ ],
+ "/org-type-b": [{"path": "/summary"}],
+ "/org-type-c": [{"path": "/summary"}],
+ },
+ ),
+ # One page, 2 possible nexts, based on a condition and a default (summary)
+ (
+ [
Page(
page_id=uuid4(),
form_id=uuid4(),
- display_path="organisation-alternative-names",
- name_in_apply_json={"en": "Organisation Alternative Names"},
+ display_path="organisation-type",
+ name_in_apply_json={"en": "Organisation Type"},
+ form_index=1,
+ default_next_page_id="summary-id",
+ components=[
+ Component(
+ component_id=id2,
+ title="org_type",
+ type=ComponentType.RADIOS_FIELD,
+ conditions=[
+ asdict(test_condition_org_type_b),
+ ],
+ runner_component_name="test_c_1",
+ )
+ ],
+ ),
+ test_page_object_org_type_b,
+ Page(
+ page_id="summary-id",
+ form_id=uuid4(),
+ display_path="summary",
+ name_in_apply_json={"en": "Summary"},
form_index=2,
+ controller="summary.js",
),
],
{
"conditions": [],
"pages": [
{
- "path": "/organisation-name",
- "title": "Organisation Name",
- "components": [
- {
- # "name": "reuse-organisation-name",
- # "options": {
- # "hideTitle": False,
- # "classes": "govuk-!-width-full",
- # },
- # "type": "TextField",
- # "title": "Organisation name",
- # "hint": "This must match your registered legal organisation name",
- # "schema": {},
- },
- {
- # "name": "reuse_organisation_other_names_yes_no",
- # "options": {},
- # "type": "YesNoField",
- # "title": "Does your organisation use any other names?",
- # "schema": {},
- },
- ],
+ "path": "/organisation-type",
+ "title": "Organisation Type",
+ "components": [],
"next": [],
"options": {},
},
+ deepcopy(test_form_json_page_org_type_b),
{
- "path": "/organisation-alternative-names",
- "title": "Organisation Alternative Names",
+ "path": "/summary",
+ "title": "Summary",
"components": [],
"next": [],
"options": {},
+ "controller": "summary.js",
},
],
},
{
- "/organisation-name": [
+ "/organisation-type": [
{
"path": "/summary",
- "condition": "orgno",
},
{
- "path": "/organisation-alternative-names",
- "condition": "orgyes",
+ "path": "/org-type-b",
+ "condition": "org_type_b",
},
],
- "/organisation-alternative-names": [{"path": "/summary"}],
+ "/org-type-b": [{"path": "/summary"}],
+ "/summary": [],
},
+ ), # One page, 2 possible nexts, based on a condition and a default
+ (
[
- {
- "displayName": "organisation_other_names_no",
- "name": "orgno",
- "value": {
- "name": "organisation_other_names_no",
- "conditions": [
- {
- "field": {
- "name": "test_c_1",
- "type": "TextField",
- "display": "test_title_2",
- },
- "operator": "is",
- "value": {
- "type": "Value",
- "value": "no",
- "display": "no",
- },
- }
- ],
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-type",
+ name_in_apply_json={"en": "Organisation Type"},
+ form_index=1,
+ default_next_page_id="page-2",
+ components=[
+ Component(
+ component_id=id2,
+ title="org_type",
+ type=ComponentType.RADIOS_FIELD,
+ conditions=[
+ asdict(test_condition_org_type_b),
+ ],
+ runner_component_name="test_c_1",
+ )
+ ],
+ ),
+ test_page_object_org_type_b,
+ Page(
+ page_id="page-2",
+ form_id=uuid4(),
+ display_path="page_2",
+ name_in_apply_json={"en": "Page 2"},
+ form_index=2,
+ ),
+ ],
+ {
+ "conditions": [],
+ "pages": [
+ {
+ "path": "/organisation-type",
+ "title": "Organisation Type",
+ "components": [],
+ "next": [],
+ "options": {},
},
- },
- {
- "displayName": "organisation_other_names_yes",
- "name": "orgyes",
- "value": {
- "name": "organisation_other_names_yes",
- "conditions": [
- {
- "field": {
- "name": "test_c_1",
- "type": "TextField",
- "display": "test_title_2",
- },
- "operator": "is",
- "value": {
- "type": "Value",
- "value": "yes",
- "display": "yes",
- },
- }
- ],
+ deepcopy(test_form_json_page_org_type_b),
+ {
+ "path": "/page_2",
+ "title": "Page 2",
+ "components": [],
+ "next": [],
+ "options": {},
},
- },
+ ],
+ },
+ {
+ "/organisation-type": [
+ {
+ "path": "/page_2",
+ },
+ {
+ "path": "/org-type-b",
+ "condition": "org_type_b",
+ },
+ ],
+ "/org-type-b": [{"path": "/summary"}],
+ "/page_2": [{"path": "/summary"}],
+ "/summary": [],
+ },
+ ),
+ # # One page, 3 possible nexts based on complex conditions (coordinators)
+ (
+ [
+ Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="organisation-type",
+ name_in_apply_json={"en": "Organisation Type"},
+ form_index=1,
+ components=[
+ Component(
+ component_id=id2,
+ title="org_type",
+ type=ComponentType.RADIOS_FIELD,
+ conditions=[
+ asdict(test_condition_org_type_a),
+ asdict(test_condition_org_type_b),
+ asdict(test_condition_org_type_c),
+ ],
+ runner_component_name="org_type_component",
+ )
+ ],
+ ),
+ test_page_object_org_type_a,
+ test_page_object_org_type_b,
+ test_page_object_org_type_c,
],
- )
+ {
+ "conditions": [],
+ "pages": [
+ {
+ "path": "/organisation-type",
+ "title": "Organisation Type",
+ "components": [
+ {}, # don't care about these right now...
+ {},
+ ],
+ "next": [],
+ "options": {},
+ },
+ deepcopy(test_form_json_page_org_type_a),
+ deepcopy(test_form_json_page_org_type_b),
+ deepcopy(test_form_json_page_org_type_c),
+ ],
+ },
+ {
+ "/organisation-type": [
+ {
+ "path": "/org-type-a",
+ "condition": "org_type_a",
+ },
+ {
+ "path": "/org-type-b",
+ "condition": "org_type_b",
+ },
+ {
+ "path": "/org-type-c",
+ "condition": "org_type_c",
+ },
+ ],
+ "/org-type-a": [{"path": "/summary"}],
+ "/org-type-b": [{"path": "/summary"}],
+ "/org-type-c": [{"path": "/summary"}],
+ },
+ ),
],
)
-def test_build_navigation_with_conditions(mocker, input_pages, input_partial_json, exp_next, exp_conditions):
- mocker.patch(
- "app.export_config.generate_form.build_page",
- return_value={"path": "/organisation-alternative-names", "next": []},
- )
+def test_build_navigation_with_conditions(mocker, input_pages, input_partial_json, exp_next):
+ mocker.patch("app.export_config.generate_form.build_conditions", return_value=["mock list"])
results = build_navigation(partial_form_json=input_partial_json, input_pages=input_pages)
for page in results["pages"]:
exp_next_this_page = exp_next[page["path"]]
- assert page["next"] == exp_next_this_page
- assert results["conditions"] == exp_conditions
+ assert page["next"] == exp_next_this_page, f"next for page {page['path']} does not match expected"
+ assert results["conditions"] == ["mock list"]
@pytest.mark.parametrize(
@@ -656,3 +834,60 @@ def test_build_form(input_form, exp_results):
assert exp_next["path"] in [next["path"] for next in result_page["next"]]
if "condition" in exp_next:
assert exp_next["condition"] in [next["condition"] for next in result_page["next"]]
+
+
+@pytest.mark.parametrize(
+ "input_content, input_form, expected_title, expected_path, expected_next, expected_content",
+ [
+ (
+ "2 pages",
+ Form(
+ name_in_apply_json={"en": "Test Form"},
+ pages=[
+ Page(name_in_apply_json={"en": "Page 1"}, display_path="page-1"),
+ Page(name_in_apply_json={"en": "Page 2"}, display_path="page-2"),
+ ],
+ ),
+ "Test Form",
+ "/intro-test-form",
+ [{"path": "/page-1"}],
+ (
+ '2 pages
'
+ 'We will ask you about:
"
+ ),
+ ),
+ (
+ "Single page",
+ Form(
+ name_in_apply_json={"en": "Another Form"},
+ pages=[Page(name_in_apply_json={"en": "Details Page"}, display_path="details-page")],
+ ),
+ "Another Form",
+ "/intro-another-form",
+ [{"path": "/details-page"}],
+ (
+ 'Single page
'
+ 'We will ask you about:
"
+ ),
+ ),
+ (
+ "Form with no pages",
+ Form(name_in_apply_json={"en": "Another Form"}, pages=[]),
+ "Another Form",
+ "/intro-another-form",
+ [],
+ ('Form with no pages
'),
+ ),
+ ],
+)
+def test_build_start_page(input_content, input_form, expected_title, expected_path, expected_next, expected_content):
+ result = build_start_page(input_content, input_form)
+
+ # Assert
+ assert result["title"] == expected_title
+ assert result["path"] == expected_path
+ assert result["controller"] == "./pages/start.js"
+ assert result["next"] == expected_next
+ assert result["components"][0]["content"] == expected_content
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 686a7a98..497b1578 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -1,6 +1,7 @@
import json
import os
import shutil
+from dataclasses import asdict
from pathlib import Path
from uuid import uuid4
@@ -22,6 +23,8 @@
generate_form_jsons_for_round,
)
from app.import_config.load_form_json import load_form_jsons
+from app.shared.data_classes import Condition
+from app.shared.data_classes import ConditionValue
from tasks.test_data import BASIC_FUND_INFO
from tasks.test_data import BASIC_ROUND_INFO
@@ -134,20 +137,50 @@ def test_build_form_json_no_conditions(seed_dynamic_data):
runner_component_name="does_your_organisation_use_other_names",
is_template=True,
conditions=[
- {
- "name": "organisation_other_names_no",
- "value": "false", # this must be lowercaes or the navigation doesn't work
- "operator": "is",
- "destination_page_path": "CONTINUE",
- "display_name": "Other Name No",
- },
- {
- "name": "organisation_other_names_yes",
- "value": "true", # this must be lowercaes or the navigation doesn't work
- "operator": "is",
- "destination_page_path": "organisation-alternative-names",
- "display_name": "Other Name Yes",
- },
+ asdict(
+ Condition(
+ name="organisation_other_names_no",
+ display_name="org other names no",
+ destination_page_path="/summary",
+ value=ConditionValue(
+ name="org other names no",
+ conditions=[
+ {
+ "field": {
+ "name": "org_other_names",
+ "type": "YesNoField",
+ "display": "org other names",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "false", "display": "false"},
+ "coordinator": None,
+ },
+ ],
+ ),
+ ),
+ ),
+ asdict(
+ Condition(
+ name="organisation_other_names_yes",
+ display_name="org other names yes",
+ destination_page_path="/organisation-alternative-names",
+ value=ConditionValue(
+ name="org other names yes",
+ conditions=[
+ {
+ "field": {
+ "name": "org_other_names",
+ "type": "YesNoField",
+ "display": "org other names",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "true", "display": "false"},
+ "coordinator": None,
+ },
+ ],
+ ),
+ ),
+ ),
],
),
Component(
diff --git a/tests/unit_test_data.py b/tests/unit_test_data.py
index 57219af1..80024886 100644
--- a/tests/unit_test_data.py
+++ b/tests/unit_test_data.py
@@ -9,6 +9,8 @@
from app.db.models import Section
from app.db.models import Subcriteria
from app.db.models import Theme
+from app.shared.data_classes import Condition
+from app.shared.data_classes import ConditionValue
form_1_id = uuid4()
page_1_id = uuid4()
@@ -93,3 +95,188 @@
components=[component_with_list],
form_id=None,
)
+
+
+test_condition_org_type_a = Condition(
+ name="org_type_a",
+ display_name="org type a",
+ destination_page_path="/org-type-a",
+ value=ConditionValue(
+ name="org type a",
+ conditions=[
+ {
+ "field": {
+ "name": "org_type",
+ "type": "RadiosField",
+ "display": "org type",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "A", "display": "A"},
+ }
+ ],
+ ),
+)
+test_condition_org_type_b = Condition(
+ name="org_type_b",
+ display_name="org type b",
+ destination_page_path="/org-type-b",
+ value=ConditionValue(
+ name="org type b",
+ conditions=[
+ {
+ "field": {
+ "name": "org_type",
+ "type": "RadiosField",
+ "display": "org type",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "B", "display": "B"},
+ }
+ ],
+ ),
+)
+
+
+test_condition_org_type_c = Condition(
+ name="org_type_c",
+ display_name="org type c",
+ destination_page_path="/org-type-c",
+ value=ConditionValue(
+ name="org type c",
+ conditions=[
+ {
+ "field": {
+ "name": "org_type",
+ "type": "RadiosField",
+ "display": "org type",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "C1", "display": "C1"},
+ },
+ {
+ "field": {
+ "name": "org_type",
+ "type": "RadiosField",
+ "display": "org type",
+ },
+ "operator": "is",
+ "value": {"type": "Value", "value": "C2", "display": "C2"},
+ "coordinator": "or",
+ },
+ ],
+ ),
+)
+
+
+test_page_object_org_type_a = Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="org-type-a",
+ name_in_apply_json={"en": "Organisation Type A"},
+ form_index=2,
+)
+
+test_page_object_org_type_b = Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="org-type-b",
+ name_in_apply_json={"en": "Organisation Type B"},
+ form_index=2,
+)
+test_page_object_org_type_c = Page(
+ page_id=uuid4(),
+ form_id=uuid4(),
+ display_path="org-type-c",
+ name_in_apply_json={"en": "Organisation Type C"},
+ form_index=2,
+)
+
+test_form_json_page_org_type_a = {
+ "path": "/org-type-a",
+ "title": "org-type-a",
+ "components": [],
+ "next": [],
+ "options": {},
+}
+test_form_json_page_org_type_b = {
+ "path": "/org-type-b",
+ "title": "org-type-b",
+ "components": [],
+ "next": [],
+ "options": {},
+}
+test_form_json_page_org_type_c = {
+ "path": "/org-type-c",
+ "title": "org-type-c",
+ "components": [],
+ "next": [],
+ "options": {},
+}
+test_form_json_condition_org_type_c = {
+ "displayName": "org type c",
+ "name": "org_type_c",
+ "value": {
+ "name": "org type c",
+ "conditions": [
+ {
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "C1", "display": "C1"},
+ },
+ {
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "C2", "display": "C2"},
+ "coordinator": "or",
+ },
+ ],
+ },
+}
+test_form_json_condition_org_type_a = {
+ "displayName": "org type a",
+ "name": "org_type_a",
+ "value": {
+ "name": "org type a",
+ "conditions": [
+ {
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "A", "display": "A"},
+ }
+ ],
+ },
+}
+test_form_json_condition_org_type_b = {
+ "displayName": "org type b",
+ "name": "org_type_b",
+ "value": {
+ "name": "org type b",
+ "conditions": [
+ {
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "B", "display": "B"},
+ }
+ ],
+ },
+}
+test_form_json_condition_org_type_c = {
+ "displayName": "org type c",
+ "name": "org_type_c",
+ "value": {
+ "name": "org type c",
+ "conditions": [
+ {
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "C1", "display": "C1"},
+ },
+ {
+ "coordinator": "or",
+ "field": {"name": "org_type", "type": "RadiosField", "display": "org type"},
+ "operator": "is",
+ "value": {"type": "Value", "value": "C2", "display": "C2"},
+ },
+ ],
+ },
+}