@@ -750,13 +750,63 @@ def _deduplicate_errors(normalized: List[Dict[str, Any]]) -> List[Dict[str, Any]
750750 return deduped
751751
752752
753+ def _normalize_validation_issues (error_list : List [Any ]) -> List [Dict [str , Any ]]:
754+ """Helper to process list of errors (ValidationIssue objects)."""
755+ normalized = []
756+ for err in error_list :
757+ if hasattr (err , 'message' ):
758+ row = getattr (err , 'row_number' , getattr (err , 'line' , None ))
759+ col = getattr (err , 'column_name' , getattr (err , 'column' , None ))
760+
761+ item = {
762+ "severity" : "error" ,
763+ "category" : "csv-content" ,
764+ "title" : toolkit ._ (f"Error in { err .file_name } " ) if hasattr (err , 'file_name' )
765+ else toolkit ._ ("Validation error" ),
766+ "details" : err .message ,
767+ "csv_file" : getattr (err , 'file_name' , None ),
768+ "location" : {"line" : row , "col" : col } if row else None ,
769+ "suggestion" : toolkit ._ ("Check the format of the uploaded file." ),
770+ "raw" : str (err )
771+ }
772+ normalized .append (item )
773+ else :
774+ # Fallback for simple strings inside a list
775+ parsed = _parse_schema_error_line (str (err ))
776+ normalized .append (_normalize_single_error (str (err ), parsed ))
777+ return normalized
778+
779+
780+ def _format_raw_errors_as_json (error_dict : Any ) -> str :
781+ """Helper to dump errors to a pretty JSON string."""
782+ def _json_default (obj ):
783+ """Converts complex objects (ValidationIssue, Enums) to dictionaries/strings."""
784+ if isinstance (obj , ValidationIssue ):
785+ return {
786+ "level" : getattr (obj .level , 'value' , str (obj .level )) if hasattr (obj , 'level' ) else None ,
787+ "code" : getattr (obj .code , 'value' , str (obj .code )) if hasattr (obj , 'code' ) else None ,
788+ "message" : obj .message ,
789+ "file_name" : getattr (obj , 'file_name' , None ),
790+ "row" : getattr (obj , 'row_number' , None ),
791+ "column" : getattr (obj , 'column_name' , None ),
792+ "value" : getattr (obj , 'value' , None )
793+ }
794+ if hasattr (obj , 'value' ):
795+ return obj .value
796+ return str (obj )
797+
798+ try :
799+ return json .dumps (error_dict , default = _json_default , indent = 4 , ensure_ascii = False )
800+ except Exception :
801+ return str (error_dict )
802+
803+
753804def normalize_iati_errors (error_dict : Any , package_id : Optional [str ] = None ) -> Dict [str , Any ]:
754805 """
755806 It normalizes converter errors (XSD / latest_errors) into a user-friendly structure.
756807
757808 It also supports pre-normalized structures (e.g., the output of validate_required_csv_folder()).
758809 """
759-
760810 if isinstance (error_dict , dict ) and "items" in error_dict and "raw" in error_dict :
761811 if "summary" not in error_dict or error_dict ["summary" ] is None :
762812 error_dict ["summary" ] = toolkit ._ (
@@ -767,54 +817,15 @@ def normalize_iati_errors(error_dict: Any, package_id: Optional[str] = None) ->
767817 normalized = []
768818
769819 if isinstance (error_dict , list ):
770- for err in error_dict :
771- if hasattr (err , 'message' ):
772- row = getattr (err , 'row_number' , getattr (err , 'line' , None ))
773- col = getattr (err , 'column_name' , getattr (err , 'column' , None ))
774-
775- item = {
776- "severity" : "error" ,
777- "category" : "csv-content" ,
778- "title" : toolkit ._ (f"Error in { err .file_name } " ) if hasattr (err , 'file_name' ) else toolkit ._ ("Validation error" ),
779- "details" : err .message ,
780- "csv_file" : getattr (err , 'file_name' , None ),
781- "location" : {"line" : row , "col" : col } if row else None ,
782- "suggestion" : toolkit ._ ("Check the format of the uploaded file." ),
783- "raw" : str (err )
784- }
785- normalized .append (item )
786- else :
787- parsed = _parse_schema_error_line (str (err ))
788- normalized .append (_normalize_single_error (str (err ), parsed ))
789-
820+ normalized = _normalize_validation_issues (error_dict )
790821 elif isinstance (error_dict , dict ):
791822 raw_lines = _flatten_error_dict (error_dict )
792823 for raw in raw_lines :
793824 parsed = _parse_schema_error_line (raw )
794825 normalized .append (_normalize_single_error (raw , parsed ))
795826
796827 deduped = _deduplicate_errors (normalized )
797-
798- def _json_default (obj ):
799- """Converts complex objects (ValidationIssue, Enums) to dictionaries/strings."""
800- if isinstance (obj , ValidationIssue ):
801- return {
802- "level" : getattr (obj .level , 'value' , str (obj .level )) if hasattr (obj , 'level' ) else None ,
803- "code" : getattr (obj .code , 'value' , str (obj .code )) if hasattr (obj , 'code' ) else None ,
804- "message" : obj .message ,
805- "file_name" : getattr (obj , 'file_name' , None ),
806- "row" : getattr (obj , 'row_number' , None ),
807- "column" : getattr (obj , 'column_name' , None ),
808- "value" : getattr (obj , 'value' , None )
809- }
810- if hasattr (obj , 'value' ):
811- return obj .value
812- return str (obj )
813-
814- try :
815- raw_formatted = json .dumps (error_dict , default = _json_default , indent = 4 , ensure_ascii = False )
816- except Exception :
817- raw_formatted = str (error_dict )
828+ raw_formatted = _format_raw_errors_as_json (error_dict )
818829
819830 return {
820831 "summary" : toolkit ._ ("Validation errors were found in the source CSV files." ) if deduped else None ,
0 commit comments