From 5f783c5861afe5aed4c2bc910d7a2051c2aeb8ac Mon Sep 17 00:00:00 2001 From: alexfurmenkov Date: Tue, 13 Jan 2026 11:54:19 +0100 Subject: [PATCH 1/4] 1501: implemented proposed fix for raw report. --- .../services/reporting/json_report.py | 2 +- .../test_Issues/test_CoreIssue1501.py | 50 +++++++ tests/resources/CoreIssue1501/Rule.yml | 132 ++++++++++++++++++ .../unit-test-coreid-SENDIG282-negative.json | 82 +++++++++++ .../unit-test-coreid-SENDIG282-positive.json | 82 +++++++++++ .../test_reporting/test_json_export.py | 28 ++++ 6 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 tests/QARegressionTests/test_Issues/test_CoreIssue1501.py create mode 100644 tests/resources/CoreIssue1501/Rule.yml create mode 100644 tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json create mode 100644 tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json diff --git a/cdisc_rules_engine/services/reporting/json_report.py b/cdisc_rules_engine/services/reporting/json_report.py index 3c1a32b2b..7dff629ca 100644 --- a/cdisc_rules_engine/services/reporting/json_report.py +++ b/cdisc_rules_engine/services/reporting/json_report.py @@ -58,7 +58,7 @@ def get_export( json_export[self._get_property_name(sheet_name)] = data_sheet if raw_report: json_export["results_data"] = [ - rule_result.to_representation() for rule_result in self._results + rule_result.to_representation() for rule_result in self._report_standard._results ] return json_export diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py new file mode 100644 index 000000000..f5bff9ba1 --- /dev/null +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -0,0 +1,50 @@ +import os +import subprocess +import unittest + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1501(unittest.TestCase): + def test_raw_report(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "send", + "-v", + "1-0", + "-dp", + "tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json", + "-lr", + "tests/resources/CoreIssue1501/Rule.yml", + "-ps", + "1", + "-of", + "json", + "-rr", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert ['Conformance_Details', 'Dataset_Details', 'Issue_Summary', 'Issue_Details', 'Rules_Report'] in json_report + + if not 'results_data' in json_report.keys(): + assert False, "'results_data' key not found in report. Expected while using -rr, --raw_report flag" + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/resources/CoreIssue1501/Rule.yml b/tests/resources/CoreIssue1501/Rule.yml new file mode 100644 index 000000000..2ac8a5682 --- /dev/null +++ b/tests/resources/CoreIssue1501/Rule.yml @@ -0,0 +1,132 @@ +Authorities: + - Organization: CDISC + Standards: + - Name: SENDIG + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "3.1" + - Name: SENDIG + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "3.1.1" + - Name: SENDIG-DART + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.1" + - Name: SENDIG-DART + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.2" + - Name: SENDIG-GENETOX + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.0" +Check: + all: + - name: $spec_codelist + operator: not_contains_all + value: $ppspec_value +Core: + Id: CDISC.SENDIG.SEND282 + Status: Draft + Version: "1" +Operations: + - codelists: + - SPEC + level: term + id: $spec_codelist + operator: codelist_terms + returntype: value + - name: PPSPEC + delimiter: ; + id: $ppspec_value + operator: split_by +Description: If multiple specimen types are used for a calculation (e.g., serum + and urine for creatinine clearance), then this field should be populated with + values from the (SPEC) Controlled Terminology codelist delimited by a + semicolon. +Executability: Fully Executable +Outcome: + Message: The multiple specimens are not correctly separated by a semicolon + Output Variables: + - $ppspec_value + - $spec_codelist +Rule Type: Record Data +Scope: + Classes: + Include: + - ALL + Domains: + Include: + - ALL +Sensitivity: Record diff --git a/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json new file mode 100644 index 000000000..9d62965bd --- /dev/null +++ b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json @@ -0,0 +1,82 @@ +{ + "datasets": [ + { + "filename": "pp.xpt", + "label": "Pharmacokinetics Parameters", + "domain": "PP", + "variables": [ + { + "name": "STUDYID", + "label": "Study Identifier", + "type": "Char", + "length": 12 + }, + { + "name": "DOMAIN", + "label": "Domain Abbreviation", + "type": "Char", + "length": 2 + }, + { + "name": "USUBJID", + "label": "Unique Subject Identifier", + "type": "Char", + "length": 20 + }, + { + "name": "PPSEQ", + "label": "Sequence Number", + "type": "Num", + "length": 20 + }, + { + "name": "PPTESTCD", + "label": "Parameter Short Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPTEST", + "label": "Parameter Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPSPEC", + "label": "Specimen Material Type", + "type": "Char", + "length": 20 + } + ], + "records": { + "STUDYID": [ + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01" + ], + "DOMAIN": ["PP", "PP", "PP", "PP"], + "USUBJID": ["CDISC001", "CDISC001", "CDISC001", "CDISC001"], + "PPSEQ": [1, 2, 3, 4], + "PPTESTCD": ["TMAX", "TMAX", "TMAX", "TMAX"], + "PPTEST": [ + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX " + ], + "PPSPEC": [ + "ABDOMINAL WALL", + "ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL|ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN&AIR SAC" + ] + } + } + ], + "standard": { + "product": "sendig", + "version": "3-1" + }, + "codelists": [] +} diff --git a/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json new file mode 100644 index 000000000..c93389420 --- /dev/null +++ b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json @@ -0,0 +1,82 @@ +{ + "datasets": [ + { + "filename": "pp.xpt", + "label": "Pharmacokinetics Parameters", + "domain": "PP", + "variables": [ + { + "name": "STUDYID", + "label": "Study Identifier", + "type": "Char", + "length": 12 + }, + { + "name": "DOMAIN", + "label": "Domain Abbreviation", + "type": "Char", + "length": 2 + }, + { + "name": "USUBJID", + "label": "Unique Subject Identifier", + "type": "Char", + "length": 20 + }, + { + "name": "PPSEQ", + "label": "Sequence Number", + "type": "Num", + "length": 20 + }, + { + "name": "PPTESTCD", + "label": "Parameter Short Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPTEST", + "label": "Parameter Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPSPEC", + "label": "Specimen Material Type", + "type": "Char", + "length": 20 + } + ], + "records": { + "STUDYID": [ + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01" + ], + "DOMAIN": ["PP", "PP", "PP", "PP"], + "USUBJID": ["CDISC001", "CDISC001", "CDISC001", "CDISC001"], + "PPSEQ": [1, 2, 3, 4], + "PPTESTCD": ["TMAX", "TMAX", "TMAX", "TMAX"], + "PPTEST": [ + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX " + ], + "PPSPEC": [ + "ABDOMINAL WALL", + "ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN;AIR SAC" + ] + } + } + ], + "standard": { + "product": "sendig", + "version": "3-1" + }, + "codelists": [] +} diff --git a/tests/unit/test_services/test_reporting/test_json_export.py b/tests/unit/test_services/test_reporting/test_json_export.py index 2ff0e8f59..a3833e840 100644 --- a/tests/unit/test_services/test_reporting/test_json_export.py +++ b/tests/unit/test_services/test_reporting/test_json_export.py @@ -48,3 +48,31 @@ def test_get_export(_, mock_validation_results): assert len(export["Issue_Summary"]) > 0 assert len(export["Issue_Details"]) > 0 assert len(export["Rules_Report"]) > 0 + + +@patch.object( + sdtm_report_data.SDTMReportData, + "get_define_version", + return_value="2.1", +) +def test_get_raw_export(_, mock_validation_results): + mock_args = MagicMock() + mock_args.max_errors_per_rule = (None, False) + mock_args.controlled_terminology_package = ["sdtmct-03-2021"] + mock_args.standard = "sdtmig" + mock_args.version = "3.4" + mock_args.dictionary_versions = {} + mock_args.whodrug = "test" + report_standard = SDTMReportData( + [], ["test"], mock_validation_results, 10.1, mock_args + ) + report: JsonReport = JsonReport(report_standard, mock_args, None) + export = report.get_export( + raw_report=True, + ) + assert ({'Conformance_Details', 'Dataset_Details', 'Issue_Summary', 'Issue_Details', 'Rules_Report'} + .issubset(export.keys())) + assert len(export["Issue_Summary"]) > 0 + assert len(export["Issue_Details"]) > 0 + assert len(export["Rules_Report"]) > 0 + assert 'results_data' in export From d9e3377b2a689a0897f19ddcecf53ba5d9a1bf19 Mon Sep 17 00:00:00 2001 From: alexfurmenkov Date: Tue, 13 Jan 2026 11:54:19 +0100 Subject: [PATCH 2/4] 1501: implemented proposed fix for raw report. --- .../services/reporting/json_report.py | 2 +- .../test_Issues/test_CoreIssue1501.py | 58 ++++++++ tests/resources/CoreIssue1501/Rule.yml | 132 ++++++++++++++++++ .../unit-test-coreid-SENDIG282-negative.json | 82 +++++++++++ .../unit-test-coreid-SENDIG282-positive.json | 82 +++++++++++ .../test_reporting/test_json_export.py | 28 ++++ 6 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 tests/QARegressionTests/test_Issues/test_CoreIssue1501.py create mode 100644 tests/resources/CoreIssue1501/Rule.yml create mode 100644 tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json create mode 100644 tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json diff --git a/cdisc_rules_engine/services/reporting/json_report.py b/cdisc_rules_engine/services/reporting/json_report.py index 3c1a32b2b..7dff629ca 100644 --- a/cdisc_rules_engine/services/reporting/json_report.py +++ b/cdisc_rules_engine/services/reporting/json_report.py @@ -58,7 +58,7 @@ def get_export( json_export[self._get_property_name(sheet_name)] = data_sheet if raw_report: json_export["results_data"] = [ - rule_result.to_representation() for rule_result in self._results + rule_result.to_representation() for rule_result in self._report_standard._results ] return json_export diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py new file mode 100644 index 000000000..09e55323b --- /dev/null +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -0,0 +1,58 @@ +import os +import subprocess +import unittest + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1501(unittest.TestCase): + def test_raw_report(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "send", + "-v", + "1-0", + "-dp", + "tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json", + "-lr", + "tests/resources/CoreIssue1501/Rule.yml", + "-ps", + "1", + "-of", + "json", + "-rr", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + + if not "results_data" in json_report.keys(): + assert ( + False + ), "'results_data' key not found in report. Expected while using -rr, --raw_report flag" + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/resources/CoreIssue1501/Rule.yml b/tests/resources/CoreIssue1501/Rule.yml new file mode 100644 index 000000000..2ac8a5682 --- /dev/null +++ b/tests/resources/CoreIssue1501/Rule.yml @@ -0,0 +1,132 @@ +Authorities: + - Organization: CDISC + Standards: + - Name: SENDIG + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "3.1" + - Name: SENDIG + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "3.1.1" + - Name: SENDIG-DART + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.1" + - Name: SENDIG-DART + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.2" + - Name: SENDIG-GENETOX + References: + - Citations: + - Cited Guidance: + "Defines the type of specimen used for a measurement. Examples: + SERUM, PLASMA, URINE. If multiple specimen types are used for + a calculation (e.g., serum and urine for creatinine + clearance), then refer to Section 4.3.6.2 for guidance on how + to populate." + Document: IG v3.1.1 + Item: PPSPEC CDISC Notes + Section: 6.3.12 + Origin: SEND Conformance Rules + Rule Identifier: + Id: "SEND282" + Version: "1" + Version: "5.0" + Version: "1.0" +Check: + all: + - name: $spec_codelist + operator: not_contains_all + value: $ppspec_value +Core: + Id: CDISC.SENDIG.SEND282 + Status: Draft + Version: "1" +Operations: + - codelists: + - SPEC + level: term + id: $spec_codelist + operator: codelist_terms + returntype: value + - name: PPSPEC + delimiter: ; + id: $ppspec_value + operator: split_by +Description: If multiple specimen types are used for a calculation (e.g., serum + and urine for creatinine clearance), then this field should be populated with + values from the (SPEC) Controlled Terminology codelist delimited by a + semicolon. +Executability: Fully Executable +Outcome: + Message: The multiple specimens are not correctly separated by a semicolon + Output Variables: + - $ppspec_value + - $spec_codelist +Rule Type: Record Data +Scope: + Classes: + Include: + - ALL + Domains: + Include: + - ALL +Sensitivity: Record diff --git a/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json new file mode 100644 index 000000000..9d62965bd --- /dev/null +++ b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json @@ -0,0 +1,82 @@ +{ + "datasets": [ + { + "filename": "pp.xpt", + "label": "Pharmacokinetics Parameters", + "domain": "PP", + "variables": [ + { + "name": "STUDYID", + "label": "Study Identifier", + "type": "Char", + "length": 12 + }, + { + "name": "DOMAIN", + "label": "Domain Abbreviation", + "type": "Char", + "length": 2 + }, + { + "name": "USUBJID", + "label": "Unique Subject Identifier", + "type": "Char", + "length": 20 + }, + { + "name": "PPSEQ", + "label": "Sequence Number", + "type": "Num", + "length": 20 + }, + { + "name": "PPTESTCD", + "label": "Parameter Short Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPTEST", + "label": "Parameter Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPSPEC", + "label": "Specimen Material Type", + "type": "Char", + "length": 20 + } + ], + "records": { + "STUDYID": [ + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01" + ], + "DOMAIN": ["PP", "PP", "PP", "PP"], + "USUBJID": ["CDISC001", "CDISC001", "CDISC001", "CDISC001"], + "PPSEQ": [1, 2, 3, 4], + "PPTESTCD": ["TMAX", "TMAX", "TMAX", "TMAX"], + "PPTEST": [ + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX " + ], + "PPSPEC": [ + "ABDOMINAL WALL", + "ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL|ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN&AIR SAC" + ] + } + } + ], + "standard": { + "product": "sendig", + "version": "3-1" + }, + "codelists": [] +} diff --git a/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json new file mode 100644 index 000000000..c93389420 --- /dev/null +++ b/tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-positive.json @@ -0,0 +1,82 @@ +{ + "datasets": [ + { + "filename": "pp.xpt", + "label": "Pharmacokinetics Parameters", + "domain": "PP", + "variables": [ + { + "name": "STUDYID", + "label": "Study Identifier", + "type": "Char", + "length": 12 + }, + { + "name": "DOMAIN", + "label": "Domain Abbreviation", + "type": "Char", + "length": 2 + }, + { + "name": "USUBJID", + "label": "Unique Subject Identifier", + "type": "Char", + "length": 20 + }, + { + "name": "PPSEQ", + "label": "Sequence Number", + "type": "Num", + "length": 20 + }, + { + "name": "PPTESTCD", + "label": "Parameter Short Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPTEST", + "label": "Parameter Name", + "type": "Char", + "length": 20 + }, + { + "name": "PPSPEC", + "label": "Specimen Material Type", + "type": "Char", + "length": 20 + } + ], + "records": { + "STUDYID": [ + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01", + "CDISCPILOT01" + ], + "DOMAIN": ["PP", "PP", "PP", "PP"], + "USUBJID": ["CDISC001", "CDISC001", "CDISC001", "CDISC001"], + "PPSEQ": [1, 2, 3, 4], + "PPTESTCD": ["TMAX", "TMAX", "TMAX", "TMAX"], + "PPTEST": [ + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX ", + "Time of CMAX " + ], + "PPSPEC": [ + "ABDOMINAL WALL", + "ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN", + "ABDOMINAL WALL;ADIPOSE TISSUE, BROWN;AIR SAC" + ] + } + } + ], + "standard": { + "product": "sendig", + "version": "3-1" + }, + "codelists": [] +} diff --git a/tests/unit/test_services/test_reporting/test_json_export.py b/tests/unit/test_services/test_reporting/test_json_export.py index 2ff0e8f59..a3833e840 100644 --- a/tests/unit/test_services/test_reporting/test_json_export.py +++ b/tests/unit/test_services/test_reporting/test_json_export.py @@ -48,3 +48,31 @@ def test_get_export(_, mock_validation_results): assert len(export["Issue_Summary"]) > 0 assert len(export["Issue_Details"]) > 0 assert len(export["Rules_Report"]) > 0 + + +@patch.object( + sdtm_report_data.SDTMReportData, + "get_define_version", + return_value="2.1", +) +def test_get_raw_export(_, mock_validation_results): + mock_args = MagicMock() + mock_args.max_errors_per_rule = (None, False) + mock_args.controlled_terminology_package = ["sdtmct-03-2021"] + mock_args.standard = "sdtmig" + mock_args.version = "3.4" + mock_args.dictionary_versions = {} + mock_args.whodrug = "test" + report_standard = SDTMReportData( + [], ["test"], mock_validation_results, 10.1, mock_args + ) + report: JsonReport = JsonReport(report_standard, mock_args, None) + export = report.get_export( + raw_report=True, + ) + assert ({'Conformance_Details', 'Dataset_Details', 'Issue_Summary', 'Issue_Details', 'Rules_Report'} + .issubset(export.keys())) + assert len(export["Issue_Summary"]) > 0 + assert len(export["Issue_Details"]) > 0 + assert len(export["Rules_Report"]) > 0 + assert 'results_data' in export From 51e7e8c0d463f36e762c49f987f5b467e31abf41 Mon Sep 17 00:00:00 2001 From: alexfurmenkov Date: Tue, 13 Jan 2026 12:32:40 +0100 Subject: [PATCH 3/4] standardized command parameters --- .../test_Issues/test_CoreIssue1501.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index 09e55323b..832e06851 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -21,9 +21,14 @@ def test_raw_report(self): "-v", "1-0", "-dp", - "tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json", + os.path.join( + "tests", + "resources", + "CoreIssue1501", + "unit-test-coreid-SENDIG282-negative.json", + ), "-lr", - "tests/resources/CoreIssue1501/Rule.yml", + os.path.join("tests", "resources", "CoreIssue1501", "Rule.yml"), "-ps", "1", "-of", @@ -50,7 +55,7 @@ def test_raw_report(self): "Rules_Report", }.issubset(json_report.keys()) - if not "results_data" in json_report.keys(): + if "results_data" not in json_report.keys(): assert ( False ), "'results_data' key not found in report. Expected while using -rr, --raw_report flag" From eb22a448298607594cd612181c6e7eb7f01ab026 Mon Sep 17 00:00:00 2001 From: alexfurmenkov Date: Tue, 13 Jan 2026 12:32:40 +0100 Subject: [PATCH 4/4] standardized command parameters --- cdisc_rules_engine/services/reporting/json_report.py | 3 ++- .../test_Issues/test_CoreIssue1501.py | 11 ++++++++--- .../test_services/test_reporting/test_json_export.py | 11 ++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cdisc_rules_engine/services/reporting/json_report.py b/cdisc_rules_engine/services/reporting/json_report.py index 7dff629ca..afc50f8b6 100644 --- a/cdisc_rules_engine/services/reporting/json_report.py +++ b/cdisc_rules_engine/services/reporting/json_report.py @@ -58,7 +58,8 @@ def get_export( json_export[self._get_property_name(sheet_name)] = data_sheet if raw_report: json_export["results_data"] = [ - rule_result.to_representation() for rule_result in self._report_standard._results + rule_result.to_representation() + for rule_result in self._report_standard._results ] return json_export diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index 09e55323b..832e06851 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -21,9 +21,14 @@ def test_raw_report(self): "-v", "1-0", "-dp", - "tests/resources/CoreIssue1501/unit-test-coreid-SENDIG282-negative.json", + os.path.join( + "tests", + "resources", + "CoreIssue1501", + "unit-test-coreid-SENDIG282-negative.json", + ), "-lr", - "tests/resources/CoreIssue1501/Rule.yml", + os.path.join("tests", "resources", "CoreIssue1501", "Rule.yml"), "-ps", "1", "-of", @@ -50,7 +55,7 @@ def test_raw_report(self): "Rules_Report", }.issubset(json_report.keys()) - if not "results_data" in json_report.keys(): + if "results_data" not in json_report.keys(): assert ( False ), "'results_data' key not found in report. Expected while using -rr, --raw_report flag" diff --git a/tests/unit/test_services/test_reporting/test_json_export.py b/tests/unit/test_services/test_reporting/test_json_export.py index a3833e840..8be646b84 100644 --- a/tests/unit/test_services/test_reporting/test_json_export.py +++ b/tests/unit/test_services/test_reporting/test_json_export.py @@ -70,9 +70,14 @@ def test_get_raw_export(_, mock_validation_results): export = report.get_export( raw_report=True, ) - assert ({'Conformance_Details', 'Dataset_Details', 'Issue_Summary', 'Issue_Details', 'Rules_Report'} - .issubset(export.keys())) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(export.keys()) assert len(export["Issue_Summary"]) > 0 assert len(export["Issue_Details"]) > 0 assert len(export["Rules_Report"]) > 0 - assert 'results_data' in export + assert "results_data" in export