diff --git a/fsd_utils/mapping/application/application_utils.py b/fsd_utils/mapping/application/application_utils.py index 7a3d8dd..44000aa 100644 --- a/fsd_utils/mapping/application/application_utils.py +++ b/fsd_utils/mapping/application/application_utils.py @@ -1,3 +1,4 @@ +import calendar import re from io import StringIO @@ -40,12 +41,24 @@ def format_answer(answer): if "null" in answer: return re.sub(r"\s*null\s*,?", "", answer) + if isinstance(answer, str): + if answer.startswith("http") or answer.startswith("https"): + return answer + elif "-" in answer: + return answer.replace("-", " ") + if isinstance(answer, list): - return [a.replace("'", "") for a in answer if isinstance(a, str)] + return [ + a.replace("'", "").replace("-", " ") + if isinstance(a, str) and "-" in a + else a + for a in answer + ] + else: - return answer + return answer except Exception as e: - current_app.logger.error(f"Could not format the answer, {e}") + current_app.logger.error(f"Could not format the answer: {e}") def simplify_title(section_name, remove_text: list): @@ -70,7 +83,7 @@ def format_checkbox(answer): formatted_elements = [] indent = " " * 5 for index, element in enumerate(answer, start=1): - separator = f"{indent}-" if index > 1 else "-" + separator = f"{indent}." if index > 1 else "." if "-" in element: sub_elements = element.split("-") formatted_sub_elements = " ".join(sub_elements).strip() @@ -91,5 +104,18 @@ def generate_text_of_application(q_and_a: dict, fund_name: str): output.write(f"\n* {' '.join(title).capitalize()}\n\n") for questions, answers in values.items(): output.write(f" Q) {questions}\n") - output.write(f" A) {answers}\n\n") + output.write(f" A) {format_answer(answers)}\n\n") return output.getvalue() + + +def number_to_month(number, iso_key): + """Converts a month number to its corresponding month name.""" + try: + if iso_key == "month": + month_name = calendar.month_name[number] + return month_name + else: + return number + except IndexError: + current_app.logger.warn("Invalid month number") + return number diff --git a/fsd_utils/mapping/application/multi_input.py b/fsd_utils/mapping/application/multi_input.py index 74b7044..5f11e85 100644 --- a/fsd_utils/mapping/application/multi_input.py +++ b/fsd_utils/mapping/application/multi_input.py @@ -3,6 +3,7 @@ from flask import current_app from fsd_utils.mapping.application.application_utils import convert_bool_value +from fsd_utils.mapping.application.application_utils import number_to_month class MultiInput: @@ -18,7 +19,7 @@ def format_values(cls, value, index): Returns: str: The formatted string representation of the value. """ - return f"{cls.indent}- {value}" if index != 1 else f"- {value}" + return f"{cls.indent}. {value}" if index != 1 else f". {value}" @classmethod def format_keys_and_values(cls, key, value, index): @@ -48,37 +49,43 @@ def formatted_values(value): ) return ( - f"{cls.indent}- {key}: {formatted_values(value)}" # noqa + f"{cls.indent}. {key}: {formatted_values(value)}" # noqa if index != 1 - else (f"- {key}: {formatted_values(value)}") # noqa + else (f". {key}: {formatted_values(value)}") # noqa ) @classmethod def format_nested_data(cls, value): + """ + Formats nested data based on specific keys and data and returns the formatted result. + Args: + value: A nested data structure to be processed and formatted. - formatted_nested_values = [] - for inner_items in value: + Returns: + str: The formatted result obtained from the nested data. + """ - for k, v in inner_items.items(): - for iso_keys in ["date", "month", "year"]: - try: + formatted_nested_values = [] + try: + for inner_items in value: + for k, v in inner_items.items(): + for iso_keys in ["date", "month", "year"]: if iso_keys in k.split("__"): - formatted_nested_values.append(f"{iso_keys}: {v}") + v = number_to_month(v, iso_keys) + formatted_nested_values.append(f"{v}") break # handles all other nested multiple values - except: # noqa - formatted_nested_values.append( - ", ".join( - map( - lambda item: ", ".join( - [f"{k}: {v}" for k, v in item.items()] - ), - value, - ) - ) - ) - return formatted_nested_values + except: # noqa + formatted_nested_values.append( + ", ".join( + map( + lambda item: ", ".join([f"{k}: {v}" for k, v in item.items()]), + value, + ) + ) + ) + return " ".join(formatted_nested_values) @classmethod def process_data(cls, data): @@ -92,6 +99,8 @@ def process_data(cls, data): output = [] for index, (key, value) in enumerate(data.items(), start=1): + if isinstance(key, int): + key = str(key) # handles single value/answer containing uuid and excludes uuid key # & display the value only. @@ -108,9 +117,9 @@ def process_data(cls, data): ): formatted_nested_values = cls.format_nested_data(value) output.append( - f"{cls.indent}- {key}: {formatted_nested_values}" + f"{cls.indent}. {key}: {formatted_nested_values}" if index != 1 - else f"- {key}: {formatted_nested_values}" + else f". {key}: {formatted_nested_values}" ) # handles all other multiple values else: diff --git a/pyproject.toml b/pyproject.toml index 5853b66..c59011f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "funding-service-design-utils" -version = "2.0.8" +version = "2.0.9" authors = [ { name="DLUHC", email="FundingServiceDesignTeam@levellingup.gov.uk" }, ] diff --git a/tests/test_data_utils.py b/tests/test_data_utils.py index ffc3e6b..5f50760 100644 --- a/tests/test_data_utils.py +++ b/tests/test_data_utils.py @@ -32,9 +32,9 @@ def test_get_remote_data_json_404(self, flask_test_client): ], }, "expected_response": [ - "- trusts one: [125, '1 April 2023 to 31 March 2024'," + ". trusts one: [125, '1 April 2023 to 31 March 2024'," " 'Capital', 'Yes']", - " - trust two: [456, '1 April 2024 to 31 March 2025'," + " . trust two: [456, '1 April 2024 to 31 March 2025'," " 'Revenue', 'No']", ], }, @@ -43,7 +43,7 @@ def test_get_remote_data_json_404(self, flask_test_client): "bbd0ec2a-972f-4d06-bf93-bf24786c3859": "Sky builders", "ac8bbdfe-6a39-45b8-8c0a-6558148388d1": "trust builders", }, - "expected_response": ["- Sky builders", " - trust builders"], + "expected_response": [". Sky builders", " . trust builders"], }, "iso_values": { "input_data": { @@ -52,8 +52,8 @@ def test_get_remote_data_json_404(self, flask_test_client): }, "expected_response": ( [ - "- Project one: ['month: 1', 'year: 2021']", - " - Project two: ['month: 2', 'year: 2022']", + ". Project one: January 2021", + " . Project two: February 2022", ] ), }, @@ -77,8 +77,8 @@ def test_get_remote_data_json_404(self, flask_test_client): }, ], "expected_response": ( - "- trusts one: [125, '1 April 2023 to 31 March 2024'," - " 'Capital', 'Yes']\n - trust two: [456, '1 April 2024 to" + ". trusts one: [125, '1 April 2023 to 31 March 2024'," + " 'Capital', 'Yes']\n . trust two: [456, '1 April 2024 to" " 31 March 2025', 'Revenue', 'No']" ), }, @@ -87,14 +87,14 @@ def test_get_remote_data_json_404(self, flask_test_client): {"CZZYvE": "Sky builders"}, {"CZZYvE": "trust builders"}, ], - "expected_response": "- Sky builders\n - trust builders", + "expected_response": ". Sky builders\n . trust builders", }, "integer_values": { "input_data": [ {"GLQlOh": "cost one", "JtwkMy": 4444}, {"GLQl6y": "cost two", "JtwkMt": 4455}, ], - "expected_response": "- cost one: 4444\n - cost two: 4455", + "expected_response": ". cost one: 4444\n . cost two: 4455", }, "nested_dict_value_with_str_value": { "input_data": [ @@ -104,12 +104,15 @@ def test_get_remote_data_json_404(self, flask_test_client): }, { "fFIuPP": "Milestone two", - "PrulfI": {"PrulfI__month": 3, "PrulfI__year": 2023}, + "PrulfI": { + "PrulfI__date": 12, + "PrulfI__month": 3, + "PrulfI__year": 2023, + }, }, ], "expected_response": ( - "- Milestone one: ['month: 2', 'year: 2022']\n - Milestone" - " two: ['month: 3', 'year: 2023']" + ". Milestone one: February 2022\n . Milestone" " two: 12 March 2023" ), }, }, @@ -119,41 +122,98 @@ def test_get_remote_data_json_404(self, flask_test_client): test_data_sort_questions_answers = { "forms": [ { + "name": "funding-required-ns", "questions": [ { - "category": "ZbxIUV", - "question": "Lead contact details", "fields": [ { - "key": "fUMWcd", - "title": "Name of lead contact", + "answer": "both-revenue-and-capital", + "key": "NxVqXd", + "title": "What funding are you applying for?", + "type": "list", + } + ], + "question": "What funding are you applying for?", + }, + { + "fields": [ + { + "answer": "4020", + "key": "GRWtfV", + "title": "Both revenue and capital", "type": "text", - "answer": "test name", }, { - "key": "ayzqnK", - "title": ( - "Is the lead contact the same person as the" - " authorised signatory?" - ), - "type": "list", - "answer": True, + "answer": "4020", + "key": "zvPzXN", + "title": "Revenue for 1 April 2024 to 31 March 2025", + "type": "text", + }, + { + "answer": "1230", + "title": "Capital for 1 April 2023 to 31 March 2024", + "type": "text", + }, + { + "answer": "1230", + "key": "pppiYl", + "title": "Capital for 1 April 2024 to 31 March 2025", + "type": "text", }, ], - } + "question": "How much funding are you applying for?", + }, + { + "fields": [ + { + "answer": [ + { + "TrTaZQ": "Test Funding Required NS Form", + "dpDFgB": "Test Funding Required NS Form", + "iZdZrr": 40, + "leIxEX": "1 April 2023 to 31 March 2024", + } + ], + "key": "mCbbyN", + "title": "Revenue costs", + "type": "multiInput", + } + ], + "question": "Revenue funding", + }, + { + "fields": [ + { + "answer": [ + { + "JtBjFp": 50, + "cpFthG": "Test Funding Required NS Form", + "mmwzGc": "1 April 2024 to 31 March 2025", + "pMffVz": "Test Funding Required NS Form", + } + ], + "key": "XsAoTv", + "title": "Capital costs", + "type": "multiInput", + } + ], + "question": "Capital funding", + }, ], - "name": "applicant-information-ns", - }, + } ], "form_names": [ - "objectives-and-activities-ns", - "risk-and-deliverability-ns", - "applicant-information-ns", + "funding-required-ns", ], "questions_answers": { - "applicant-information-ns": { - "Name of lead contact": "test name", - "Is the lead contact the same person as the authorised signatory?": ("Yes"), + "funding-required-ns": { + "What funding are you applying for?": "both-revenue-and-capital", + "Both revenue and capital": "4020", + "Revenue for 1 April 2024 to 31 March 2025": "4020", + "Capital for 1 April 2023 to 31 March 2024": "1230", + "Capital for 1 April 2024 to 31 March 2025": "1230", + "Revenue costs": ". Test Funding Required NS Form: ['Test Funding Required NS Form', 40, '1 April 2023 to 31 March 2024']", # noqa + "Capital costs": ". 50: ['Test Funding Required NS Form', '1 April 2024 to 31 March 2025', 'Test Funding Required NS Form']", # noqa } }, "incorrect_form_data": [ diff --git a/tests/test_mapping_application.py b/tests/test_mapping_application.py index 11f14f7..13108fb 100644 --- a/tests/test_mapping_application.py +++ b/tests/test_mapping_application.py @@ -32,6 +32,17 @@ def test_convert_bool_values(input_data, expected_response): [ ("null", ""), (None, "Not provided"), + ( + "555 dummy road, null, beautiful town, optional county, UU8 8UU", + "555 dummy road, beautiful town, optional county, UU8 8UU", + ), + ("https://example.com/test-the-link", "https://example.com/test-the-link"), + (["one-two-three"], ["one two three"]), + (["four five six"], ["four five six"]), + ("seven-eight-nine", "seven eight nine"), + ("seven-eight-nine", "seven eight nine"), + ("ten eleven twelve", "ten eleven twelve"), + ({"title": ["one-two-three"]}, {"title": ["one-two-three"]}), ], ) def test_format_answer(input_data, expected_response): @@ -86,7 +97,6 @@ def test_sort_questions_and_answers(app_context): forms = test_data_sort_questions_answers["forms"] response = extract_questions_and_answers(forms) - assert response == test_data_sort_questions_answers["questions_answers"] @@ -107,11 +117,11 @@ def test_sort_questions_and_answers_fail(app_context): "health-interventions", "employment-support", ], - "- health interventions\n - employment support", + ". health interventions\n . employment support", ), ( ["Survivors of domestic abuse", "ethnic minorities"], - "- Survivors of domestic abuse\n - ethnic minorities", + ". Survivors of domestic abuse\n . ethnic minorities", ), ], )