From b988bde7fdcae4448a2526bf644d4e3906963a39 Mon Sep 17 00:00:00 2001 From: Sam <78538841+spwoodcock@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:52:56 +0100 Subject: [PATCH] fix: handle all XLSForm manuipulation in update_xlsform.py (#299) * refactor: rename functions for update_form.py clarity * refactor: rename update_form.py --> update_xlsform.py * docs: add docs page for update_xlsform function * fix: rename xlsform field 'existing' --> 'feature' * fix: refactor update_xlsform + add additional select_one_from_file fields * fix: append task ids to xlsform choices sheet * test: add tests for entity list addition + task id in choices sheet * fix: mandatory fields xlsform add form_id uuid and form_title * test: update_xlsform tests add form_category param * fix: add form_category calculation field to xlsform * test: update tests for update_xlsform code * fix: use existing form id if provided, else random uuid4 * refactor: pass task_count instead of task_id manually to xlsform * fix: xlsform group creation logic * refactor: remove verification question section from bundled xlsforms * fix: retain start_group and end_group even if name field is empty * fix: more flexible omitting of begin_group 'begin group' * refactor: make append_mandatory_fields async io bound * test: update_xlsform tests use async/await --- docs/api/update_xlsform.md | 6 + mkdocs.yml | 1 + osm_fieldwork/update_form.py | 123 ---------- osm_fieldwork/update_xlsform.py | 213 ++++++++++++++++++ osm_fieldwork/xlsforms/buildings.xls | Bin 109568 -> 107008 bytes .../xlsforms/fmtm/mandatory_fields.xls | Bin 17408 -> 31232 bytes osm_fieldwork/xlsforms/health.xls | Bin 55808 -> 53760 bytes tests/test_update_form.py | 102 --------- tests/test_update_xlsform.py | 148 ++++++++++++ 9 files changed, 368 insertions(+), 225 deletions(-) create mode 100644 docs/api/update_xlsform.md delete mode 100644 osm_fieldwork/update_form.py create mode 100644 osm_fieldwork/update_xlsform.py delete mode 100644 tests/test_update_form.py create mode 100644 tests/test_update_xlsform.py diff --git a/docs/api/update_xlsform.md b/docs/api/update_xlsform.md new file mode 100644 index 000000000..1c6be4811 --- /dev/null +++ b/docs/api/update_xlsform.md @@ -0,0 +1,6 @@ +# update_xlsform.py + +::: osm_fieldwork.update_xlsform.append_mandatory_fields +options: +show_source: false +heading_level: 3 diff --git a/mkdocs.yml b/mkdocs.yml index 87027284c..2337e5b0a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -91,6 +91,7 @@ nav: - ODKInstance: api/ODKInstance.md - ODKForm: api/ODKForm.md - filter_data: api/filter_data.md + - update_xlsform: api/update_xlsform.md - Deep Tech Dives: - External Data: about/externaldata.md - Data conflation: about/conflation.md diff --git a/osm_fieldwork/update_form.py b/osm_fieldwork/update_form.py deleted file mode 100644 index c6a226a07..000000000 --- a/osm_fieldwork/update_form.py +++ /dev/null @@ -1,123 +0,0 @@ -from datetime import datetime -from io import BytesIO - -import pandas as pd -from python_calamine.pandas import pandas_monkeypatch - -from osm_fieldwork.xlsforms import xlsforms_path - -# Monkeypatch pandas to add calamine driver -pandas_monkeypatch() - - -def merge_sheets(mandatory_df, custom_df, digitisation_df, is_survey_sheet=False): - # Remove rows with None in 'name' column - if "name" in mandatory_df.columns: - mandatory_df = mandatory_df.dropna(subset=["name"]) - if "name" in custom_df.columns: - custom_df = custom_df.dropna(subset=["name"]) - if "name" in digitisation_df.columns: - digitisation_df = digitisation_df.dropna(subset=["name"]) - - # Identify common fields between custom_df and mandatory_df or digitisation_df - common_fields = ( - set(custom_df["name"]) - .intersection(set(mandatory_df["name"])) - .union(set(custom_df["name"]).intersection(set(digitisation_df["name"]))) - ) - - # Keep common fields from custom_df in their original order - custom_common_df = custom_df[custom_df["name"].isin(common_fields)] - custom_non_common_df = custom_df[~custom_df["name"].isin(common_fields)] - - # Filter out the common fields from the mandatory_df and digitisation_df - mandatory_df_filtered = mandatory_df[~mandatory_df["name"].isin(common_fields)] - digitisation_df_filtered = digitisation_df[~digitisation_df["name"].isin(common_fields)] - - if not is_survey_sheet: - return pd.concat( - [ - custom_common_df, - mandatory_df_filtered, - custom_non_common_df, - digitisation_df_filtered, - ], - ignore_index=True, - ) - survey_group_row = pd.DataFrame( - { - "type": ["begin group"], - "name": ["survey_questions"], - "label": ["Survey Form"], - "relevant": [ - "(${new_feature} = 'yes') or (${building_exists} = 'yes')" - ], # Add the relevant condition to display this group only if "Yes" is selected - } - ) - survey_end_group_row = pd.DataFrame({"type": ["end group"], "name": ["end_survey_questions"], "label": ["End Survey Form"]}) - digitisation_group = pd.DataFrame( - { - "type": ["begin group"], - "name": ["verification"], - "label": ["Verification Form"], - "relevant": ["(${new_feature} = 'yes') or (${building_exists} = 'yes')"], - } - ) - digitisation_end_group = pd.DataFrame({"type": ["end group"], "name": ["end_verification"], "label": ["End Verification Form"]}) - - # Concatenate: mandatory fields at the top, custom common fields, remaining custom fields, and finally append form fields - return pd.concat( - [ - custom_common_df, - mandatory_df_filtered, - survey_group_row, - custom_non_common_df, - survey_end_group_row, - digitisation_group, - digitisation_df_filtered, - digitisation_end_group, - ], - ignore_index=True, - ) - - -def update_xls_form(custom_form: BytesIO) -> BytesIO: - custom_sheets = pd.read_excel(custom_form, sheet_name=None, engine="calamine") - default_form_path = f"{xlsforms_path}/fmtm/mandatory_fields.xls" - digitisation_form_path = f"{xlsforms_path}/fmtm/digitisation_fields.xls" - digitisation_sheets = pd.read_excel(digitisation_form_path, sheet_name=None, engine="calamine") - mandatory_sheets = pd.read_excel(default_form_path, sheet_name=None, engine="calamine") - - # Process and merge the 'survey' sheet if present in all forms - if "survey" in mandatory_sheets and "survey" in digitisation_sheets and "survey" in custom_sheets: - custom_sheets["survey"] = merge_sheets( - mandatory_sheets["survey"], custom_sheets["survey"], digitisation_sheets["survey"], True - ) - - # Process and merge the 'choices' sheet if present in all forms - if "choices" in mandatory_sheets and "choices" in digitisation_sheets and "choices" in custom_sheets: - custom_sheets["choices"] = merge_sheets( - mandatory_sheets["choices"], custom_sheets["choices"], digitisation_sheets["choices"] - ) - - # Append or overwrite the existing entities sheet - if "entities" in mandatory_sheets: - custom_sheets["entities"] = mandatory_sheets["entities"] - - if "settings" in mandatory_sheets: - custom_sheets["settings"] = mandatory_sheets["settings"] - current_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - # Set the 'version' column to the current timestamp (if 'version' column exists in 'settings') - if "version" in custom_sheets["settings"].columns: - # set column type to str - custom_sheets["settings"]["version"] = custom_sheets["settings"]["version"].astype(str) - custom_sheets["settings"].loc[:, "version"] = current_timestamp - - output = BytesIO() - with pd.ExcelWriter(output, engine="openpyxl") as writer: - for sheet_name, df in custom_sheets.items(): - df.to_excel(writer, sheet_name=sheet_name, index=False) - - output.seek(0) - return output diff --git a/osm_fieldwork/update_xlsform.py b/osm_fieldwork/update_xlsform.py new file mode 100644 index 000000000..31ff22df3 --- /dev/null +++ b/osm_fieldwork/update_xlsform.py @@ -0,0 +1,213 @@ +"""Update an existing XLSForm with additional fields useful for field mapping.""" + +from datetime import datetime +from io import BytesIO +from uuid import uuid4 + +import pandas as pd +from python_calamine.pandas import pandas_monkeypatch + +from osm_fieldwork.xlsforms import xlsforms_path + +# Monkeypatch pandas to add calamine driver +pandas_monkeypatch() + +# Constants +FEATURE_COLUMN = "feature" +NAME_COLUMN = "name" +SURVEY_GROUP_NAME = "survey_questions" +DIGITISATION_GROUP_NAME = "verification" + + +def filter_df_empty_rows(df, column=NAME_COLUMN): + """Remove rows with None values in the specified column, but retain group rows if they exist.""" + if column in df.columns: + # Only retain 'begin group' and 'end group' if 'type' column exists + if "type" in df.columns: + return df[(df[column].notna()) | (df["type"].isin(["begin group", "end group", "begin_group", "end_group"]))] + else: + return df[df[column].notna()] + return df + + +def merge_dataframes(mandatory_df, custom_df, digitisation_df, is_survey_sheet=False): + """Merge multiple Pandas dataframes together, keeping common fields in custom_df.""" + # Filter dataframes (remove rows with None in 'name' column) + mandatory_df = filter_df_empty_rows(mandatory_df) + custom_df = filter_df_empty_rows(custom_df) + digitisation_df = filter_df_empty_rows(digitisation_df) + + # Find common fields between custom_df and mandatory_df or digitisation_df + common_fields = set(custom_df[NAME_COLUMN]).intersection( + set(mandatory_df[NAME_COLUMN]).union(set(digitisation_df[NAME_COLUMN])) + ) + + # Split custom DataFrame into common and non-common fields + custom_common_df = custom_df[custom_df[NAME_COLUMN].isin(common_fields)] + custom_non_common_df = custom_df[~custom_df[NAME_COLUMN].isin(common_fields)] + + # Filter out common fields from mandatory and digitisation DataFrames + mandatory_df_filtered = mandatory_df[~mandatory_df[NAME_COLUMN].isin(common_fields)] + digitisation_df_filtered = digitisation_df[~digitisation_df[NAME_COLUMN].isin(common_fields)] + + if not is_survey_sheet: + return pd.concat( + [custom_common_df, mandatory_df_filtered, custom_non_common_df, digitisation_df_filtered], + ignore_index=True, + ) + + # Create groups for survey and digitisation + survey_group = create_group(SURVEY_GROUP_NAME) + digitisation_group = create_group(DIGITISATION_GROUP_NAME) + digitisation_group["start"]["relevant"] = ["(${new_feature} = 'yes') or (${building_exists} = 'yes')"] + + # Concatenate dataframes in the desired order + return pd.concat( + [ + custom_common_df, + mandatory_df_filtered, + survey_group["start"], + custom_non_common_df, + survey_group["end"], + digitisation_group["start"], + digitisation_df_filtered, + digitisation_group["end"], + ], + ignore_index=True, + ) + + +def create_group(name: str) -> dict[str, pd.DataFrame]: + """Helper function to create a start and end group for XLSForm.""" + start_group = pd.DataFrame({"type": ["begin group"], "name": [name]}) + end_group = pd.DataFrame({"type": ["end group"], "name": [f"end of {name}"]}) + return {"start": start_group, "end": end_group} + + +def append_select_one_from_file_row(df: pd.DataFrame, entity_name: str) -> pd.DataFrame: + """Add a new select_one_from_file question to reference an Entity.""" + # Find the row index where name column = 'feature' + select_one_from_file_index = df.index[df[NAME_COLUMN] == FEATURE_COLUMN].tolist() + + if not select_one_from_file_index: + raise ValueError(f"Row with '{NAME_COLUMN}' == '{FEATURE_COLUMN}' not found in survey sheet.") + + # Find the row index after 'feature' row + row_index_to_split_on = select_one_from_file_index[0] + 1 + # Strip the 's' from the end for singular form + if entity_name.endswith("s"): + # Plural to singular + entity_name = entity_name[:-1] + + additional_row = pd.DataFrame( + { + "type": [f"select_one_from_file {entity_name}.csv"], + "name": [entity_name], + "label::English(en)": [entity_name], + "appearance": ["map"], + "choice_filter": ["selected(${task_filter}, '') or task_id=${task_filter}"], + "trigger": ["${task_filter}"], + "label::Swahili(sw)": [entity_name], + "label::French(fr)": [entity_name], + "label::Spanish(es)": [entity_name], + } + ) + + # Insert the new row into the DataFrame + top_df = df.iloc[:row_index_to_split_on] + bottom_df = df.iloc[row_index_to_split_on:] + return pd.concat([top_df, additional_row, bottom_df], ignore_index=True) + + +def append_task_ids_to_choices_sheet(df: pd.DataFrame, task_count: int) -> pd.DataFrame: + """Add task id rows to choices sheet (for filtering Entity list).""" + task_ids = list(range(1, task_count + 1)) + + additional_rows = pd.DataFrame( + { + "list_name": ["task_filter"] * task_count, + "name": task_ids, + "label::English(en)": task_ids, + "label::Swahili(sw)": task_ids, + "label::French(fr)": task_ids, + "label::Spanish(es)": task_ids, + } + ) + + df = pd.concat([df, additional_rows], ignore_index=True) + return df + + +async def append_mandatory_fields( + custom_form: BytesIO, + form_category: str, + additional_entities: list[str] = None, + task_count: int = None, + existing_id: str = None, +) -> BytesIO: + """Append mandatory fields to the XLSForm for use in FMTM. + + Args: + custom_form(BytesIO): the XLSForm data uploaded, wrapped in BytesIO. + form_category(str): the form category name (in form_title and description). + additional_entities(list[str]): add extra select_one_from_file fields to + reference an additional Entity list (set of geometries). + The values should be plural, so that 's' will be stripped in the + field name. + task_count(int): number of tasks, used to generate task_id entries in choices + sheet. These are used to filter Entities by task id in ODK Collect. + existing_id(str): an existing UUID to use for the form_id, else random uuid4. + + Returns: + BytesIO: the update XLSForm, wrapped in BytesIO. + """ + custom_sheets = pd.read_excel(custom_form, sheet_name=None, engine="calamine") + mandatory_sheets = pd.read_excel(f"{xlsforms_path}/fmtm/mandatory_fields.xls", sheet_name=None, engine="calamine") + digitisation_sheets = pd.read_excel(f"{xlsforms_path}/fmtm/digitisation_fields.xls", sheet_name=None, engine="calamine") + + # Merge 'survey' and 'choices' sheets + if "survey" in custom_sheets: + custom_sheets["survey"] = merge_dataframes( + mandatory_sheets.get("survey"), custom_sheets.get("survey"), digitisation_sheets.get("survey"), True + ) + # Hardcode the form_category value for the start instructions + if form_category.endswith("s"): + # Plural to singular + form_category_singular = form_category[:-1] + form_category_row = custom_sheets["survey"].loc[custom_sheets["survey"]["name"] == "form_category"] + if not form_category_row.empty: + custom_sheets["survey"].loc[custom_sheets["survey"]["name"] == "form_category", "calculation"] = ( + f"once('{form_category_singular}')" + ) + + if "choices" in custom_sheets: + custom_sheets["choices"] = merge_dataframes( + mandatory_sheets.get("choices"), custom_sheets.get("choices"), digitisation_sheets.get("choices") + ) + + # Append or overwrite 'entities' and 'settings' sheets + custom_sheets.update({key: mandatory_sheets[key] for key in ["entities", "settings"] if key in mandatory_sheets}) + + # Set the 'version' column to the current timestamp (if 'version' column exists in 'settings') + if "settings" in custom_sheets: + custom_sheets["settings"]["version"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + custom_sheets["settings"]["form_id"] = existing_id if existing_id else uuid4() + custom_sheets["settings"]["form_title"] = form_category + + # Append select_one_from_file for additional entities + if additional_entities: + for entity_name in additional_entities: + custom_sheets["survey"] = append_select_one_from_file_row(custom_sheets["survey"], entity_name) + + # Append task id rows to choices sheet + if task_count: + custom_sheets["choices"] = append_task_ids_to_choices_sheet(custom_sheets["choices"], task_count) + + # Return spreadsheet wrapped as BytesIO memory object + output = BytesIO() + with pd.ExcelWriter(output, engine="openpyxl") as writer: + for sheet_name, df in custom_sheets.items(): + df.to_excel(writer, sheet_name=sheet_name, index=False) + + output.seek(0) + return output diff --git a/osm_fieldwork/xlsforms/buildings.xls b/osm_fieldwork/xlsforms/buildings.xls index 6551bc312e224cefbca04703f7501754995b451a..cfc6e46d7f37d6eb93a854c363b441484c7bbfb4 100644 GIT binary patch delta 38493 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 H<>mhXm;4H` literal 109568 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 diff --git a/osm_fieldwork/xlsforms/fmtm/mandatory_fields.xls b/osm_fieldwork/xlsforms/fmtm/mandatory_fields.xls index d297c2a6c7eba2fb65307968ead81a3a2482b77d..699ad3174c2d0eeba66bc689e00d528d91dae1ac 100644 GIT binary patch literal 31232 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 literal 17408 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 delta 12096 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. -# -"""Test functionalty of update_form.py.""" - -from io import BytesIO -from pathlib import Path - -from openpyxl import load_workbook - -from osm_fieldwork.update_form import update_xls_form - - -def test_merge_mandatory_fields(): - """Merge the mandatory fields XLSForm to a test survey form.""" - test_form = Path(__file__).parent / "testdata" / "test_form_for_mandatory_fields.xls" - with open(test_form, "rb") as xlsform: - form_bytes = BytesIO(xlsform.read()) - - updated_form = update_xls_form(form_bytes) - workbook = load_workbook(filename=BytesIO(updated_form.getvalue())) - - # Check the 'survey' sheet - if "survey" not in workbook.sheetnames: - raise ValueError("The 'survey' sheet was not found in the workbook") - survey_sheet = workbook["survey"] - - # Find the index of the 'name' column - name_col = None - for col in survey_sheet.iter_cols(1, survey_sheet.max_column): - if col[0].value == "name": - name_col = col - break - assert name_col is not None, "The 'name' column was not found." - - # Check if certain fields are present in the 'name' column (skip the header) - existing_field = any(cell.value == "existing" for cell in name_col[1:]) - assert existing_field, "'existing' field not found in the 'name' column." - - status_field = any(cell.value == "status" for cell in name_col[1:]) - assert status_field, "'status' field not found in the 'name' column." - - digitisation_correct_field = any(cell.value == "digitisation_correct" for cell in name_col[1:]) - assert digitisation_correct_field, "'digitisation_correct' field not found in the 'name' column." - - # Check that the 'name' column does not have a duplicate entry for 'username' - username_count = sum(1 for cell in name_col[1:] if cell.value == "username") - assert username_count <= 1, "Duplicate 'username' entry found in the 'name' column." - - # Check the 'choices' sheet - if "choices" not in workbook.sheetnames: - raise ValueError("The 'choices' sheet was not found in the workbook") - choices_sheet = workbook["choices"] - - # Find the index of the 'name' column in the 'choices' sheet - choices_name_col = None - for col in choices_sheet.iter_cols(1, choices_sheet.max_column): - if col[0].value == "name": - choices_name_col = col - break - - assert choices_name_col is not None, "'name' column was not found in the 'choices' sheet." - - # Test: Check that the 'choices' sheet does not have duplicate entries for 'yes' and 'no' - yes_count = sum(1 for cell in choices_name_col[1:] if cell.value == "yes") - no_count = sum(1 for cell in choices_name_col[1:] if cell.value == "no") - assert yes_count <= 1, "Duplicate 'yes' entry found in the 'value' column of 'choices' sheet." - assert no_count <= 1, "Duplicate 'no' entry found in the 'value' column of 'choices' sheet." - - # Check the 'entities' sheet - if "entities" not in workbook.sheetnames: - raise ValueError("The 'entities' sheet was not found in the workbook") - entities_sheet = workbook["entities"] - - # Find the index of the 'label' column in the 'entities' sheet - entities_label_col = None - for col in entities_sheet.iter_cols(1, entities_sheet.max_column): - if col[0].value == "label": - entities_label_col = col - break - - assert entities_label_col is not None, "'label' column was not found in the 'entities' sheet." - - # Check that the 'entities' label value of 'test label' is replaced by required value - test_label_present = any(cell.value == "test label" for cell in entities_label_col[1:]) - assert not test_label_present, "'test label' found in the 'label' column of 'entities' sheet." - - # TODO add test to check that digitisation questions come at end of sheet diff --git a/tests/test_update_xlsform.py b/tests/test_update_xlsform.py new file mode 100644 index 000000000..fcb17db47 --- /dev/null +++ b/tests/test_update_xlsform.py @@ -0,0 +1,148 @@ +# Copyright (c) 2022, 2023 Humanitarian OpenStreetMap Team +# +# This file is part of osm_fieldwork. +# +# osm-fieldwork is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# osm-fieldwork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with osm_fieldwork. If not, see . +# +"""Test functionalty of update_form.py.""" + +from io import BytesIO +from pathlib import Path + +from openpyxl import load_workbook + +from osm_fieldwork.update_xlsform import append_mandatory_fields + + +def get_column_index(sheet, column_name): + """Get the column index for the given column name.""" + for col_idx, col in enumerate(sheet.iter_cols(1, sheet.max_column), start=1): + if col[0].value == column_name: + return col_idx + raise ValueError(f"Column '{column_name}' not found.") + + +def get_row_index(sheet, column_index, value): + """Get the row index where the given column has the specified value.""" + for row_idx, row in enumerate(sheet.iter_rows(min_col=column_index, max_col=column_index), start=1): + if row[0].value == value: + return row_idx + raise ValueError(f"Value '{value}' not found in column {column_index}.") + + +async def test_merge_mandatory_fields(): + """Merge the mandatory fields XLSForm to a test survey form.""" + test_form = Path(__file__).parent / "testdata" / "test_form_for_mandatory_fields.xls" + with open(test_form, "rb") as xlsform: + form_bytes = BytesIO(xlsform.read()) + + updated_form = await append_mandatory_fields(form_bytes, "buildings") + workbook = load_workbook(filename=BytesIO(updated_form.getvalue())) + + # Check the 'survey' sheet + if "survey" not in workbook.sheetnames: + raise ValueError("The 'survey' sheet was not found in the workbook") + survey_sheet = workbook["survey"] + + name_col_index = get_column_index(survey_sheet, "name") + calculation_col_index = get_column_index(survey_sheet, "calculation") + + form_category_row_index = get_row_index(survey_sheet, name_col_index, "form_category") + form_category_calculation = survey_sheet.cell(row=form_category_row_index, column=calculation_col_index).value + expected_calculation = "once('building')" + assert ( + form_category_calculation == expected_calculation + ), f"Expected 'calculation' value for 'form_category' to be '{expected_calculation}', but got '{form_category_calculation}'." + + # Check the 'choices' sheet + if "choices" not in workbook.sheetnames: + raise ValueError("The 'choices' sheet was not found in the workbook") + choices_sheet = workbook["choices"] + + choices_name_col_index = get_column_index(choices_sheet, "name") + + # Check that the 'choices' sheet does not have duplicate entries for 'yes' and 'no' + yes_count = sum( + 1 + for row in choices_sheet.iter_rows(min_col=choices_name_col_index, max_col=choices_name_col_index, min_row=2) + if row[0].value == "yes" + ) + no_count = sum( + 1 + for row in choices_sheet.iter_rows(min_col=choices_name_col_index, max_col=choices_name_col_index, min_row=2) + if row[0].value == "no" + ) + assert yes_count <= 1, "Duplicate 'yes' entry found in the 'value' column of 'choices' sheet." + assert no_count <= 1, "Duplicate 'no' entry found in the 'value' column of 'choices' sheet." + + # Check the 'entities' sheet + if "entities" not in workbook.sheetnames: + raise ValueError("The 'entities' sheet was not found in the workbook") + entities_sheet = workbook["entities"] + + entities_label_col_index = get_column_index(entities_sheet, "label") + + # Check that the 'entities' label value of 'test label' is replaced by required value + test_label_present = any( + row[0].value == "test label" + for row in entities_sheet.iter_rows(min_col=entities_label_col_index, max_col=entities_label_col_index, min_row=2) + ) + assert not test_label_present, "'test label' found in the 'label' column of 'entities' sheet." + + # Check that form_title is set correctly + if "settings" not in workbook.sheetnames: + raise ValueError("The 'settings' sheet was not found in the workbook") + settings_sheet = workbook["settings"] + + form_title_col_index = get_column_index(settings_sheet, "form_title") + form_title_value = settings_sheet.cell(row=2, column=form_title_col_index).value + assert form_title_value == "buildings", "form_title field is not set to 'buildings'" + + # TODO add test to check that digitisation questions come at end of sheet + + +async def test_add_extra_select_from_file(): + """Append extra select_one_from_file questions based on Entity list names.""" + test_form = Path(__file__).parent / "testdata" / "test_form_for_mandatory_fields.xls" + with open(test_form, "rb") as xlsform: + form_bytes = BytesIO(xlsform.read()) + + updated_form = await append_mandatory_fields(form_bytes, "buildings", additional_entities=["roads", "waterpoints"]) + workbook = load_workbook(filename=BytesIO(updated_form.getvalue())) + + survey_sheet = workbook["survey"] + # Assuming 'name' is in column B + name_column = [cell.value for cell in survey_sheet["B"]] + assert "road" in name_column, "The 'road' field was not added to the survey sheet." + assert "waterpoint" in name_column, "The 'waterpoint' field was not added to the survey sheet." + + +async def test_add_task_ids_to_choices(): + """Test appending each task id as a row in choices sheet.""" + test_form = Path(__file__).parent / "testdata" / "test_form_for_mandatory_fields.xls" + with open(test_form, "rb") as xlsform: + form_bytes = BytesIO(xlsform.read()) + + task_count = 7 + updated_form = await append_mandatory_fields(form_bytes, "buildings", task_count=task_count) + workbook = load_workbook(filename=BytesIO(updated_form.getvalue())) + + survey_sheet = workbook["choices"] + # Assuming 'name' is in column B + name_column = [cell.value for cell in survey_sheet["B"]] + + # Assert each task_id is in the name_column + task_ids = [1, 2, 3, 4, 5, 6, 7] + for task_id in task_ids: + assert task_id in name_column, f"Task ID {task_id} not found in the choices sheet."