From a1320d19f6eb5433b61421332249a45dd8dd84f8 Mon Sep 17 00:00:00 2001 From: yshalenyk Date: Fri, 28 Jun 2024 14:55:45 +0300 Subject: [PATCH] refactor: rename Mapping class to correct MappingTemplate name --- docs/examples.rst | 22 +++++++++---------- docs/tutorial.rst | 14 ++++++------ nightingale/cli.py | 2 +- nightingale/mapper.py | 12 +++++----- nightingale/mapping/v1/__init__.py | 2 -- .../{mapping => mapping_template}/__init__.py | 0 .../v09/__init__.py} | 11 ++++------ .../v1 => mapping_template}/validator.py | 0 nightingale/utils.py | 6 +++++ tests/test_mapping_loader.py | 22 +++++++++---------- tests/test_unflatten.py | 10 ++++----- tests/test_validator.py | 10 ++++----- 12 files changed, 56 insertions(+), 55 deletions(-) delete mode 100644 nightingale/mapping/v1/__init__.py rename nightingale/{mapping => mapping_template}/__init__.py (100%) rename nightingale/{mapping/v1/config.py => mapping_template/v09/__init__.py} (97%) rename nightingale/{mapping/v1 => mapping_template}/validator.py (100%) diff --git a/docs/examples.rst b/docs/examples.rst index a778430..17078d2 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -36,13 +36,13 @@ Example 1: Basic Transformation [output] directory = 'output' -2. **Sample Mapping File (`mapping.xlsx`):** +2. **Sample MappingTemplate File (`mapping.xlsx`):** **General Sheet:** .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|----------------|-----------------------------|----------|------------------------------------ | ------- 1 | OCID | unique ID | ocid | Required | example_table (id) | - 2 | Party Identifier | Identifier | parties/[0]/identifier/id | Required | party_table (identifier) | - @@ -53,7 +53,7 @@ Example 1: Basic Transformation .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|-----------------|----------------------------|----------|------------------------------------ | ------- 1 | Tender Title | Name of tender | tender/title | Optional | example_table (name) | - 2 | Value | Tender value | tender/value/amount | Optional | example_table (value) | - @@ -101,7 +101,7 @@ Example 2: Transformation with Packaging [output] directory = 'output' -2. **Sample Mapping File (`mapping.xlsx`):** +2. **Sample MappingTemplate File (`mapping.xlsx`):** Use the same `mapping.xlsx` as in Example 1. @@ -154,13 +154,13 @@ You may need to manipulate data within the SQL query itself before it is fed int [output] directory = 'output' -2. **Sample Mapping File (`mapping.xlsx`):** +2. **Sample MappingTemplate File (`mapping.xlsx`):** **General Sheet:** .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|----------------|-----------------------------|----------|------------------------------------ | ------- 1 | OCID | unique ID | ocid | Required | example_table (id) | - 2 | Party Identifier | Identifier | parties/[0]/identifier/id | Required | party_table (identifier) | - @@ -171,7 +171,7 @@ You may need to manipulate data within the SQL query itself before it is fed int .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|-----------------|----------------------------|----------|------------------------------------ | ------- 1 | Tender Title | Tender title | tender/title | Optional | example_table (name) | - 2 | Value | Tender value | tender/value/amount | Optional | example_table (value) | - @@ -272,13 +272,13 @@ If the required data spans across multiple tables, you can use SQL JOINs to comb [output] directory = 'output' -3. **Sample Mapping File (`mapping.xlsx`):** +3. **Sample MappingTemplate File (`mapping.xlsx`):** **General Sheet:** .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|----------------|-----------------------------|----------|------------------------------------ | ------- 1 | OCID | unique ID | ocid | Required | example_table (id) | - 2 | Party Identifier | Identifier | parties/[0]/identifier/id | Required | party_table (identifier) | - @@ -289,7 +289,7 @@ If the required data spans across multiple tables, you can use SQL JOINs to comb .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|--------------|----------------------------|----------|------------------------------------ | ------- 1 | Tender Title | Tender title | tender/title | Optional | example_table (name) | - 2 | Value | Tender value | tender/value/amount | Optional | example_table (value) | - @@ -298,7 +298,7 @@ If the required data spans across multiple tables, you can use SQL JOINs to comb .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|--------------------------------|-------------------------------|----------|----------------------------------- | ------- 1 | Description | Description from another table | contracts/[0]/description | Optional | another_table (description) | - diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 642c1f2..0ee95dc 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -92,7 +92,7 @@ Setup [output] directory = 'output' -3. **Prepare the Mapping File:** +3. **Prepare the MappingTemplate File:** Use the following configuration for `mapping.xlsx` based on the `ocds field level mapping` template: @@ -100,7 +100,7 @@ Setup .. code-block:: text - | Title | Description | Path | Status | Mapping | Comment + | Title | Description | Path | Status | MappingTemplate | Comment |---------------|---------------|------------------|----------|------------------------------------ | ------- | OCID | unique ID | ocid | Required | example_table (id) | - | Party ID | Party ID | parties/id | Optional | party_table (identifier) | - @@ -112,7 +112,7 @@ Setup .. code-block:: text - id | Title | Description | Path | Status | Mapping | Comment + id | Title | Description | Path | Status | MappingTemplate | Comment ----|----------------|--------------|-------------------|----------|------------------------------------ | ------- 1 | Tender Title | Tender title | tender/title | Optional | example_table (name) | - 2 | Value | Tender value | tender/value/amount | Optional | example_table (value) | - @@ -129,17 +129,17 @@ Run the transformation using the CLI: This will produce an output file in the `output` directory. -Mapping Configuration +MappingTemplate Configuration ---------------------- -Field-level mapping is specified in the `mapping.xlsx` file. It is formed from stardard `"OCDS Field Level Mapping template" `_. -For more information about how to fill the mapping file, refer to the `OCDS Field Level Mapping template guidance `_. +Field-level mapping is specified in the `mapping.xlsx` file. It is formed from stardard `"OCDS Field Level MappingTemplate template" `_. +For more information about how to fill the mapping file, refer to the `OCDS Field Level MappingTemplate template guidance `_. Here the bried description of the columns from mapping sheets in the mapping file: * **Path**: The path in the OCDS release schema where the field value should be placed. * **Title**: A human-readable title for the field. * **Description**: A description of what the field represents. - * **Mapping**: The field in the source data that maps to the OCDS path. + * **MappingTemplate**: The field in the source data that maps to the OCDS path. Understanding these mappings will help you configure the transformation correctly for your data. diff --git a/nightingale/cli.py b/nightingale/cli.py index 65dd4e6..0564e89 100644 --- a/nightingale/cli.py +++ b/nightingale/cli.py @@ -48,7 +48,7 @@ def run(config_file, package, validate_mapping, loglevel): config = Config.from_file(config_file) mapper = OCDSDataMapper(config) writer = DataWriter(config.output) - logger.info("Mapping data...") + logger.info("MappingTemplate data...") ocds_data = mapper.map(DataLoader(config.datasource), validate_mapping=validate_mapping) if package: diff --git a/nightingale/mapper.py b/nightingale/mapper.py index 8cda0f8..c8b0525 100644 --- a/nightingale/mapper.py +++ b/nightingale/mapper.py @@ -4,7 +4,7 @@ import dict_hash from .config import Config -from .mapping.v1 import Mapping, MappingTemplateValidator +from .mapping_template.v09 import MappingTemplate, MappingTemplateValidator from .utils import get_iso_now, is_new_array, remove_dicts_without_id logger = logging.getLogger(__name__) @@ -49,7 +49,7 @@ def map(self, loader: Any, validate_mapping: bool = False) -> list[dict[str, Any :rtype: list[dict[str, Any]] """ config = self.config.mapping - mapping = Mapping(config) + mapping = MappingTemplate(config) data = loader.load(config.selector) if validate_mapping: validator = MappingTemplateValidator(loader, mapping) @@ -58,14 +58,14 @@ def map(self, loader: Any, validate_mapping: bool = False) -> list[dict[str, Any logger.info("Start mapping data") return self.transform_data(data, mapping) - def transform_data(self, data: list[dict[Any, Any]], mapping: Mapping) -> list[dict[str, Any]]: + def transform_data(self, data: list[dict[Any, Any]], mapping: MappingTemplate) -> list[dict[str, Any]]: """ Transform the input data to the OCDS format. :param data: List of input data dictionaries. :type data: list[dict[Any, Any]] :param mapping: Mapping configuration object. - :type mapping: Mapping + :type mapping: MappingTemplate :return: List of transformed release dictionaries. :rtype: list[dict[str, Any]] """ @@ -103,7 +103,7 @@ def finish_release(self, curr_ocid, curr_release, mapped): def transform_row( self, input_data: dict[Any, Any], - mapping_config: Mapping, + mapping_config: MappingTemplate, flattened_schema: dict[str, Any], result: dict = None, ) -> dict: @@ -113,7 +113,7 @@ def transform_row( :param input_data: Dictionary of input data. :type input_data: dict[Any, Any] :param mapping_config: Mapping configuration object. - :type mapping_config: Mapping + :type mapping_config: MappingTemplate :param flattened_schema: Flattened schema dictionary. :type flattened_schema: dict[str, Any] :param result: Existing result dictionary to update. diff --git a/nightingale/mapping/v1/__init__.py b/nightingale/mapping/v1/__init__.py deleted file mode 100644 index 8c2834e..0000000 --- a/nightingale/mapping/v1/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .config import Mapping # noqa -from .validator import MappingTemplateValidator # noqa diff --git a/nightingale/mapping/__init__.py b/nightingale/mapping_template/__init__.py similarity index 100% rename from nightingale/mapping/__init__.py rename to nightingale/mapping_template/__init__.py diff --git a/nightingale/mapping/v1/config.py b/nightingale/mapping_template/v09/__init__.py similarity index 97% rename from nightingale/mapping/v1/config.py rename to nightingale/mapping_template/v09/__init__.py index 1183800..615fd71 100644 --- a/nightingale/mapping/v1/config.py +++ b/nightingale/mapping_template/v09/__init__.py @@ -2,6 +2,9 @@ import openpyxl +from nightingale.mapping_template.validator import MappingTemplateValidator # noqa +from nightingale.utils import get_longest_array_path + logger = logging.getLogger(__name__) @@ -19,7 +22,7 @@ SCHEMA_SHEET = "OCDS Schema" -class Mapping: +class MappingTemplate: def __init__(self, config): self.config = config self.wb = openpyxl.load_workbook(self.config.file, data_only=True) @@ -197,9 +200,3 @@ def get_ocid_mapping(self): def get_containing_array_path(self, path): return get_longest_array_path(self.get_arrays(), path) - - -def get_longest_array_path(arrays, path): # extract for testing - for array in reversed(sorted(arrays, key=len)): - if path.startswith(array): - return array diff --git a/nightingale/mapping/v1/validator.py b/nightingale/mapping_template/validator.py similarity index 100% rename from nightingale/mapping/v1/validator.py rename to nightingale/mapping_template/validator.py diff --git a/nightingale/utils.py b/nightingale/utils.py index 089f818..471925c 100644 --- a/nightingale/utils.py +++ b/nightingale/utils.py @@ -67,3 +67,9 @@ def is_new_array(array_counters, child_path, array_key, array_value, array_path) if array_key == "id" and "/" + array_key == child_path and array_counters[array_path] != array_value: return True return False + + +def get_longest_array_path(arrays, path): # extract for testing + for array in reversed(sorted(arrays, key=len)): + if path.startswith(array): + return array diff --git a/tests/test_mapping_loader.py b/tests/test_mapping_loader.py index 586ad17..e68157f 100644 --- a/tests/test_mapping_loader.py +++ b/tests/test_mapping_loader.py @@ -3,8 +3,8 @@ import openpyxl import pytest -from nightingale.mapping.v1 import Mapping -from nightingale.mapping.v1.config import get_longest_array_path +from nightingale.mapping_template.v09 import MappingTemplate +from nightingale.utils import get_longest_array_path def test_sheets(): @@ -136,7 +136,7 @@ class Config: @patch("openpyxl.load_workbook") def test_mapping_init(mock_load_workbook, mock_workbook, mock_config): mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) assert mapping.wb == mock_workbook @@ -150,7 +150,7 @@ def test_normmalize_mapping_column(mock_config): {"mapping": "field1 field2"}, {"mapping": "field3 field4"}, ] - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) result = mapping.normmalize_mapping_column(mappings) assert result == expected_result @@ -160,7 +160,7 @@ def test_read_data_elements_sheet(mock_load_workbook, mock_workbook, mock_config mock_workbook.__getitem__ = lambda self, x: getter(x) mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) expected_data_elements = { "element1": { @@ -193,7 +193,7 @@ def test_read_schema_sheet(mock_load_workbook, mock_workbook, mock_config): mock_workbook.__getitem__ = lambda self, x: getter(x) mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) schema = mapping.get_schema() expected_schema = { @@ -232,7 +232,7 @@ def test_enforce_mapping_structure(mock_load_workbook, mock_workbook, mock_confi {"path": "general/item6", "mapping": "map6"}, ] - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) result = mapping.enforce_mapping_structure(mappings) expected_result = [ @@ -252,7 +252,7 @@ def test_read_mappings(mock_load_workbook, mock_workbook, mock_config): mock_workbook.__getitem__ = lambda self, x: getter(x) mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) expected_mappings = [ { @@ -491,7 +491,7 @@ def test_get_element_by_mapping(mock_load_workbook, mock_workbook, mock_config): mock_workbook.__getitem__.side_effect = lambda name: mock_sheet mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) element = mapping.get_element_by_mapping("mapping1 (element1)") expected_element = { @@ -565,7 +565,7 @@ def test_get_paths_for_mapping(mock_load_workbook, mock_workbook, mock_config): }, ] - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) mapping.mappings = mock_mappings paths = mapping.get_paths_for_mapping("mapping1 (element1)") @@ -580,7 +580,7 @@ def test_is_array_path(mock_load_workbook, mock_workbook, mock_config): mock_workbook.__getitem__ = lambda self, x: getter(x) mock_load_workbook.return_value = mock_workbook - mapping = Mapping(mock_config) + mapping = MappingTemplate(mock_config) mapping.schema = mapping.read_schema_sheet() assert mapping.is_array_path("/array_path") is True assert mapping.is_array_path("/path2") is False diff --git a/tests/test_unflatten.py b/tests/test_unflatten.py index e8a9423..44c21b3 100644 --- a/tests/test_unflatten.py +++ b/tests/test_unflatten.py @@ -6,7 +6,7 @@ from nightingale.config import Config, Datasource, Mapping, Output, Publishing from nightingale.mapper import OCDSDataMapper -from nightingale.mapping.v1.config import get_longest_array_path +from nightingale.utils import get_longest_array_path class MockMappingConfig: @@ -354,8 +354,8 @@ def test_map(mock_config, mocker): loader = mocker.Mock() loader.load.return_value = [{"ocid": "1", "field": "value"}] - # Mock the Mapping class to avoid reading from an actual file - with mock.patch("nightingale.mapper.Mapping") as MockMapping: + # Mock the MappingTemplate class to avoid reading from an actual file + with mock.patch("nightingale.mapper.MappingTemplate") as MockMapping: MockMapping.return_value = MockMappingConfig({}) mapper = OCDSDataMapper(mock_config) result = mapper.map(loader) @@ -423,7 +423,7 @@ def test_map_with_validation(mock_config): mock_validator_instance = MockValidator.return_value mock_validator_instance.validate_data_elements = mock.MagicMock() mock_validator_instance.validate_selector = mock.MagicMock() - with mock.patch("nightingale.mapper.Mapping") as mock_mapping_template: + with mock.patch("nightingale.mapper.MappingTemplate") as mock_mapping_template: mapper = OCDSDataMapper(mock_config) mapper.map(loader, validate_mapping=True) MockValidator.assert_called_with(loader, mock_mapping_template()) @@ -472,7 +472,7 @@ def test_finish_release(mock_get_iso_now, mock_config): @mock.patch("nightingale.mapper.get_iso_now") -@mock.patch("nightingale.mapper.Mapping") +@mock.patch("nightingale.mapper.MappingTemplate") def test_finish_release_with_tags(mock_mapping, mock_get_iso_now, mock_config): mock_get_iso_now.return_value = "2022-01-01T00:00:00Z" mock_mapping.get_ocid_mapping.return_value = "ocid" diff --git a/tests/test_validator.py b/tests/test_validator.py index c9c4d99..effb37e 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import Mock, patch -from nightingale.mapping.v1.validator import MappingTemplateValidator +from nightingale.mapping_template.validator import MappingTemplateValidator class TestMappingTemplateValidator(unittest.TestCase): @@ -13,7 +13,7 @@ def setUp(self): # Create instance of the class self.validator = MappingTemplateValidator(self.loader, self.mapping_template) - @patch("nightingale.mapping.v1.validator.logger") + @patch("nightingale.mapping_template.validator.logger") def test_validate_data_elements_all_columns_described(self, mock_logger): # Mock the cursor and database table structure cursor = Mock() @@ -62,7 +62,7 @@ def test_validate_data_elements_all_columns_described(self, mock_logger): # Ensure no warnings were logged mock_logger.warning.assert_not_called() - @patch("nightingale.mapping.v1.validator.logger") + @patch("nightingale.mapping_template.validator.logger") def test_validate_data_elements_missing_columns(self, mock_logger): # Mock the cursor and database table structure cursor = Mock() @@ -107,7 +107,7 @@ def test_validate_data_elements_missing_columns(self, mock_logger): # Check if a warning was logged for the missing column mock_logger.warning.assert_called_with("Column table1/missing is not described in data elements") - @patch("nightingale.mapping.v1.validator.logger") + @patch("nightingale.mapping_template.validator.logger") def test_validate_selector_all_columns_mapped(self, mock_logger): # Mock the mapping template data elements self.mapping_template.get_data_elements.return_value = { @@ -142,7 +142,7 @@ def test_validate_selector_all_columns_mapped(self, mock_logger): # Ensure no warnings were logged mock_logger.warning.assert_not_called() - @patch("nightingale.mapping.v1.validator.logger") + @patch("nightingale.mapping_template.validator.logger") def test_validate_selector_missing_columns(self, mock_logger): # Mock the mapping template data elements self.mapping_template.get_data_elements.return_value = {