From 5b4d9c0a07b052a0c7b32e3f4fecb42becc7fb60 Mon Sep 17 00:00:00 2001 From: sujata-m Date: Thu, 18 Dec 2025 14:24:53 +0530 Subject: [PATCH 1/4] Added Support for 0.3 OSW schema ## Dev Board Ticket https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2624 https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2670/ https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2668/ https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2667/ https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2659/ https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2624/ https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2424/ ## Changes - Updated `osm-osw-reformatter` package version from `0.2.12` to `0.3.0` ## Testing TDEI Portal --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3a405e7..bc08b0b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,5 @@ pydantic==1.10.4 python-ms-core==0.0.23 uvicorn==0.20.0 html_testRunner==1.2.1 -osm-osw-reformatter==0.2.12 +osm-osw-reformatter==0.3.0 numpy==1.26.4 \ No newline at end of file From 6199ef5d626c47469f779f2e90e27238305f4aac Mon Sep 17 00:00:00 2001 From: sujata-m Date: Thu, 18 Dec 2025 14:54:30 +0530 Subject: [PATCH 2/4] Fixed ISSUE-2415 ## Dev Board Ticket https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2415 ## Changes - Updated `osm-osw-reformatter` package version from `0.2.12` to `0.3.0` ## Testing TDEI Portal --- src/service/osw_formatter_service.py | 25 +++++++---- .../service/test_osw_formatter_service.py | 23 +++++----- tests/unit_tests/service/test_service.py | 44 ++++++++++++++++++- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/service/osw_formatter_service.py b/src/service/osw_formatter_service.py index 239c673..2e398cc 100644 --- a/src/service/osw_formatter_service.py +++ b/src/service/osw_formatter_service.py @@ -111,10 +111,10 @@ def format(self, received_message: OSWValidationMessage): formatter_result = ValidationResult() if result and result.status and result.error is None and result.generated_files is not None: # Generated files can be .xml or a bunch of geojson - if isinstance(result.generated_files, list): # If it's a list - converted_file = formatter.create_zip(result.generated_files) - else: - converted_file = result.generated_files + converted_file = self._prepare_upload_file( + formatter=formatter, + generated_files=result.generated_files, + ) upload_path = self.upload_to_azure( file_path=converted_file, project_group_id=received_message.data.tdei_project_group_id, @@ -211,10 +211,10 @@ def process_on_demand_format(self, request: OSWOnDemandRequest): # Create remote path if result and result.status and result.error is None and result.generated_files is not None: logger.info('Formatting complete') - if isinstance(result.generated_files, list): # If its a list - converted_file = formatter.create_zip(result.generated_files) - else: - converted_file = result.generated_files + converted_file = self._prepare_upload_file( + formatter=formatter, + generated_files=result.generated_files, + ) target_directory = f'jobs/{request.data.jobId}/{request.data.target}' target_file_remote_path = f'{target_directory}/{os.path.basename(converted_file)}' @@ -288,6 +288,15 @@ def upload_to_azure_on_demand(self, remote_path: str, local_url: str): file.upload(data) return file.get_remote_url() + def _prepare_upload_file(self, formatter: OSWFormat, generated_files): + if isinstance(generated_files, list): + return formatter.create_zip(generated_files) + if isinstance(generated_files, str): + _, extension = os.path.splitext(generated_files) + if extension.lower() == ".xml": + return formatter.create_zip([generated_files]) + return generated_files + def stop_listening(self): self.listening_thread.join(timeout=0) return diff --git a/tests/unit_tests/service/test_osw_formatter_service.py b/tests/unit_tests/service/test_osw_formatter_service.py index bc06633..b3a03c8 100644 --- a/tests/unit_tests/service/test_osw_formatter_service.py +++ b/tests/unit_tests/service/test_osw_formatter_service.py @@ -66,15 +66,15 @@ def test_format_success(self, mock_send_status, mock_download_single_file): # Set up mock data file_path = f'{SAVED_FILE_PATH}/osw.zip' # Mock OSWFormat instance - with patch.object(OSWFormat, '__init__', return_value=None): + with patch.object(OSWFormat, '__init__', return_value=None), \ + patch.object(OSWFormat, 'format', return_value=Mock(status=True, error=None, generated_files='file1.xml')), \ + patch.object(OSWFormat, 'create_zip', return_value='file1.zip') as mock_create_zip: self.OSW_format = OSWFormat(file_path=file_path, storage_client=MagicMock()) self.OSW_format.file_path = MagicMock() self.OSW_format.file_path.return_value = Mock(file_path) self.OSW_format.file_relative_path = MagicMock() self.OSW_format.file_relative_path.return_value = Mock(file_path.split('/')[-1]) mock_download_single_file.return_value = f'{DOWNLOAD_PATH}/osw.zip' - self.OSW_format.format = MagicMock() - self.OSW_format.format.return_value = Mock(status=True, error=None, generated_files='file1.xml') # Arrange received_message = OSWValidationMessage(data=TEST_DATA) @@ -89,6 +89,7 @@ def test_format_success(self, mock_send_status, mock_download_single_file): # Assert mock_send_status.assert_called_once() + mock_create_zip.assert_called_once_with(['file1.xml']) @patch.object(OSWFomatterService, 'send_status') def test_format_failure(self, mock_send_status): @@ -96,10 +97,10 @@ def test_format_failure(self, mock_send_status): file_path = f'{SAVED_FILE_PATH}/osw.zip' # Mock OSWFormat instance - with patch.object(OSWFormat, '__init__', return_value=None): + with patch.object(OSWFormat, '__init__', return_value=None), \ + patch.object(OSWFormat, 'format', return_value=Mock(status=True, error=None, generated_files='file1.xml')), \ + patch.object(OSWFormat, 'create_zip', return_value='file1.zip'): self.OSW_format = OSWFormat(file_path=file_path, storage_client=MagicMock()) - self.OSW_format.format = MagicMock() - self.OSW_format.format.return_value = Mock(status=True, error=None, generated_files='file1.xml') # Arrange received_message = OSWValidationMessage(data=TEST_DATA) @@ -121,10 +122,10 @@ def test_format_failure_with_invalid_file_path(self, mock_send_status): file_path = f'{SAVED_FILE_PATH}/osw.zip' # Mock OSWFormat instance - with patch.object(OSWFormat, '__init__', return_value=None): + with patch.object(OSWFormat, '__init__', return_value=None), \ + patch.object(OSWFormat, 'format', return_value=Mock(status=True, error=None, generated_files='file1.xml')), \ + patch.object(OSWFormat, 'create_zip', return_value='file1.zip'): self.OSW_format = OSWFormat(file_path=file_path, storage_client=MagicMock()) - self.OSW_format.format = MagicMock() - self.OSW_format.format.return_value = Mock(status=True, error=None, generated_files='file1.xml') # Arrange received_message = OSWValidationMessage(data=TEST_DATA) @@ -207,7 +208,8 @@ def test_invalid_send_status(self): @patch.object(OSWFormat, 'format') def test_process_on_demand_format_success(self, mock_format): file_path = f'{SAVED_FILE_PATH}/osw.zip' - with patch.object(OSWFormat, '__init__', return_value=None): + with patch.object(OSWFormat, '__init__', return_value=None), \ + patch.object(OSWFormat, 'create_zip', return_value='file1.zip') as mock_create_zip: mock_init = OSWFormat(file_path=file_path, storage_client=MagicMock()) mock_init.file_path = file_path mock_init.file_relative_path = file_path.split('/')[-1] @@ -227,6 +229,7 @@ def test_process_on_demand_format_success(self, mock_format): self.formatter.process_on_demand_format(request=request) mock_format.assert_called_once() + mock_create_zip.assert_called_once_with(['file1.xml']) @patch.object(OSWFormat, 'format') def test_process_on_demand_format_failure(self, mock_format): diff --git a/tests/unit_tests/service/test_service.py b/tests/unit_tests/service/test_service.py index 4f11f6f..cc8d016 100644 --- a/tests/unit_tests/service/test_service.py +++ b/tests/unit_tests/service/test_service.py @@ -227,15 +227,18 @@ def test_format_with_generated_file(self, mock_osw_format): mock_format_result = MagicMock() mock_format_result.status = True mock_format_result.error = None - mock_format_result.generated_files = 'file1.geojson' + mock_format_result.generated_files = 'file1.xml' mock_osw_instance.format.return_value = mock_format_result + mock_osw_instance.create_zip.return_value = 'zipped_file.zip' + self.service.upload_to_azure = MagicMock() self.service.upload_to_azure.return_value = 'uploaded_path' # Act self.service.format(received_message) + mock_osw_instance.create_zip.assert_called_once_with(['file1.xml']) self.service.upload_to_azure.assert_called_once() @patch('src.service.osw_formatter_service.OSWFormat') @@ -277,6 +280,45 @@ def test_process_on_demand_format_with_generated_files_list(self, mock_osw_forma mock_osw_instance.create_zip.assert_called_once_with(['file1.geojson', 'file2.geojson']) self.service.upload_to_azure_on_demand.assert_called_once() + @patch('src.service.osw_formatter_service.OSWFormat') + def test_process_on_demand_format_with_generated_xml(self, mock_osw_format): + # Arrange + message_data = { + 'sourceUrl': 'http://example.com/file.osm', + 'jobId': '1234', + 'source': 'source_format', + 'target': 'target_format' + } + received_message = OSWOnDemandRequest( + messageId='1234', + messageType='message_type', + data=message_data + ) + + # Mock OSWFormat instance + mock_osw_instance = MagicMock() + mock_osw_format.return_value = mock_osw_instance + + # Mock format method return values + mock_format_result = MagicMock() + mock_format_result.status = True + mock_format_result.error = None + mock_format_result.generated_files = 'file1.xml' + mock_osw_instance.format.return_value = mock_format_result + + # Mock create_zip return value + mock_osw_instance.create_zip.return_value = 'zipped_file.zip' + + self.service.upload_to_azure_on_demand = MagicMock() + self.service.upload_to_azure_on_demand.return_value = 'uploaded_path' + + # Act + self.service.process_on_demand_format(received_message) + + # Assert + mock_osw_instance.create_zip.assert_called_once_with(['file1.xml']) + self.service.upload_to_azure_on_demand.assert_called_once() + @patch('src.service.osw_formatter_service.OSWFormat') @patch('src.service.osw_formatter_service.logger') def test_process_on_demand_format_exception(self, mock_logger, mock_osw_format): From 4dfaa0ff820cf232243dcb32bc9ab1696ff27d4e Mon Sep 17 00:00:00 2001 From: sujata-m Date: Thu, 18 Dec 2025 15:07:20 +0530 Subject: [PATCH 3/4] Fixed unit test cases --- .../service/test_osw_formatter_service.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/unit_tests/service/test_osw_formatter_service.py b/tests/unit_tests/service/test_osw_formatter_service.py index b3a03c8..844b4e3 100644 --- a/tests/unit_tests/service/test_osw_formatter_service.py +++ b/tests/unit_tests/service/test_osw_formatter_service.py @@ -60,21 +60,18 @@ def test_start_listening(self, mock_start_listening): # Assert mock_start_listening.assert_called_once() + @patch('src.service.osw_formatter_service.OSWFormat') @patch.object(OSWFormat, 'download_single_file') @patch.object(OSWFomatterService, 'send_status') - def test_format_success(self, mock_send_status, mock_download_single_file): + def test_format_success(self, mock_send_status, mock_download_single_file, mock_osw_format): # Set up mock data file_path = f'{SAVED_FILE_PATH}/osw.zip' # Mock OSWFormat instance - with patch.object(OSWFormat, '__init__', return_value=None), \ - patch.object(OSWFormat, 'format', return_value=Mock(status=True, error=None, generated_files='file1.xml')), \ - patch.object(OSWFormat, 'create_zip', return_value='file1.zip') as mock_create_zip: - self.OSW_format = OSWFormat(file_path=file_path, storage_client=MagicMock()) - self.OSW_format.file_path = MagicMock() - self.OSW_format.file_path.return_value = Mock(file_path) - self.OSW_format.file_relative_path = MagicMock() - self.OSW_format.file_relative_path.return_value = Mock(file_path.split('/')[-1]) - mock_download_single_file.return_value = f'{DOWNLOAD_PATH}/osw.zip' + mock_osw_instance = MagicMock() + mock_osw_instance.format.return_value = Mock(status=True, error=None, generated_files='file1.xml') + mock_osw_instance.create_zip.return_value = 'file1.zip' + mock_osw_format.return_value = mock_osw_instance + mock_download_single_file.return_value = f'{DOWNLOAD_PATH}/osw.zip' # Arrange received_message = OSWValidationMessage(data=TEST_DATA) @@ -89,7 +86,7 @@ def test_format_success(self, mock_send_status, mock_download_single_file): # Assert mock_send_status.assert_called_once() - mock_create_zip.assert_called_once_with(['file1.xml']) + mock_osw_instance.create_zip.assert_called_once_with(['file1.xml']) @patch.object(OSWFomatterService, 'send_status') def test_format_failure(self, mock_send_status): From d44438c5f1498decc8b3d262d02ded404b5bf64c Mon Sep 17 00:00:00 2001 From: sujata-m Date: Fri, 19 Dec 2025 13:07:08 +0530 Subject: [PATCH 4/4] ## Dev Board Ticket N/A ## Changes - Update package `osm-osw-reformatter` version from `0.3.0` to `0.3.1` ## Testing - TDEI Portal --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bc08b0b..4f0b448 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,5 @@ pydantic==1.10.4 python-ms-core==0.0.23 uvicorn==0.20.0 html_testRunner==1.2.1 -osm-osw-reformatter==0.3.0 +osm-osw-reformatter==0.3.1 numpy==1.26.4 \ No newline at end of file