From c1f788c215e63ace5433c33d01a5b414e0fd8b18 Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Mon, 19 Jan 2026 12:43:49 -0500 Subject: [PATCH 1/9] Add finer details to execution status --- cdisc_rules_engine/enums/execution_status.py | 7 + cdisc_rules_engine/rules_engine.py | 6 +- .../services/reporting/sdtm_report_data.py | 145 +++++++++++------ .../services/reporting/usdm_report_data.py | 150 ++++++++++++------ cdisc_rules_engine/utilities/utils.py | 79 +++++++-- resources/templates/report-template.xlsx | Bin 18217 -> 20606 bytes resources/templates/usdm-report-template.xlsx | Bin 53830 -> 53983 bytes tests/conftest.py | 4 +- tests/unit/test_rules_engine.py | 74 ++++----- .../test_reporting/test_sdtm_report.py | 2 +- .../test_reporting/test_usdm_report.py | 2 +- tests/unit/test_usdm_data.py | 3 +- 12 files changed, 318 insertions(+), 154 deletions(-) diff --git a/cdisc_rules_engine/enums/execution_status.py b/cdisc_rules_engine/enums/execution_status.py index d1b6b58bb..96ab25540 100644 --- a/cdisc_rules_engine/enums/execution_status.py +++ b/cdisc_rules_engine/enums/execution_status.py @@ -5,3 +5,10 @@ class ExecutionStatus(BaseEnum): SUCCESS = "success" SKIPPED = "skipped" EXECUTION_ERROR = "execution_error" + ISSUE_REPORTED = "issue_reported" + UNKNOWN_STATUS = "unknown_status" + + +class ExecutionError(BaseEnum): + AN_UNKNOWN_EXCEPTION_HAS_OCCURRED = "An unknown exception has occurred" + COLUMN_NOT_FOUND_IN_DATA = "Column not found in data" diff --git a/cdisc_rules_engine/rules_engine.py b/cdisc_rules_engine/rules_engine.py index e67c56b12..9e8394de3 100644 --- a/cdisc_rules_engine/rules_engine.py +++ b/cdisc_rules_engine/rules_engine.py @@ -5,7 +5,7 @@ from business_rules.engine import run import os from cdisc_rules_engine.config import config as default_config -from cdisc_rules_engine.enums.execution_status import ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus from cdisc_rules_engine.enums.rule_types import RuleTypes from cdisc_rules_engine.exceptions.custom_exceptions import ( DatasetNotFoundError, @@ -486,7 +486,7 @@ def handle_validation_exceptions( # noqa elif isinstance(exception, (KeyError, ParserError)): error_obj = FailedValidationEntity( dataset=os.path.basename(dataset_path), - error="Column not found in data", + error=ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, message=exception.args[0], ) message = "rule execution error" @@ -630,7 +630,7 @@ def handle_validation_exceptions( # noqa else: error_obj = FailedValidationEntity( dataset=os.path.basename(dataset_path), - error="An unknown exception has occurred", + error=ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, message=str(exception), ) message = "rule execution error" diff --git a/cdisc_rules_engine/services/reporting/sdtm_report_data.py b/cdisc_rules_engine/services/reporting/sdtm_report_data.py index b9a258ab2..cf3596625 100644 --- a/cdisc_rules_engine/services/reporting/sdtm_report_data.py +++ b/cdisc_rules_engine/services/reporting/sdtm_report_data.py @@ -5,7 +5,7 @@ from typing import BinaryIO, Iterable from cdisc_rules_engine.constants.define_xml_constants import DEFINE_XML_FILE_NAME from cdisc_rules_engine.enums.default_file_paths import DefaultFilePaths -from cdisc_rules_engine.enums.execution_status import ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus from cdisc_rules_engine.models.dictionaries.dictionary_types import DictionaryTypes from cdisc_rules_engine.services.define_xml.define_xml_reader_factory import ( DefineXMLReaderFactory, @@ -211,39 +211,112 @@ def get_summary_data(self) -> list[dict]: """ summary_data = [] for validation_result in self._results: - if validation_result.execution_status == "success": - for result in validation_result.results or []: - dataset = result.get("dataset") - if ( - result.get("errors") - and result.get("executionStatus") == "success" - ): - summary_item = { - "dataset": dataset, - "core_id": validation_result.id, - "message": result.get("message"), - "issues": len(result.get("errors")), - } - summary_data.append(summary_item) + for result in validation_result.results or []: + dataset = result.get("dataset") + if ( + result.get("errors") + and result.get("executionStatus") + == ExecutionStatus.ISSUE_REPORTED.value + ): + summary_item = { + "dataset": dataset, + "core_id": validation_result.id, + "message": result.get("message"), + "issues": len(result.get("errors")), + } + summary_data.append(summary_item) return sorted( summary_data, key=lambda x: (x["dataset"], x["core_id"]), ) - def get_detailed_data(self, excel=False) -> list[dict]: + def get_detailed_data(self) -> list[dict]: detailed_data = [] for validation_result in self._results: detailed_data = detailed_data + self._generate_error_details( - validation_result, excel + validation_result ) return sorted( detailed_data, key=lambda x: (x["core_id"], x["dataset"]), ) + def _issue_details( + self, validation_result: RuleValidationResult, result: dict + ) -> list[dict]: + """ + Generates the Issue details data that goes into the excel export. + Each row is represented by a list or a dict containing the following + information: + return [ + "CORE-ID", + "Message", + "Executability", + "Dataset Name" + "USUBJID", + "Record", + "Sequence", + "Variable(s)", + "Value(s)" + ] + """ + errors = [] + variables = result.get("variables", []) + for error in [ + error + for error in result.get("errors") + if error.get("error") + not in [ + ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, + ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, + ] + ]: + values = [] + for variable in variables: + raw_value = error.get("value", {}).get(variable) + if raw_value is None: + values.append(None) + else: + values.append(str(raw_value)) + error_item = { + "core_id": validation_result.id, + "message": result.get("message"), + "executability": validation_result.executability, + "dataset": error.get("dataset"), + "USUBJID": error.get("USUBJID", ""), + "row": error.get("row", ""), + "SEQ": error.get("SEQ", ""), + "variables": variables, + "values": self.process_values(values), + } + errors.append(error_item) + return errors + + def _error_details(self, validation_result: RuleValidationResult, result: dict): + errors = [] + for error in [ + error + for error in result.get("errors") + if error.get("error") + == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value + ]: + error_item = { + "core_id": validation_result.id, + "message": (f"{result.get('message')} - {error.get('error')}"), + "executability": validation_result.executability, + "dataset": error.get("dataset"), + "USUBJID": "", + "row": "", + "SEQ": "", + "variables": "", + "values": error.get("message"), + } + errors.append(error_item) + return errors + def _generate_error_details( - self, validation_result: RuleValidationResult, excel + self, validation_result: RuleValidationResult ) -> list[dict]: """ Generates the Issue details data that goes into the excel export. @@ -263,28 +336,11 @@ def _generate_error_details( """ errors = [] for result in validation_result.results or []: - if result.get("errors", []) and result.get("executionStatus") == "success": - variables = result.get("variables", []) - for error in result.get("errors"): - values = [] - for variable in variables: - raw_value = error.get("value", {}).get(variable) - if raw_value is None: - values.append(None) - else: - values.append(str(raw_value)) - error_item = { - "core_id": validation_result.id, - "message": result.get("message"), - "executability": validation_result.executability, - "dataset": error.get("dataset"), - "USUBJID": error.get("USUBJID", ""), - "row": error.get("row", ""), - "SEQ": error.get("SEQ", ""), - "variables": variables, - "values": self.process_values(values), - } - errors.append(error_item) + errors = ( + errors + + self._issue_details(validation_result, result) + + self._error_details(validation_result, result) + ) return errors def get_rules_report_data(self) -> list[dict]: @@ -309,12 +365,9 @@ def get_rules_report_data(self) -> list[dict]: "cdisc_rule_id": validation_result.cdisc_rule_id, "fda_rule_id": validation_result.fda_rule_id, "message": validation_result.message, - "status": ( - ExecutionStatus.SUCCESS.value.upper() - if validation_result.execution_status - == ExecutionStatus.SUCCESS.value - else ExecutionStatus.SKIPPED.value.upper() - ), + "status": ExecutionStatus( + validation_result.execution_status + ).value.upper(), } rules_report.append(rules_item) return sorted( diff --git a/cdisc_rules_engine/services/reporting/usdm_report_data.py b/cdisc_rules_engine/services/reporting/usdm_report_data.py index 41cb5c21a..558d8d0cc 100644 --- a/cdisc_rules_engine/services/reporting/usdm_report_data.py +++ b/cdisc_rules_engine/services/reporting/usdm_report_data.py @@ -3,7 +3,7 @@ import os from cdisc_rules_engine.enums.default_file_paths import DefaultFilePaths -from cdisc_rules_engine.enums.execution_status import ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus from cdisc_rules_engine.services.reporting.base_report_data import ( BaseReportData, ) @@ -119,40 +119,115 @@ def get_summary_data(self) -> list[dict]: """ summary_data = [] for validation_result in self._results: - if validation_result.execution_status == "success": - for result in validation_result.results or []: - if ( - result.get("errors") - and result.get("executionStatus") == "success" - ): - summary_item = { - "entity": result.get("entity") - or (result.get("dataset", "") or "").replace(".json", ""), - "core_id": validation_result.id, - "cdisc_rule_id": validation_result.cdisc_rule_id, - "message": result.get("message"), - "issues": len(result.get("errors")), - } - summary_data.append(summary_item) + for result in validation_result.results or []: + if ( + result.get("errors") + and result.get("executionStatus") + == ExecutionStatus.ISSUE_REPORTED.value + ): + summary_item = { + "entity": result.get("entity") + or (result.get("dataset", "") or "").replace(".json", ""), + "core_id": validation_result.id, + "cdisc_rule_id": validation_result.cdisc_rule_id, + "message": result.get("message"), + "issues": len(result.get("errors")), + } + summary_data.append(summary_item) return sorted( summary_data, key=lambda x: (x.get("entity"), x["core_id"]), ) - def get_detailed_data(self, excel=False) -> list[dict]: + def get_detailed_data(self) -> list[dict]: detailed_data = [] for validation_result in self._results: detailed_data = detailed_data + self._generate_error_details( - validation_result, excel + validation_result ) return sorted( detailed_data, key=lambda x: (x["core_id"], x.get("entity")), ) + def _issue_details( + self, validation_result: RuleValidationResult, result: dict + ) -> list[dict]: + """ + Generates the Issue details data that goes into the excel export. + Each row is represented by a list or a dict containing the following + information: + return [ + "CORE-ID", + "Message", + "Executability", + "Dataset Name" + "USUBJID", + "Record", + "Sequence", + "Variable(s)", + "Value(s)" + ] + """ + errors = [] + variables = result.get("variables", []) + for error in [ + error + for error in result.get("errors") + if error.get("error") + not in [ + ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, + ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, + ] + ]: + values = [] + for variable in variables: + raw_value = error.get("value", {}).get(variable) + if raw_value is None: + values.append(None) + else: + values.append(str(raw_value)) + error_item = { + "core_id": validation_result.id, + "cdisc_rule_id": validation_result.cdisc_rule_id, + "message": result.get("message"), + "executability": validation_result.executability, + "entity": error.get("entity") + or error.get("dataset", "").replace(".json", ""), + "instance_id": error.get("instance_id"), + "path": error.get("path"), + "attributes": variables, + "values": self.process_values(values), + } + errors.append(error_item) + return errors + + def _error_details(self, validation_result: RuleValidationResult, result: dict): + errors = [] + for error in [ + error + for error in result.get("errors") + if error.get("error") + == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value + ]: + error_item = { + "core_id": validation_result.id, + "cdisc_rule_id": validation_result.cdisc_rule_id, + "message": (f"{result.get('message')} - {error.get('error')}"), + "executability": validation_result.executability, + "entity": error.get("entity") + or error.get("dataset", "").replace(".json", ""), + "instance_id": "", + "path": "", + "attributes": "", + "values": error.get("message"), + } + errors.append(error_item) + return errors + def _generate_error_details( - self, validation_result: RuleValidationResult, excel + self, validation_result: RuleValidationResult ) -> list[dict]: """ Generates the Issue details data that goes into the excel export. @@ -172,29 +247,11 @@ def _generate_error_details( """ errors = [] for result in validation_result.results or []: - if result.get("errors", []) and result.get("executionStatus") == "success": - variables = result.get("variables", []) - for error in result.get("errors"): - values = [] - for variable in variables: - raw_value = error.get("value", {}).get(variable) - if raw_value is None: - values.append(None) - else: - values.append(str(raw_value)) - error_item = { - "core_id": validation_result.id, - "cdisc_rule_id": validation_result.cdisc_rule_id, - "message": result.get("message"), - "executability": validation_result.executability, - "entity": error.get("entity") - or error.get("dataset", "").replace(".json", ""), - "instance_id": error.get("instance_id"), - "path": error.get("path"), - "attributes": variables, - "values": self.process_values(values), - } - errors.append(error_item) + errors = ( + errors + + self._issue_details(validation_result, result) + + self._error_details(validation_result, result) + ) return errors def get_rules_report_data(self) -> list[dict]: @@ -217,12 +274,9 @@ def get_rules_report_data(self) -> list[dict]: "version": "1", "cdisc_rule_id": validation_result.cdisc_rule_id, "message": validation_result.message, - "status": ( - ExecutionStatus.SUCCESS.value.upper() - if validation_result.execution_status - == ExecutionStatus.SUCCESS.value - else ExecutionStatus.SKIPPED.value.upper() - ), + "status": ExecutionStatus( + validation_result.execution_status + ).value.upper(), } rules_report.append(rules_item) diff --git a/cdisc_rules_engine/utilities/utils.py b/cdisc_rules_engine/utilities/utils.py index f3e001cee..766c6a3b9 100644 --- a/cdisc_rules_engine/utilities/utils.py +++ b/cdisc_rules_engine/utilities/utils.py @@ -25,7 +25,7 @@ SUPPLEMENTARY_DOMAINS, ) from cdisc_rules_engine.constants.classes import SPECIAL_PURPOSE, SPECIAL_PURPOSE_MODEL -from cdisc_rules_engine.enums.execution_status import ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus from cdisc_rules_engine.interfaces import ConditionInterface from cdisc_rules_engine.models.base_validation_entity import BaseValidationEntity from cdisc_rules_engine.check_operators.helpers import is_valid_date @@ -48,25 +48,74 @@ def convert_file_size(size_in_bytes: int, desired_unit: str) -> float: def get_execution_status(results): """ - If all results have skipped status, return skipped. - Else return success + If any result has an execution error, return execution error + Else if any result has an issue reported, return issue reported + Else if any result is successful, return issue successful + Else, result should have all skips, return issue skipped """ if len(results) == 0: return ExecutionStatus.SUCCESS.value - if isinstance(results[0], BaseValidationEntity): - successful_results = [ - entity for entity in results if entity.status == ExecutionStatus.SUCCESS - ] - else: - successful_results = [ - result - for result in results - if result.get("executionStatus") == ExecutionStatus.SUCCESS.value - ] - if successful_results: + status = ( + { + ExecutionStatus.SUCCESS: [], + ExecutionStatus.EXECUTION_ERROR: [], + ExecutionStatus.ISSUE_REPORTED: results, + ExecutionStatus.SKIPPED: [], + } + if isinstance(results[0], BaseValidationEntity) + else { + ExecutionStatus.SUCCESS: [ + result + for result in results + if result.get("executionStatus") == ExecutionStatus.SUCCESS.value + ], + ExecutionStatus.EXECUTION_ERROR: [ + result + for result in results + if result.get("executionStatus") + == ExecutionStatus.EXECUTION_ERROR.value + and [ + error + for error in result.get("errors", []) + if error.get("error") + == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value + ] + ], + ExecutionStatus.ISSUE_REPORTED: [ + result + for result in results + if result.get("executionStatus") == ExecutionStatus.ISSUE_REPORTED.value + ], + ExecutionStatus.SKIPPED: [ + result + for result in results + if result.get("executionStatus") == ExecutionStatus.SKIPPED.value + or [ + error + for error in result.get("errors", []) + if error.get("error") + == ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value + ] + ], + } + ) + if len(results) != ( + len(status[ExecutionStatus.SUCCESS]) + + len(status[ExecutionStatus.EXECUTION_ERROR]) + + len(status[ExecutionStatus.ISSUE_REPORTED]) + + len(status[ExecutionStatus.SKIPPED]) + ): + return ExecutionStatus.UNKNOWN_STATUS.value + elif status[ExecutionStatus.EXECUTION_ERROR]: + return ExecutionStatus.EXECUTION_ERROR.value + elif status[ExecutionStatus.ISSUE_REPORTED]: + return ExecutionStatus.ISSUE_REPORTED.value + elif status[ExecutionStatus.SUCCESS]: return ExecutionStatus.SUCCESS.value - else: + elif status[ExecutionStatus.SKIPPED]: return ExecutionStatus.SKIPPED.value + else: + return ExecutionStatus.UNKNOWN_STATUS.value def get_standard_codelist_cache_key(standard: str, version: str) -> str: diff --git a/resources/templates/report-template.xlsx b/resources/templates/report-template.xlsx index 2e805f055156cc9742847f3be01de12304c35c50..759a54a1c4af7c0369fab828a6018d2d3584d5bc 100644 GIT binary patch delta 14251 zcmaKTWmsHE(C*;w?(Xg+xCRUEF2RDkGr0Sp!5xBypn+h)gF}M51PBZkg3BG)e7oQ7 zz0aK=)2F+ttKO>W(^GR!b!7t7Lje?_sv-<5E&v{Y2mk=60JMjyT_I2a04+j2AvH8m zYuND%CuWDvPci9E?LKh=Excf<42xLUdjJkbA9X$3g!sm>5SOYJUM!~EP6}iFE*sA7 z>++V0uW!(y;v2GTN?#`jjpg!trbJI{7;LH0%6)yS^%5mZ7dpoH^5#8{p>KCs6`NHN z<(Z1Qxv_XDrw2J}U7S$`?x{tlT-{kE@LiC-KsE-qS9-U4=v&k&@aJybF$D!R zH*c!t@^o(BzE@&ARs^w_7rHwBrbvk(NqmjIxYH0*sBsRJIshH?p8IzXrv)JZi$sd5 z#TB0MRB*bfDwU;}lQnLTq`5P+(0Bp-*66Y)pN0@k-UQB92hT4fRz2_&C)o>u*TBnA z(xrFvMUCnMj_$`BiP>rc1_z^(Z-H1>a%5nXDHlJgmq+Z@tg?g zw9_A9g)g0mBrv!Ln1jeM%a^tfp#v?BXL^#CFve-heS@~zKSWQqwQUd~LK9sfO*xpI z_YTo)Y&T1Zxbg0>Epv~z9tl9=0SZUOZfJ#}g8_=(Yw;Vx005JaFkwI_-u4_`POf(5 zPEL00-VXNF+73>Oa+nW+)AtBoc$N92em-1%F_nm6dcRCJm;V^ ze|LG!4)g`*q$=74gMV{Rj0*o6%^bm45_Th(uw|E#-ut9NidnDJs{3mDK#a#rj?^I& zD5pygC;bNDoj>9I_27=|`^eH%40LEjvgB`>)aSxv?oM64Jpjl@ELEx`;t(K z?#QNRqX$=k>pO8M>NH_L`3PN`Ho2564S!H7p%qbD!1vBEakz)357q^bWcf0p2&DT2 zV=CskG*pR4g9bPJ*-FeuP-j-BI~%mFfJi5b0m2@{Dy<%y%)9j|`n97kB>g-l8LlV)2G9e&8rpOVut-!yNXb(SO7=h>T;`5e{wF zZ$oE^nP6>6`x*t}a;U{|q>LT{WVphpVBGYnS-NPQp?V1s+}Emno{|_BKqk<=1-e*? zBUzbJhf7p~r5?5PwB|vYMNzpNCr-n7rpEhbp4x3^GOsKpWxTQUTkR}XVR}IURu_d4okiL zd2(=T@s1M(k6-^Y*Vmrg>vN~-K{lXY-_HX0lz?C3$$^U<1og=K_m>+UoaEN(KJwN+FlmD1>oBAdB7>yBS)N~ zMwLEr+_$ zm+i19=FmQx7Yx@d+Zk$h2WfTzQ*5P_pji&%`V!zvDw`HT9$jP7fq`OM%_|N!inQIz zahaAa7q6RkS$E{2FW}^5j}vu$ACK()WqTVIh&s;o$yX22ZSC5t7FKY+3uZsETkFni zSl_DikJkb-c&Z(i^xEG&j+tj`R8!&8cd8|H%r9#8F4;ZA#RWnG0FRFi0?4M&NO759 zd{tNgfCE&3$^--p=>}rJOD@5NYgWCt38-pz^z$60(>^&x5itN8PW;sJ1SF~e(N5@2G8dg)fpm{zx@|L@}55e(mZFO)AdH2li&aO&qu7|-I zG;9d$7RzG9WLlihRLBp}dNM*%+v*wN^{OS#wHNEZPm+P!tSU1GWuJK6X;Y7gBOHjC z)~8~OLy>6uf;XY=Z?!!iAP z2#d83%rD&isOHyr(%JnMQG~d*aH9ix?eK_Y=rxJa2lZs!Rzj}VmI2lY;(Uc_}zw-Nt?jZB4omO#orkzA#cD+|YmX@yQvgfBsF})v6JnHgbaPZ}d zpy&vwXej2K|5Xe|B;oP(qqqHs3k*;pIzEsHTW*_@?`zSVI4++NZ>9OW=9XjBX6U&1 zK6B5Z>$^vyErPfXcO#(VO&R~?l8UPsa}D&2UC}8wcRXviY9r3x)(lZ6}UQFk9t7p zV{9e~(N}y21^HxF(UhrjqO4qr4EU1K+r8LSc~O3h+*)U5(Qr$83{wm4mo2B-LWSDf zz4~BX76!fV?{VMCh*oP6_`;j2S=bf5831)K{@AG zUBGukV>J>Yk^Lyc)sIQtBsa^LFbDQc0`@?2no#~txF`rT%``Oc{811_NkX}g6IG@T`x-EVPgn*drb}bQo@O=e zL6bZv!LnU;G3r^)b5}Kk5_)vHzFqK@zPO6w8h>9*7adGubDmg5m>V4Vb6#8Z?)QO( zL0#Y}$;@7YIt8uj$J1!-biD$>Il-S4#nDvc38J5W?xLFr~sTxF>62 z0RK+9!iMp4@FA2g0sw&UpC5NnEh;KV6blPTy$0f0Fp}17GAgY7hVpHz$%Wpu1|u2l zZ1dggcper%OtUb)8|L&X8#ur-Gnk7z1s}Jg|D85g13TEfZR4#Lh+=KG*biIpm6lYv zwkk+O_e{|5(Xs9OkN#RH!!TG&yN{l=*|gD?c1hm`+F-(kYWP!L-q~qKgHb2xNmHnS z!!wNvv^z1Rh&XO2QY~!OCnb6u5}#oDf(p=oFYz58Cetsl3CfQtuEl4jlToJ|I=5h` zV$1XU7&AHSshh60SC_CZ65VZIu_c&&xvi2*;VG_hG>Y5aF1UVqAGu9ccqrXA{z$K+ zO%Kn%Y_iV649qUE_c!0E`>+}R;V0}b;0`w^Pk`?lgg$}j@FT-L)hh6X-$Tj*@A>eT z(XuwL5Ek9cp$lvd`$w#>)md7)Vf%ME%fty}8&#M@Hg;!J*`vRv+hG&)Nt-VYy|!+&eI;G%hF*F5@!PoV{$+2NAkhCi z@jZ!V6cY3#LBIEp@L%$ciT+CKhO5GHsAWsyihdiNKf%`PRZVl619a)rmzyOEV!@Vz zuoWWg{;FkgWl?stpD3&j&Du~rM@U-G2dmyIRrw#%9S4}uB>cw5x#nv{Er5a6Ku2tZ zDXJJxWbbLaR;_+CJ-LB_2L}pJy!0GISIaq5t7pS(?mdYMQ$uB4JBkD}(?X&auJ?F&yANqUeF*tlqZhn@nINp5xA+^Bc zFd}Lduu{vva^N}h8%Qs@DXo8l)yC<%`|9e8oHxxU&W{%c=e!@^X1guA1IhQ#rgA2% zHzpW>14}K%Q}-J{AD!_7u~!<}!p$SienQcRxWP1Xuzc%htD_SJ+O40l7@=P!A-?V2 z87sH#0v^9u`}k!iUK~p>L|PSw-6@ds#}76Z!_~iM{jm0RT>Uo-{qDb6YIk@4*AyT` z)5O0uy`K98qatKzjtOms38ls&>bdv*@~*a2LosU;K!pj9h1s-ep;OTg8-O55iAqiR zwtuj8(iiD1MwmFEwD`A}M5Dqx>_!U2V9uzZ3OtH-Nd$UEiN9rqGb>GoQ^J0sgf7zd zKSYiPW)@&eAcjf6O4nS?Z=M@LiGP+sBvxb@4$^SK_e12sO zR!lDt1~iJ7i@acX@#qZMs4B@pg{VZJIQ-|m2__7e`pbJTzDKEX0eM|K4-^Uo^^#tJ z)>?;!lrD1#ez4>~G{f2*PhOVDVQExek8Z7&%tt{wEXFds+F88o3lFZ$q@kDZ-!eCb z#E^EUL|z~0H01Idv_m;#VW-v%4SZ@=!XDwLtc>?SLG~hcp!K-2nT>Wpn|$d{D+AUwc!t9fHybJ{Ajms}L+>G%ZX1 ztO7|JcIKt%2vh6@d>z%ptANat2j-ORQJX3f0-6^u-gYVj-znjI9WOb!bNcee$9z5O z7RFx!?HH6AKT*AbV(-pSE!gW1{G_yS+D6?qJ>wL_|EtA4$($ohtLrj*)Vfe5-AZ&O z7ss$?TDlqJZ5ruCd0tD+B>$sf`%v5M5YZ%jNL(YiMeo|o&^u%OG`Dx$xSD3Ii>0O| za0|(!l<;FIa#3#vemlTASX#Lyzrf{K0uk0+hSBYgD1A>mIn7g9v5SHp*tbzNu$NyT zse~ES$vD9C=qeg}k$elwzQguq$h3`*w-M*4VKo4@bJhE`PmTps07v@o4J_7PDsk}X z5Q3$?X^eLw?PYJAh|*=xkvEvX^$Tvb&{d%9C~~zy4!31)|C_*^W#tbv@LfoRjUraq zyFTgV{m%Zww+Jv3Vw{tCKtdUpNL*YpP8&ZaTVe9u{%thoPh1q|sF;l%=`J~?GgFU5 z@)alldoRjbahwsy3^(aNgIPaJWRg~<&FS{jrNOBLyw~?10G*Ynd=8svKhOUwamP3C&b3omIXe04!zYj0`KExSd!sW4XH%%|-nUnoEP{94(59tL+UzfX zAe+_$fV%r`AJfxVg8HFNJ4`0N;&q!teITE0aE1Gz3#i68(E9|f%LEAmmXxcBsfQ-1Xq}5fSzwg`dE>z+=07p%#|N34WD> ziKK#QU4YGC_ywmAFZ&7%7P(rh%bP;Vg|Nv08Gj3!;g&P?|d|I zm+-QiSy;lbUO_}zx4!|_rJwn?pbzs@0$rzWDLF&6WR2HayLp;#&WD&ebcVmjT3ECz z2a&40!BLxN)skf1R5sIrp(S-|lXZr?e+d}G`JnK}hP2gg=5N7*W6T{-B3#~S`97oj zHrN2Ky}tva|A)|mqrLw%kpKt)xsl`{dog3GsG$G=AsWw{9xo{#kO~R8vQ2A>87$MZ z+k#FStreH$@b1m`&f9Txs{NN*)r_Z5qK`*M$2_rBB6Yb4Nios%48aFD2=63Qs~5Jn z4>G>LktHp))tJ`jO#;C%x;FXWTGiv689P8VhK9-Rys7fY_Q_*z?yhu|K|veWlyrT6 zs^%#CU7?`VsLzh$9C-h2E0u3sZfKh@qJmkwJD%|+dc3G4?Ke$}hqoj89!00-s0kWA zYfT`@6MQT?ZJvM+s+%X&?JK;J(;o32$Dx_{K0T^%>@?5hLo&BO z#JzeYm2i35soBy*K7FgzsQvf*`#Omgtr-Vri&VWlvmLqNKQgtQkv=eKqt{G6d; zr!+i;y$Ke0J-~FW+yqnnil4#u_R6Z?X}ru4{82A{K|x6n-7{NLqnE z{dT3B;e_h>b90po?KA%7s?xH#x#)nsEzJ0aTdYG+WU!!qBSqo#abaz8RLZ1e1464* zOHOt8Y)H0`pEQuS^L@z`TzotdUjc`eo8g(j7vby*Lg{;9deRqjW)~J7I9I2vS!F6r z^bHJDk&CY%7|7UT4HrO>@6%MLYG?ENj$l5F&1&Mea~(z5?pF&h2Qi`w7=c~Fe*i|bD@X>I}ij;GubV84xFPhNy=n}qQ( zcKi$n21j(fv9(dTrgbDmsLzkcoRUMfvS+fHNTrRQ$znPPKM}MeC=y{qU0Gnwh4b%@v*A6-jxQy^Eou!f{M)Oz!B^>Hl#5b7V=zz)ZEK!?Iu`O(F@ zVU4|(9OxH#eRq04!y%U4+Imy$Ku+Fuy+7dvRGCOr$yW{?5jf}vk8Sykwd{&CgGR*}ssTXTsO5aF2SW zy2l(l|J@J#Pv7i+`t*n=+YCwL0a-aZ-5@V2VIR!(;U(9Hh9z`wU!5<%fUn@tP=Olk z8+r%lsN!yP#Ctqt1yE3`Bv{|mY$!SL&=3h15sPZ2VW5g_#Dm6Um)+@z5x1pbpw;;$ zOIfDGgXoAcyX&#YjH2P8DW=4?%_wfxfZs`=rXaH^vCyPpjo$_l&Lf7XNx)!*W)M@N z^Fy0yc6T$UoJhhn!>DKjcWayoHn6WyLv8Tny(^)p3ms%&@Q`$p!!n518|Wz5S+HxV zkvRCoX&Y-Y#bv%|(QcH&M};-xp=r_@%3wpJ%h?!A{g;3wXCrjtAZIO=FTWZAja?fR zRx6FJx!aQ#6xIxbLy3KZ%J2a{Y0`7u2uPMZX^jm7;fW5b4O7I#yE1sDm_kcS=F4Ma zCOMA@%=%j!B|EcG7J|EB_pBDJQ($3f4RMf+qU(ZV!t9`s91!R+TdniMSFV*(f`Y>A zUgET2<5Z0QA&Fc65%k(xQpX(n`^u@Txa3lBJ|h-VKm-nO9ub0dQd|(~f}n7M79N&% zc`|D<%^9f(nsN1s#0hc91T7MxTCbNQmb#tV50Sx)&>bCCp~ zu@}*mg2ux{L9~>V8xiASwSBPfc{ZN)Awk4i3kKIbc>uK)8#@Zw*_4H{5C|5B`5#La zY2E4QRaeNI;Ak7kG64|BJXyMo4Y3qSlXe;T8O?)o=}R2PCk~ruO@`1=NG z5NLSM(A??IvuL6IjMGDqc|9SM>rOMJWETID`vd_E6Jk9_k23Jd7w~&W^Upyzf?T9| zBfW3mdS=2Y;Q3>Fak~Y?cX)pZQVNpW8O6!#kZD_s8}K%hSR5JUQD)!-pv{1x0kmXrUnocwRg1%r$K32o8|BV@Jy z9oZAP=g1~Mk9NcU50qrNJq4gr0riI3GjvEq=;g)5ztBMZ@pP*_%X_+6o>%oN>OvMS z$a;VEyxyN2sa(JY$9SmN{e?ChQ~ZmG7CW4vee)Ym3Gtk=kF>CI_KN14v=Z2OcuVR* z{he>*Lb?TCTgoF&&ZqYc$cNA}ziciABnAM7lVG7--+fKNr2NPhoeYAN*G#ongrdJq zp+bV*;Z38X61OzKW??`|sjyi{hc>*Rlfs4_wPluv{>pe?lxy7Ap?_u4?!abSOzV*f z+$&~`%HmVc%&adBnQy)s`quba5Q*MOT`_vvR8nHJBr8QzqlhQa%>I+9i6hu(Xh=pTeM6nq#N$CYfURj38mvIacuvA zxEg$aFGdB{F(DSykj_+K7Q|V&ednvlnd>}O_ARLLj8Xe5c$`@1sWa`x!WPWPZ1E&) zAlyVR%koma`#0>>t>33FCF>DNHe4CUGnegc>xa0F5`GFDlrgrP(+x%ITDjm@c9rU# zzB$@ik5F8T%_u%><&_yO(Se{!Iq-)JQeDfjFDqYQf7^ z`*l_>YyfEG5?x-i>nFrMMH6n?o<3GTjxlED22jD;#d(Jm_V+r!cSU#88L09nL=A^s zyjAZn4TJYpb~Y%R@#r!K*fQl~fZbool{9e8Zm>U-)?aPTv$u1ODKNWnAhej> z82g*02V|Bp_Q{?@0_s1&D#SI0%+>%}pWy0XgsF=@b5oMxDs$nnO6iuCSE{OqC=eUm z_En0VKEc;oPIHqg>z5ooKcX}@e!PD~xY%JAuSWo6CN zYR*6}nPF14;$^uDrW!P#PI!gRi~9!$u1r;P)+!Y3N@CYG7^%A5P zeqS0^k?tJBmv%DAGh%8$w8;U-D0sEa2+#pX?!=v_&*ZPHw+?h~y(jw`u?g(GP|_B+ zd5bpu@yVBwgN43Siu0zv=!kAeJas?x|2%>LMdHTIpv{+G*ho?E-qn{xW7G3oz4Fzr zPhy&^u5|BP%Jw?6BNSn>bD-rU+S|TXJe89m#}`J-c4_XaKr2>J<|f~WMFJ?i0*>^# zaCe_XM6ctI=Ged54@{*UBNpoVjY#Q0RW3Ivo6LS$XQ}Oi=FYy8!%fX|hnh*@Vx<+% zP;_=Iw=xc*!MZg@GxAC6KRnCiW2-D;WyFNE!<1riVo)!94Y?BmS(578k>`WP>;v zk4m(&+cZ;0G&7X)o3sj%yELaYiPG(fak31B1bn#{n2jQD2p{w9xAKBKRpde~4tC*C z@0lMU@3#NE1^`y)fE`J+aAlsJ5b(PF@M9Lh#zd~e3XeJ~MvIXOZn6Hc%pZ5UN@+D) zCp-0h`Y1l5h0JNk{o&;5%GK-973k+1cyoVpcSJbYdp&onvU^?C(tCQcVfkS0&*MJW z+v~KOQJyF!`f%nJ_{;l>X7M(%MeN?^(mT-mj+df(705fdG}9Y$ZM8w`@bKXCaJ0Mh z)BV@=h5N6QoxYYG=>Xx+c~K)cYexAM94gC3`8Mn-{=nSg4&l>>k?8CEIm(rtWQwoJ zKfHN%^N;s`fBbQBF!JuP7d+?Z`*?nL8u;n+V{19w>CwDVVfOtVT;ZhKKu0A(nB)Wo zsN<;%ybnANQQf} zWtm974s8Y5t48m+iI}yMYWZU127GRF3x6+8);Q1Q@tN9iZ%biuHhM*`lBKD zU=p2^7h<=m&F;&_C;AhDPw3!3${?(M&&ovpDDxs)g!xMu*eLSik21fx!YBHZu2l~k zj3-_GfM;F62=rPhgg?qc4<@1hQnq*IllQ;~%jy0hzX9+!ix2`TnPFrCM5yZUE65x< zLDRjxm+r8Cp?cYdP6qXt3lhdBvHl49Lst-mf}r{WCB-mTi1P=^u*V)O5P~I(l+)dn z?Bx>!#7DV?k;;EyGj!~XgtWj8J zFgoUK2ku`7+VHG)z$fIOfR&wUIta-el#h)OUMWX-mbc7O_ZElA*(L9*Nz&#KQiK~( zz;TH&W9H`VEgEv!3QskCvY7TRvU3|QIhCIKkuJX#T|34H8o)8KYmrNqD(X-{_Ke5v zBdrwQ-r3Hyaq?}Ki^;(O542y_OT?uhOPipZ)vV9~;kcBZWPX@+1ceYX;6*8`=-OS% zOQ5DAOP4bkQ!T}-W73pDC+|mRsn6%CZx7iVgF=j$;kdMzF#}#m&n-5oW4e_>Pwhw7 zHpP}w(ksG2Y(KUGzh#PAP!AqD?{j84bY*h&Y}8?CH$pc5tVwy}8dckLTS^J9^u!7@ zvJ+5}h|7G^);4>T8A8bYgu%ULg&U5`iWxI2FOw&Hg-0eK`9hRwUPc2GR0chjA?03U z#0SS^_v{V*c6(&=MNP_=B2!JCE4Y21E5+aVny=$}Zs5L9S*%K15rpG%{ew1F15>K} zFSG{bLxs~vK#H1sGFRm^&n@tAQgn~Te80xFp{JB*kQkUPVmmY%{%PB*Zm~72KLB|I<_TwlYc>+A$WA-Kk>TgRP!OFe`(ad){N$Ar zzE2JrxLQrGyK8xkt6;${RLJ?2!(N=zQP{(5YL=v>LM4sGB@kOJ4Xz5q`g?_z$19Aj z{jixSVOw5KVJ*xPHXE|J9Lqsr`t{U?ugn&BdCYxe#e{I6m2lqal|~lNLG(}vGC9@dxCH)2v>cO( z%-+OctAMn2A*Tu#PL8zAE97(AvjdJTNid<0E>00EW=||)P1<6dJ+NJFmUgf|cODbn zgqzB(x`0W?vpyNNJUlOzXUQ!mIM?E<8l71xLk8k-{ay3QId-zsha$zV6^lfALtjBt zyLbfH97N)5MidO%`{*UsX&=?heNFa;ZK-9bxIGYD9g;11sW>(wJZ-j0(qL2SK-=)m zTV&N8sX7X04)S>w{6LC#aXdX*W_0EwT46T;O%2YQfqpOY0~ON^-*0aW?>^*lhLRI3 zfLy3ElhUDQpxnwLw{kRTeq>ISmiX#MxPA51YAF|rYdUaw0e9q4UgM%^<5;@eDz{5| zURjb!24{@In+W|uec)mgjaPn>izqafiP{Ghttt?>><~na-VQ9(e^2CPb*`|PdjOZA z!U$VjDO>-Jq<82$x1@`(2f?`i+dlUs+b$`Di-3xh7JpsdeaG7EjF-F zMeaWEgQTc01W?eQ=LkGJ*IYR@SoJ|Ca^|!D2I^%QS(Nl_0 z#fjMXK773xJs{My!e9I0QY#vIx;{CDRqO+2QftD}Toqvjd4ZI&f_Gp-H^219#+!&53U>h-)iZk@^040q2+)rK#pdAASHsuc4|eKMf_Fkh z0}_Gt6S%BKgVK9dtE$x&!#nBRGS|t=&~M5tvunbVw8Yz)tlvu_Ii*z`l$3G%7;}hI zduD!~kPChZi#z#Pve$B+SasjDPy-`aW)69Bka?uO3RDm)_)MYRn ziPWz&nfZ9S!wY=3Gmhs?{qHIikT^0z!@3X#G*HNaTd@a1l!R0O(?HrkRimX_{r z944-o_HG={9loccQ}(BRSYl7i7fxK6OPB{KZ$Hp%II5I}0ClU@E{yrjBK%TD(k1S_ zUjwOS_s)*<#14PIeTZ3_aM4<~+$Y+VxdfciD&?+JSY`xfdGkSdcze=NvH`*03l$C6A|zklm+W`fYjq1<%Lx*ebs z`cgz)@8K{ZfI`@@xNBVQY$8nlMUts#k=%v(9Ss6wKH zD=KdMnx$#PGqQ-i0+tZ5M*kbyx@ykZ2UsLcwGzR3bao7b=AM$DAx2frj|D)=5q4@m zu?Uk%t+|!PH80??p>Q6N7-fm=Zk`aaFwf}Ls?+#Ep4)L2T&1GcyN@h%_VHgoRu93> z5@n@Lyixf0k#1?8B2DI?^rtUtSSO09DxY1qiNbhE=a8rlc`ZpgM~_?{owF>@d>Q|Ko< z;Wp8rf=$$;(d)5(-P=>2JR+c2=RbnjxaRg4lVeP!Iwpv^0P&6l zF()@lIA^T(W!61s{>1(7-j)2planzL>NTK)9ajQ)Y2F{>0Hs>Rto>UFm0z;L5%Fya zy<1ApbP^fR0*g!zY6;`$iKm-|Po<-O-lo~jcg5&iouJNEM|I#I zIUESEAOQnxjU~-h3WD;;YBD& z0xj8keUa zwmHm06pH?aR~skMqq~?Zh0zKaHimvl*Z3)<1MTjf{d9;sLED+%Mb7T}WWqCX|D&J- zRB6$vSP4G8&#Amqj@%`jb5#@Ra^6n;c@egg9#A(SDQ~fPF=si_-Ip097_A|wgW0|{ zU`yxg=3aeaF}7ez9;SM(4KKi>42Ukm98c8+!mh{?F9|fK zsRJ{p*KwfL^rn9#!^nZ~j`=jj^gbe9OVmg|j^S^|#DAV*(24{%VD){g{yv{Xx2~td zRz^*)g=ol@Se^f>ElmkGxRGk|^_mq-e4&WM?LhmMCbvrla!H?CRwVerp&*!u&r~dV zOE;mO-bZ_8P-VsC{TB<5H|9*zbQmb5PzS)`=(PKN+U@B>j1GmI={*c^d2a?ZN8ClK z7Q<~!cY*LneCf?pbzG=5%)LRH7jfE-iR$QnDyep<26y5G6p2Y<^O2S7PKx}mr3YJD z=V*qI4qgYu`Jg2RN%o&xlCyxk!^K+y4GbckW49Pt=r80Gg)9n$_V(51N;=L#bQnOT zY+d!hQnVf{rzkfl zq(@HxkrAyAz}fo^sAlxuYrGs5Ig_V4@FG0{fO`-^m00BTfvyH7b1e{@fHq;)!1UdDc4rc zIwZF~z8&&OGkof<-P^X$T@H(87ge9amX%n#YXGBiT7h`4gzTVp4$ZCbtv_-Jpz@#_ zIcWcQ#Axp79zr=_&B!oa8M3Kr@lObR(Wb}u>!aYD(_CCAE%>Uo!SHrJ(M{0hj^>pn zPb?{qCfB9r>WLn?AA{Am3YFGy;4d4mP3hs@>jsa0B-oxzeJl&zC>`Fxg4mpJH}LI{ z-v+7T#Ji&Q$y1;Q(CVfIfHS-l?Wvi&_6{@rI!*neC_oe2HG*(WLsqk7~A?stfEr&)RpYI zqaAB1U=h4F(pCdWLcL$?2IlxtG2{5|m*$roF zO9gwbPgZ~aJQQwL)O7O30A_Y>y-0jI0ZF4E{xZVsun!*BOWt{pvR1ze*oskVWYOD? zU;?9zlw}T=r+D;Y6~=-Ga|(;8KZrNKc^^vM)J8n`0(~ZIlcElxYB?XRgWL7};;~ZZ zKB3oW`X(j%QB4(s#hizH#&0gkz?il4#EJU~mzv9V?4YlP$n>nvYm3~2Thy$z8{-_k z2|9d3?MByt*`uNtI`qPl@(B}YP_x!zm=dy7XhLtqnCcepI`s><>Go;MXv=0|j2q+! zhQ8lEJeb_yx<|rzD4mMA5aSTfEX9!pU)>$5Cth7s9hS5^uOiD z|1gPRJu^Yc;F0`O`uAT+u^=uP7SeyJWd2L%B83Pc27Q;sB>m@Q!+)7t8J;!G$Z$Z5 zbAe!G>FEFYj|>2y{R0VdH3&Hx1<6d^+gwd|#$n;)3YwJVfF_d%LA@eI#{^&kL?KTIn3bNU{2yt`BS8QF delta 11895 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

$F5<_v}ycK3C1bSxNbM11Fk4D5R%zW%i0 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 delta 36993 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 wXw_0IqJ@gOZXRhVyacoW&-fiG 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 diff --git a/tests/conftest.py b/tests/conftest.py index 5c6e66f06..5563eb1a1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -984,7 +984,7 @@ def mock_validation_results() -> list[RuleValidationResult]: { "domain": "AE", "variables": ["AESTDY", "DOMAIN"], - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "errors": [ { "row": 1, @@ -1043,7 +1043,7 @@ def mock_validation_results() -> list[RuleValidationResult]: { "domain": "TT", "variables": ["TTVAR1", "TTVAR2"], - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "errors": [ { "row": 1, diff --git a/tests/unit/test_rules_engine.py b/tests/unit/test_rules_engine.py index c27468bfb..980c2b5a8 100644 --- a/tests/unit/test_rules_engine.py +++ b/tests/unit/test_rules_engine.py @@ -101,7 +101,7 @@ def test_validate_rule_invalid_suffix( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AESTDY"], @@ -149,7 +149,7 @@ def test_validate_rule_invalid_prefix( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AESTDY"], @@ -261,7 +261,7 @@ def test_validate_rule_cross_dataset_check( ).validate_single_dataset(dataset_rule_equal_to, datasets, datasets[0]) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "ec.xpt", "domain": "EC", "variables": ["ECSTDY"], @@ -366,7 +366,7 @@ def test_validate_one_to_one_rel_across_datasets(dataset_rule_one_to_one_related ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "ec.xpt", "domain": "EC", "variables": ["VISITNUM"], @@ -412,7 +412,7 @@ def test_validate_rule_single_dataset_check(dataset_rule_greater_than: dict): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "EC", "dataset": "bundle", "variables": ["ECCOOLVAR"], @@ -458,7 +458,7 @@ def test_validate_rule_equal_length(dataset_rule_has_equal_length: dict): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "EC", "dataset": "bundle", "variables": ["ECCOOLVAR"], @@ -508,7 +508,7 @@ def test_validate_is_contained_by_distinct(mock_rule_distinct_operation: dict): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "ae.xpt", "domain": "AE", "variables": ["AESTDY"], @@ -551,7 +551,7 @@ def test_validate_rule_not_equal_length(dataset_rule_has_not_equal_length: dict) ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "EC", "dataset": "bundle", "variables": ["ECCOOLVAR"], @@ -594,7 +594,7 @@ def test_validate_rule_multiple_conditions(dataset_rule_multiple_conditions: dic ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "EC", "dataset": "bundle", "variables": ["ECCOOLVAR"], @@ -636,7 +636,7 @@ def test_validate_record_rule_numbers_separated_by_dash_pattern(): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AESTDY"], @@ -676,7 +676,7 @@ def test_validate_record_rule_semi_colon_delimited_pattern(): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "AE", "dataset": "bundle", "variables": ["AESTDY"], @@ -718,7 +718,7 @@ def test_validate_record_rule_no_letters_numbers_underscores(): ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AESTDY"], @@ -774,7 +774,7 @@ def test_validate_dataset_metadata( "domain": "EC", "dataset": "bundle", "errors": [], - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "message": None, "variables": [], } @@ -825,7 +825,7 @@ def test_validate_dataset_metadata_wrong_metadata( { "domain": "EC", "dataset": "bundle", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["dataset_label", "dataset_name", "dataset_size"], "errors": [ { @@ -881,7 +881,7 @@ def test_validate_variable_metadata( "domain": "EC", "dataset": "bundle", "errors": [], - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "message": None, "variables": [], } @@ -904,7 +904,7 @@ def test_validate_variable_metadata( "domain": "EC", "dataset": "bundle", "errors": [], - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "message": None, "variables": [], } @@ -953,7 +953,7 @@ def test_validate_variable_metadata_wrong_metadata( "domain": "EC", "dataset": "bundle", "variables": ["variable_data_type", "variable_label", "variable_name"], - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "errors": [ { "dataset": "bundle", @@ -1022,7 +1022,7 @@ def test_rule_with_domain_prefix_replacement(mock_get_dataset: MagicMock): ).validate_single_dataset(rule, [dataset_metadata], dataset_metadata) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AESTDY"], @@ -1048,7 +1048,7 @@ def test_rule_with_domain_prefix_replacement(mock_get_dataset: MagicMock): ], [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "bundle", "domain": "AE", "variables": ["AE"], @@ -1066,7 +1066,7 @@ def test_rule_with_domain_prefix_replacement(mock_get_dataset: MagicMock): "domain": "AE", "dataset": "bundle", "errors": [], - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "message": None, "variables": [], } @@ -1150,7 +1150,7 @@ def test_validate_single_dataset(dataset_rule_equal_to_error_objects: dict): { "domain": "AE", "dataset": "bundle", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["AESTDY"], "errors": [ { @@ -1235,7 +1235,7 @@ def test_validate_single_dataset_not_equal_to( { "domain": "AE", "dataset": "data_bundle", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["AESTDY"], "errors": [ { @@ -1286,7 +1286,7 @@ def test_validate_single_dataset_not_equal_to( ), [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "ae.xpt", "domain": "AE", "variables": ["dataset_label", "dataset_location", "dataset_name"], @@ -1327,7 +1327,7 @@ def test_validate_single_dataset_not_equal_to( "domain": "AE", "dataset": "ae.xpt", "errors": [], - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "message": None, "variables": [], } @@ -1408,7 +1408,7 @@ def test_validate_dataset_metadata_against_define_xml( { "domain": "AE", "dataset": "test", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["variable_size"], "errors": [ {"dataset": "test", "row": 1, "value": {"variable_size": 30}} @@ -1451,7 +1451,7 @@ def test_validate_dataset_metadata_against_define_xml( { "domain": "AE", "dataset": "test", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["variable_size"], "errors": [ {"dataset": "test", "row": 1, "value": {"variable_size": 30}} @@ -1560,7 +1560,7 @@ def filter_func(row): { "domain": "AE", "dataset": "test", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": [ "AETERM", ], @@ -1612,7 +1612,7 @@ def filter_func(row): { "domain": "AE", "dataset": "ae_1.xpt, ae_2.xpt", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["AESTDY"], "errors": [ { @@ -1810,7 +1810,7 @@ def test_validate_split_dataset_metadata( { "domain": "EC", "dataset": "ec_1.xpt", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "errors": [ { "dataset": "ec_1.xpt", @@ -1886,7 +1886,7 @@ def test_validate_split_dataset_variables_metadata( { "domain": "EC", "dataset": "ec_2.xpt", - "executionStatus": ExecutionStatus.SUCCESS.value, + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "variables": ["variable_data_type", "variable_label", "variable_name"], "errors": [ { @@ -2008,7 +2008,7 @@ def test_validate_record_in_parent_domain( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "EC", "dataset": "ec.xpt", "variables": ["ECPRESP", "ECREASOC"], @@ -2073,7 +2073,7 @@ def test_validate_additional_columns( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "ts.xpt", "domain": "TS", "variables": ["TSVAL"], @@ -2183,7 +2183,7 @@ def test_validate_dataset_contents_against_define_and_library_variable_metadata( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "filename", "domain": "AE", "variables": [ @@ -2297,7 +2297,7 @@ def test_validate_single_dataset_operation_dataset_larger_than_target_dataset( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.SUCCESS.value, "dataset": "ie.xpt", "domain": "IE", "variables": [], @@ -2372,7 +2372,7 @@ def test_validate_extract_metadata_operation( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "suppec.xpt", "domain": "SUPPEC", "variables": [ @@ -2464,7 +2464,7 @@ def test_dataset_references_invalid_whodrug_terms( ) assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "domain": "AE", "dataset": "dataset_path", "variables": [ @@ -2680,7 +2680,7 @@ def mock_cached_method(*args, **kwargs): ) assert result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "dataset_path", "domain": "AE", "variables": [ diff --git a/tests/unit/test_services/test_reporting/test_sdtm_report.py b/tests/unit/test_services/test_reporting/test_sdtm_report.py index 95a9beddb..2191af35d 100644 --- a/tests/unit/test_services/test_reporting/test_sdtm_report.py +++ b/tests/unit/test_services/test_reporting/test_sdtm_report.py @@ -21,7 +21,7 @@ def test_get_rules_report_data(mock_validation_results): "cdisc_rule_id": result.cdisc_rule_id, "fda_rule_id": result.fda_rule_id, "message": result.message, - "status": ExecutionStatus.SUCCESS.value.upper(), + "status": ExecutionStatus.ISSUE_REPORTED.value.upper(), } ) expected_reports = sorted(expected_reports, key=lambda x: x["core_id"]) diff --git a/tests/unit/test_services/test_reporting/test_usdm_report.py b/tests/unit/test_services/test_reporting/test_usdm_report.py index 22a4642d2..ab804df7d 100644 --- a/tests/unit/test_services/test_reporting/test_usdm_report.py +++ b/tests/unit/test_services/test_reporting/test_usdm_report.py @@ -20,7 +20,7 @@ def test_get_rules_report_data(mock_validation_results): "version": "1", "cdisc_rule_id": result.cdisc_rule_id, "message": result.message, - "status": ExecutionStatus.SUCCESS.value.upper(), + "status": ExecutionStatus.ISSUE_REPORTED.value.upper(), } ) expected_reports = sorted(expected_reports, key=lambda x: x["core_id"]) diff --git a/tests/unit/test_usdm_data.py b/tests/unit/test_usdm_data.py index c2bdfa426..85d9eff80 100644 --- a/tests/unit/test_usdm_data.py +++ b/tests/unit/test_usdm_data.py @@ -1,5 +1,6 @@ from typing import List from unittest.mock import patch, MagicMock +from cdisc_rules_engine.enums.execution_status import ExecutionStatus from cdisc_rules_engine.models.sdtm_dataset_metadata import SDTMDatasetMetadata from cdisc_rules_engine.models.dataset.pandas_dataset import PandasDataset from cdisc_rules_engine.rules_engine import RulesEngine @@ -107,7 +108,7 @@ def test_validate_rule_single_dataset_check(dataset_rule_greater_than: dict): assert validation_result == [ { - "executionStatus": "success", + "executionStatus": ExecutionStatus.ISSUE_REPORTED.value, "dataset": "USDM_EliLilly_NCT03421379_Diabetes.json", "domain": "EC", "variables": ["ECCOOLVAR"], From 0f257d7fffbff182fa94622083547c93ec166cb7 Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Tue, 20 Jan 2026 15:03:49 -0500 Subject: [PATCH 2/9] reorg skips and execution errors --- cdisc_rules_engine/rules_engine.py | 11 +++++++++-- .../services/reporting/sdtm_report_data.py | 11 +++-------- .../services/reporting/usdm_report_data.py | 11 +++-------- .../utilities/dataset_preprocessor.py | 10 ++++++++-- cdisc_rules_engine/utilities/utils.py | 14 +------------- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/cdisc_rules_engine/rules_engine.py b/cdisc_rules_engine/rules_engine.py index 9e8394de3..322c4bc44 100644 --- a/cdisc_rules_engine/rules_engine.py +++ b/cdisc_rules_engine/rules_engine.py @@ -490,6 +490,13 @@ def handle_validation_exceptions( # noqa message=exception.args[0], ) message = "rule execution error" + errors = [error_obj] + return ValidationErrorContainer( + dataset=os.path.basename(dataset_path), + errors=errors, + message=message, + status=ExecutionStatus.SKIPPED.value, + ) elif isinstance(exception, DomainNotFoundInDefineXMLError): error_obj = FailedValidationEntity( dataset=os.path.basename(dataset_path), @@ -537,7 +544,7 @@ def handle_validation_exceptions( # noqa dataset=os.path.basename(dataset_path), errors=errors, message=message, - status=ExecutionStatus.SKIPPED.value, + status=ExecutionStatus.EXECUTION_ERROR.value, ) elif isinstance(exception, OperationError): @@ -552,7 +559,7 @@ def handle_validation_exceptions( # noqa dataset=os.path.basename(dataset_path), errors=errors, message=message, - status=ExecutionStatus.SKIPPED.value, + status=ExecutionStatus.EXECUTION_ERROR.value, ) elif isinstance(exception, DatasetBuilderError): error_obj = FailedValidationEntity( diff --git a/cdisc_rules_engine/services/reporting/sdtm_report_data.py b/cdisc_rules_engine/services/reporting/sdtm_report_data.py index cf3596625..7c2cf51ba 100644 --- a/cdisc_rules_engine/services/reporting/sdtm_report_data.py +++ b/cdisc_rules_engine/services/reporting/sdtm_report_data.py @@ -5,7 +5,7 @@ from typing import BinaryIO, Iterable from cdisc_rules_engine.constants.define_xml_constants import DEFINE_XML_FILE_NAME from cdisc_rules_engine.enums.default_file_paths import DefaultFilePaths -from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionStatus from cdisc_rules_engine.models.dictionaries.dictionary_types import DictionaryTypes from cdisc_rules_engine.services.define_xml.define_xml_reader_factory import ( DefineXMLReaderFactory, @@ -266,11 +266,7 @@ def _issue_details( for error in [ error for error in result.get("errors") - if error.get("error") - not in [ - ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, - ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, - ] + if result.get("executionStatus") == ExecutionStatus.ISSUE_REPORTED.value ]: values = [] for variable in variables: @@ -298,8 +294,7 @@ def _error_details(self, validation_result: RuleValidationResult, result: dict): for error in [ error for error in result.get("errors") - if error.get("error") - == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value + if result.get("executionStatus") == ExecutionStatus.EXECUTION_ERROR.value ]: error_item = { "core_id": validation_result.id, diff --git a/cdisc_rules_engine/services/reporting/usdm_report_data.py b/cdisc_rules_engine/services/reporting/usdm_report_data.py index 558d8d0cc..71a58a54b 100644 --- a/cdisc_rules_engine/services/reporting/usdm_report_data.py +++ b/cdisc_rules_engine/services/reporting/usdm_report_data.py @@ -3,7 +3,7 @@ import os from cdisc_rules_engine.enums.default_file_paths import DefaultFilePaths -from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionStatus from cdisc_rules_engine.services.reporting.base_report_data import ( BaseReportData, ) @@ -175,11 +175,7 @@ def _issue_details( for error in [ error for error in result.get("errors") - if error.get("error") - not in [ - ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, - ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, - ] + if result.get("executionStatus") == ExecutionStatus.ISSUE_REPORTED.value ]: values = [] for variable in variables: @@ -208,8 +204,7 @@ def _error_details(self, validation_result: RuleValidationResult, result: dict): for error in [ error for error in result.get("errors") - if error.get("error") - == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value + if result.get("executionStatus") == ExecutionStatus.EXECUTION_ERROR.value ]: error_item = { "core_id": validation_result.id, diff --git a/cdisc_rules_engine/utilities/dataset_preprocessor.py b/cdisc_rules_engine/utilities/dataset_preprocessor.py index 0eb2f8356..008135d86 100644 --- a/cdisc_rules_engine/utilities/dataset_preprocessor.py +++ b/cdisc_rules_engine/utilities/dataset_preprocessor.py @@ -16,7 +16,10 @@ get_sided_match_keys, get_dataset_name_from_details, ) -from cdisc_rules_engine.exceptions.custom_exceptions import PreprocessingError +from cdisc_rules_engine.exceptions.custom_exceptions import ( + DomainNotFoundError, + PreprocessingError, +) import os import pandas as pd @@ -110,7 +113,7 @@ def preprocess( # noqa ] if not file_infos: - raise PreprocessingError( + raise DomainNotFoundError( f"Failed to find related dataset for '{domain_name}' in preprocessor" ) @@ -577,6 +580,9 @@ def _merge_datasets( # noqa right_dataset_domain_details.get("join_type", "inner") ), ) + except KeyError as e: + # Handle with COLUMN_NOT_FOUND_IN_DATA in rules_engine + raise e except Exception as e: raise PreprocessingError( f"Failed to merge datasets. " diff --git a/cdisc_rules_engine/utilities/utils.py b/cdisc_rules_engine/utilities/utils.py index 766c6a3b9..9e0aca1a5 100644 --- a/cdisc_rules_engine/utilities/utils.py +++ b/cdisc_rules_engine/utilities/utils.py @@ -25,7 +25,7 @@ SUPPLEMENTARY_DOMAINS, ) from cdisc_rules_engine.constants.classes import SPECIAL_PURPOSE, SPECIAL_PURPOSE_MODEL -from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ExecutionStatus from cdisc_rules_engine.interfaces import ConditionInterface from cdisc_rules_engine.models.base_validation_entity import BaseValidationEntity from cdisc_rules_engine.check_operators.helpers import is_valid_date @@ -74,12 +74,6 @@ def get_execution_status(results): for result in results if result.get("executionStatus") == ExecutionStatus.EXECUTION_ERROR.value - and [ - error - for error in result.get("errors", []) - if error.get("error") - == ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value - ] ], ExecutionStatus.ISSUE_REPORTED: [ result @@ -90,12 +84,6 @@ def get_execution_status(results): result for result in results if result.get("executionStatus") == ExecutionStatus.SKIPPED.value - or [ - error - for error in result.get("errors", []) - if error.get("error") - == ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value - ] ], } ) From d7764cb6d25c61991997a6c42f7ca4238c76da93 Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Thu, 22 Jan 2026 14:39:08 -0500 Subject: [PATCH 3/9] skip reasons and better execution error categorization --- cdisc_rules_engine/enums/execution_status.py | 8 ++++- cdisc_rules_engine/rules_engine.py | 35 +++++++++++++------ .../unit/test_rule_tester/test_rule_tester.py | 2 +- tests/unit/test_rules_engine.py | 8 ++++- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/cdisc_rules_engine/enums/execution_status.py b/cdisc_rules_engine/enums/execution_status.py index 96ab25540..ab8c1740e 100644 --- a/cdisc_rules_engine/enums/execution_status.py +++ b/cdisc_rules_engine/enums/execution_status.py @@ -9,6 +9,12 @@ class ExecutionStatus(BaseEnum): UNKNOWN_STATUS = "unknown_status" +class SkippedReason(BaseEnum): + COLUMN_NOT_FOUND_IN_DATA = "Column not found in data" + DOMAIN_NOT_FOUND = "Domain not found" + SCHEMA_VALIDATION_IS_OFF = "Schema validation is off" + OUTSIDE_SCOPE = "Outside scope" + + class ExecutionError(BaseEnum): AN_UNKNOWN_EXCEPTION_HAS_OCCURRED = "An unknown exception has occurred" - COLUMN_NOT_FOUND_IN_DATA = "Column not found in data" diff --git a/cdisc_rules_engine/rules_engine.py b/cdisc_rules_engine/rules_engine.py index 322c4bc44..1d470273c 100644 --- a/cdisc_rules_engine/rules_engine.py +++ b/cdisc_rules_engine/rules_engine.py @@ -5,7 +5,11 @@ from business_rules.engine import run import os from cdisc_rules_engine.config import config as default_config -from cdisc_rules_engine.enums.execution_status import ExecutionError, ExecutionStatus +from cdisc_rules_engine.enums.execution_status import ( + ExecutionError, + ExecutionStatus, + SkippedReason, +) from cdisc_rules_engine.enums.rule_types import RuleTypes from cdisc_rules_engine.exceptions.custom_exceptions import ( DatasetNotFoundError, @@ -243,13 +247,22 @@ def validate_single_dataset( logger.info( f"Skipped dataset {dataset_metadata.name}. Reason: {reason}" ) - error_obj = ValidationErrorContainer( - status=ExecutionStatus.SKIPPED.value, + error_obj = FailedValidationEntity( + dataset=os.path.basename(dataset_metadata.full_path), + error=SkippedReason.OUTSIDE_SCOPE.value, message=reason, - dataset=dataset_metadata.filename, - domain=dataset_metadata.domain or dataset_metadata.rdomain or "", ) - return [error_obj.to_representation()] + return [ + ValidationErrorContainer( + status=ExecutionStatus.SKIPPED.value, + message=reason, + dataset=dataset_metadata.filename, + domain=dataset_metadata.domain + or dataset_metadata.rdomain + or "", + errors=[error_obj], + ).to_representation() + ] except Exception as e: logger.trace(e) logger.error( @@ -486,7 +499,7 @@ def handle_validation_exceptions( # noqa elif isinstance(exception, (KeyError, ParserError)): error_obj = FailedValidationEntity( dataset=os.path.basename(dataset_path), - error=ExecutionError.COLUMN_NOT_FOUND_IN_DATA.value, + error=SkippedReason.COLUMN_NOT_FOUND_IN_DATA.value, message=exception.args[0], ) message = "rule execution error" @@ -573,7 +586,7 @@ def handle_validation_exceptions( # noqa dataset=os.path.basename(dataset_path), errors=errors, message=message, - status=ExecutionStatus.SKIPPED.value, + status=ExecutionStatus.EXECUTION_ERROR.value, ) elif isinstance(exception, FailedSchemaValidation): if self.validate_xml: @@ -593,7 +606,7 @@ def handle_validation_exceptions( # noqa else: message = "Skipped because schema validation is off" error_obj = FailedValidationEntity( - error="Schema validation is off", + error=SkippedReason.SCHEMA_VALIDATION_IS_OFF.value, message=message, dataset=os.path.basename(dataset_path), ) @@ -607,7 +620,7 @@ def handle_validation_exceptions( # noqa elif isinstance(exception, DomainNotFoundError): error_obj = FailedValidationEntity( dataset=os.path.basename(dataset_path), - error="Domain not found", + error=SkippedReason.DOMAIN_NOT_FOUND.value, message=str(exception), ) message = "rule evaluation skipped - operation domain not found" @@ -632,7 +645,7 @@ def handle_validation_exceptions( # noqa dataset=os.path.basename(dataset_path), errors=errors, message=message, - status=ExecutionStatus.SKIPPED.value, + status=ExecutionStatus.EXECUTION_ERROR.value, ) else: error_obj = FailedValidationEntity( diff --git a/tests/unit/test_rule_tester/test_rule_tester.py b/tests/unit/test_rule_tester/test_rule_tester.py index c2e46b6d5..de381880f 100644 --- a/tests/unit/test_rule_tester/test_rule_tester.py +++ b/tests/unit/test_rule_tester/test_rule_tester.py @@ -201,7 +201,7 @@ def test_rule_skipped(): data = run_single_rule_validation(datasets, rule) assert "LB" in data assert len(data["LB"]) == 1 - assert len(data["LB"][0]["errors"]) == 0 + assert len(data["LB"][0]["errors"]) == 1 assert data["LB"][0]["executionStatus"] == "skipped" diff --git a/tests/unit/test_rules_engine.py b/tests/unit/test_rules_engine.py index 980c2b5a8..bf1ff3997 100644 --- a/tests/unit/test_rules_engine.py +++ b/tests/unit/test_rules_engine.py @@ -1601,7 +1601,13 @@ def filter_func(row): "executionStatus": ExecutionStatus.SKIPPED.value, "variables": [], "message": "Rule skipped - doesn't apply to domain for rule id=MockRule, dataset=", - "errors": [], + "errors": [ + { + "dataset": "ae_2.xpt", + "error": "Outside scope", + "message": "Rule skipped - doesn't apply to domain for rule id=MockRule, dataset=", + } + ], } ], ), From dfda21a367f6b2ded82210ee9a70db9c5f59a75a Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Thu, 22 Jan 2026 18:02:18 -0500 Subject: [PATCH 4/9] error dask parquet filename fix --- cdisc_rules_engine/rules_engine.py | 59 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/cdisc_rules_engine/rules_engine.py b/cdisc_rules_engine/rules_engine.py index 1d470273c..2738d7e4b 100644 --- a/cdisc_rules_engine/rules_engine.py +++ b/cdisc_rules_engine/rules_engine.py @@ -3,7 +3,6 @@ from dateutil.parser._parser import ParserError from business_rules import export_rule_data from business_rules.engine import run -import os from cdisc_rules_engine.config import config as default_config from cdisc_rules_engine.enums.execution_status import ( ExecutionError, @@ -248,7 +247,7 @@ def validate_single_dataset( f"Skipped dataset {dataset_metadata.name}. Reason: {reason}" ) error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_metadata.full_path), + dataset=dataset_metadata.filename, error=SkippedReason.OUTSIDE_SCOPE.value, message=reason, ) @@ -276,7 +275,7 @@ def validate_single_dataset( """ ) error_obj: ValidationErrorContainer = self.handle_validation_exceptions( - e, dataset_metadata.full_path, dataset_metadata.full_path + e, dataset_metadata.filename ) error_obj.domain = dataset_metadata.domain or dataset_metadata.rdomain or "" # this wrapping into a list is necessary to keep return type consistent @@ -473,88 +472,88 @@ def get_define_xml_value_level_metadata( return define_xml_reader.extract_value_level_metadata(domain_name=domain_name) def handle_validation_exceptions( # noqa - self, exception, dataset_path, file_name + self, exception, filename: str ) -> ValidationErrorContainer: if isinstance(exception, DatasetNotFoundError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error="Dataset Not Found", message=exception.message, ) message = "rule execution error" elif isinstance(exception, RuleFormatError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error="Rule format error", message=exception.message, ) message = "rule execution error" elif isinstance(exception, AssertionError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error="Rule format error", message="Rule contains invalid operator", ) message = "rule execution error" elif isinstance(exception, (KeyError, ParserError)): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=SkippedReason.COLUMN_NOT_FOUND_IN_DATA.value, message=exception.args[0], ) message = "rule execution error" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.SKIPPED.value, ) elif isinstance(exception, DomainNotFoundInDefineXMLError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=DomainNotFoundInDefineXMLError.description, message=exception.args[0], ) message = "rule execution error" elif isinstance(exception, SchemaNotFoundError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=SchemaNotFoundError.description, message=exception.args[0], ) message = "rule execution error" elif isinstance(exception, InvalidSchemaProvidedError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=InvalidSchemaProvidedError.description, message=exception.args[0], ) message = "rule execution error" elif isinstance(exception, VariableMetadataNotFoundError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=VariableMetadataNotFoundError.description, message=exception.args[0], ) message = "rule execution error" elif isinstance(exception, InvalidJSONFormat): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=InvalidJSONFormat.description, message=exception.args[0], ) message = "rule execution error" elif isinstance(exception, PreprocessingError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=PreprocessingError.description, message=str(exception), ) message = "rule evaluation error - preprocessing failed" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.EXECUTION_ERROR.value, @@ -562,28 +561,28 @@ def handle_validation_exceptions( # noqa elif isinstance(exception, OperationError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=OperationError.description, message=str(exception), ) message = "rule evaluation error - operation failed" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.EXECUTION_ERROR.value, ) elif isinstance(exception, DatasetBuilderError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=DatasetBuilderError.description, message=str(exception), ) message = "rule evaluation error - evaluation dataset failed to build" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.EXECUTION_ERROR.value, @@ -593,7 +592,7 @@ def handle_validation_exceptions( # noqa error_obj = FailedValidationEntity( error=FailedSchemaValidation.description, message=exception.args[0], - dataset=os.path.basename(dataset_path), + dataset=filename, ) message = "Schema Validation Error" errors = [error_obj] @@ -601,32 +600,32 @@ def handle_validation_exceptions( # noqa errors=errors, message=message, status=ExecutionStatus.SUCCESS.value, - dataset=os.path.basename(dataset_path), + dataset=filename, ) else: message = "Skipped because schema validation is off" error_obj = FailedValidationEntity( error=SkippedReason.SCHEMA_VALIDATION_IS_OFF.value, message=message, - dataset=os.path.basename(dataset_path), + dataset=filename, ) errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.SKIPPED.value, ) elif isinstance(exception, DomainNotFoundError): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=SkippedReason.DOMAIN_NOT_FOUND.value, message=str(exception), ) message = "rule evaluation skipped - operation domain not found" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.SKIPPED.value, @@ -635,28 +634,28 @@ def handle_validation_exceptions( # noqa exception, AttributeError ) and "'NoneType' object has no attribute" in str(exception): error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error="Missing field during execution", message="Missing field during execution, rule may not be applicable- unable to process dataset", ) message = "rule evaluation skipped - missing metadata" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.EXECUTION_ERROR.value, ) else: error_obj = FailedValidationEntity( - dataset=os.path.basename(dataset_path), + dataset=filename, error=ExecutionError.AN_UNKNOWN_EXCEPTION_HAS_OCCURRED.value, message=str(exception), ) message = "rule execution error" errors = [error_obj] return ValidationErrorContainer( - dataset=os.path.basename(dataset_path), + dataset=filename, errors=errors, message=message, status=ExecutionStatus.EXECUTION_ERROR.value, From 7e5da72710f37e608f4c73cf2c6658401ab1cbe6 Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Wed, 28 Jan 2026 23:20:11 -0500 Subject: [PATCH 5/9] fix dask meta issue --- cdisc_rules_engine/check_operators/dataframe_operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index 8e26e7760..18232068a 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -338,6 +338,7 @@ def equal_to_case_insensitive(self, other_value): round_values=round_values, ), axis=1, + meta=(None, "bool"), ) @log_operator_execution From abfd1025bc8eafd68509f9f272d199d5f8979052 Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Thu, 29 Jan 2026 14:21:35 -0500 Subject: [PATCH 6/9] make date parser error a rule execution error instead of skip --- cdisc_rules_engine/exceptions/custom_exceptions.py | 5 +++++ cdisc_rules_engine/rules_engine.py | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cdisc_rules_engine/exceptions/custom_exceptions.py b/cdisc_rules_engine/exceptions/custom_exceptions.py index 94da31a67..e3e5b79d5 100644 --- a/cdisc_rules_engine/exceptions/custom_exceptions.py +++ b/cdisc_rules_engine/exceptions/custom_exceptions.py @@ -100,3 +100,8 @@ class OperationError(EngineError): class DatasetBuilderError(EngineError): description = "Error occurred during dataset building" + + +class DateTimeParserError(EngineError): + code = 400 + description = "Failure to parse a datetime string" diff --git a/cdisc_rules_engine/rules_engine.py b/cdisc_rules_engine/rules_engine.py index 2738d7e4b..b5ee71eb6 100644 --- a/cdisc_rules_engine/rules_engine.py +++ b/cdisc_rules_engine/rules_engine.py @@ -12,6 +12,7 @@ from cdisc_rules_engine.enums.rule_types import RuleTypes from cdisc_rules_engine.exceptions.custom_exceptions import ( DatasetNotFoundError, + DateTimeParserError, DomainNotFoundInDefineXMLError, InvalidJSONFormat, RuleFormatError, @@ -495,7 +496,7 @@ def handle_validation_exceptions( # noqa message="Rule contains invalid operator", ) message = "rule execution error" - elif isinstance(exception, (KeyError, ParserError)): + elif isinstance(exception, KeyError): error_obj = FailedValidationEntity( dataset=filename, error=SkippedReason.COLUMN_NOT_FOUND_IN_DATA.value, @@ -509,6 +510,13 @@ def handle_validation_exceptions( # noqa message=message, status=ExecutionStatus.SKIPPED.value, ) + elif isinstance(exception, ParserError): + error_obj = FailedValidationEntity( + dataset=filename, + error=DateTimeParserError.description, + message=exception.args[0], + ) + message = "rule execution error" elif isinstance(exception, DomainNotFoundInDefineXMLError): error_obj = FailedValidationEntity( dataset=filename, From 5a253dec492141f86c50295946878bb39e433096 Mon Sep 17 00:00:00 2001 From: gerrycampion <85252124+gerrycampion@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:10:24 -0500 Subject: [PATCH 7/9] dataset_preprocessor.py lint update --- cdisc_rules_engine/utilities/dataset_preprocessor.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cdisc_rules_engine/utilities/dataset_preprocessor.py b/cdisc_rules_engine/utilities/dataset_preprocessor.py index af2ed96d4..7c55a191b 100644 --- a/cdisc_rules_engine/utilities/dataset_preprocessor.py +++ b/cdisc_rules_engine/utilities/dataset_preprocessor.py @@ -16,10 +16,7 @@ get_sided_match_keys, get_dataset_name_from_details, ) -from cdisc_rules_engine.exceptions.custom_exceptions import ( - DomainNotFoundError, - PreprocessingError, -) +from cdisc_rules_engine.exceptions.custom_exceptions import PreprocessingError import os import pandas as pd From 8c7980cba163eb5af39c2c1107411693c064ebbd Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Wed, 4 Feb 2026 11:31:31 -0500 Subject: [PATCH 8/9] Fix issue summary tab --- cdisc_rules_engine/services/reporting/sdtm_report_data.py | 3 +-- cdisc_rules_engine/services/reporting/usdm_report_data.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cdisc_rules_engine/services/reporting/sdtm_report_data.py b/cdisc_rules_engine/services/reporting/sdtm_report_data.py index 7c2cf51ba..881bbb0dd 100644 --- a/cdisc_rules_engine/services/reporting/sdtm_report_data.py +++ b/cdisc_rules_engine/services/reporting/sdtm_report_data.py @@ -215,8 +215,7 @@ def get_summary_data(self) -> list[dict]: dataset = result.get("dataset") if ( result.get("errors") - and result.get("executionStatus") - == ExecutionStatus.ISSUE_REPORTED.value + and result.get("executionStatus") != ExecutionStatus.SKIPPED.value ): summary_item = { "dataset": dataset, diff --git a/cdisc_rules_engine/services/reporting/usdm_report_data.py b/cdisc_rules_engine/services/reporting/usdm_report_data.py index 71a58a54b..01f0fbb28 100644 --- a/cdisc_rules_engine/services/reporting/usdm_report_data.py +++ b/cdisc_rules_engine/services/reporting/usdm_report_data.py @@ -122,8 +122,7 @@ def get_summary_data(self) -> list[dict]: for result in validation_result.results or []: if ( result.get("errors") - and result.get("executionStatus") - == ExecutionStatus.ISSUE_REPORTED.value + and result.get("executionStatus") != ExecutionStatus.SKIPPED.value ): summary_item = { "entity": result.get("entity") From 3a87dea2ac8cdb09a0ea5e238791d8b5594c476f Mon Sep 17 00:00:00 2001 From: Gerry Campion Date: Fri, 6 Feb 2026 09:27:02 -0500 Subject: [PATCH 9/9] merge main report-template changes --- resources/templates/report-template.xlsx | Bin 20606 -> 20694 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/templates/report-template.xlsx b/resources/templates/report-template.xlsx index 759a54a1c4af7c0369fab828a6018d2d3584d5bc..41b3eb81a5674513832c80179777143cbbc205a0 100644 GIT binary patch delta 12141 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 a7Uv%z^ePQ(uPOpd2Ch)0L1I>V9QuC=xj4cA