diff --git a/CHANGELOG.md b/CHANGELOG.md index e9d8c5a..7072305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change log +### 0.2.11 + +- Fixed [BUG-2065](https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/2065/) +- Added functionality to catch serialization errors +- Added unit test cases for that +- Added test file `test_serialization_error.zip` to test the serialization error + ### 0.2.10 - Added limit the message error when u_id and v_id are missing diff --git a/src/python_osw_validation/__init__.py b/src/python_osw_validation/__init__.py index 46527e4..a88d156 100644 --- a/src/python_osw_validation/__init__.py +++ b/src/python_osw_validation/__init__.py @@ -161,12 +161,30 @@ def validate(self, max_errors=20) -> ValidationResult: # Validate OSW external extensions for file in validator.externalExtensions: file_path = os.path.join(file) + file_name = os.path.basename(file) extensionFile = gpd.read_file(file_path) invalid_geojson = extensionFile[extensionFile.is_valid == False] is_valid = len(invalid_geojson) == 0 if not is_valid: - self.errors.append( - f"Invalid geometries found in extension file {file}, list of invalid geometries: {invalid_geojson.to_json()}") + try: + # Safely extract invalid _id or fallback to index if _id is missing + invalid_ids = list(set(invalid_geojson.get('_id', invalid_geojson.index))) + num_invalid = len(invalid_ids) + limit = min(num_invalid, 20) + displayed_invalid = ', '.join(map(str, invalid_ids[:limit])) + self.errors.append( + f"Invalid geometries found in extension file `{file_name}`. Showing {limit if num_invalid > 20 else 'all'} of {num_invalid} invalid geometry IDs: {displayed_invalid}" + ) + except Exception as e: + self.errors.append(f"Invalid features found in `{file_name}`, but failed to extract IDs: {e}") + + # Optional: Test serializability of extension file + try: + for idx, row in extensionFile.drop(columns='geometry').iterrows(): + json.dumps(row.to_dict()) + except Exception as e: + self.errors.append(f"Extension file `{file_name}` has non-serializable properties: {e}") + break if self.errors: return ValidationResult(False, self.errors) diff --git a/src/python_osw_validation/version.py b/src/python_osw_validation/version.py index f0ca935..2418de5 100644 --- a/src/python_osw_validation/version.py +++ b/src/python_osw_validation/version.py @@ -1 +1 @@ -__version__ = '0.2.10' \ No newline at end of file +__version__ = '0.2.11' \ No newline at end of file diff --git a/tests/assets/test_serialization_error.zip b/tests/assets/test_serialization_error.zip new file mode 100644 index 0000000..20f7a27 Binary files /dev/null and b/tests/assets/test_serialization_error.zip differ diff --git a/tests/unit_tests/test_osw_validation.py b/tests/unit_tests/test_osw_validation.py index 1095fe0..53be383 100644 --- a/tests/unit_tests/test_osw_validation.py +++ b/tests/unit_tests/test_osw_validation.py @@ -30,6 +30,7 @@ def setUp(self): self.invalid_zones_file = os.path.join(ASSETS_PATH, 'UW.zones.invalid.zip') self.valid_osw_file = os.path.join(ASSETS_PATH, 'wa.bellevue.zip') self.invalid_v_id_file = os.path.join(ASSETS_PATH, '4151.zip') + self.serialization_file = os.path.join(ASSETS_PATH, 'test_serialization_error.zip') self.schema_file_path = SCHEMA_FILE_PATH self.invalid_schema_file_path = INVALID_SCHEMA_FILE_PATH @@ -227,6 +228,13 @@ def test_invalid_zones_file(self): self.assertFalse(result.is_valid) self.assertIsNotNone(result.errors) + def test_invalid_serialization_file(self): + validation = OSWValidation(zipfile_path=self.serialization_file) + result = validation.validate() + self.assertFalse(result.is_valid) + self.assertIsNotNone(result.errors) + error_message = next((err for err in result.errors if 'non-serializable' in err.lower()), None) + def test_unmatched_ids_limited_to_20(self): validation = OSWValidation(zipfile_path=self.invalid_v_id_file) result = validation.validate()