Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ Run `python core.py validate --help` to see the list of validation options.
-o, --output TEXT Report output file destination and name. Path will be
relative to the validation execution directory
and should end in the desired output filename
without file extension
'/user/reports/result' will be 'user/report' directory
with the filename as 'result'
without file extension. The file extension will be
automatically added based on the output format.
Example: 'reports/result' will create 'reports' directory
with the filename 'result.json' (or 'result.xlsx' for Excel).
Note: Provide a valid, writable path. Absolute paths must
be valid for your operating system.
-of, --output-format [JSON|XLSX]
Output file format
-rr, --raw-report Report in a raw format as it is generated by
Expand Down
2 changes: 2 additions & 0 deletions cdisc_rules_engine/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
)

NULL_FLAVORS = ["", None, {None}, [], {}, np.nan]

KNOWN_REPORT_EXTENSIONS = [".json", ".xlsx", ".xls"]
19 changes: 18 additions & 1 deletion cdisc_rules_engine/services/reporting/base_report.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from io import IOBase

from cdisc_rules_engine.constants import KNOWN_REPORT_EXTENSIONS
from cdisc_rules_engine.models.validation_args import Validation_args
from cdisc_rules_engine.services.reporting.base_report_data import (
BaseReportData,
Expand All @@ -21,7 +22,23 @@ def __init__(
self._report_standard = report_standard
self._args = args
self._template = template
self._output_name: str = f"{self._args.output}.{self._file_ext}"
self._output_name: str = self._get_output_filename()

def _get_output_filename(self) -> str:
expected_ext = f".{self._file_ext}"
output_path = self._args.output

if output_path.lower().endswith(expected_ext.lower()):
base_path = output_path[: -len(expected_ext)]
return f"{base_path}{expected_ext}"

path_lower = output_path.lower()
for ext in KNOWN_REPORT_EXTENSIONS:
if path_lower.endswith(ext):
base_path = output_path[: -len(ext)]
return f"{base_path}{expected_ext}"

return f"{output_path}{expected_ext}"

@property
@abstractmethod
Expand Down
13 changes: 13 additions & 0 deletions cdisc_rules_engine/services/reporting/excel_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,22 @@ def write_report(self):
logger = logging.getLogger("validator")
try:
report_data = self.get_export()
output_dir = os.path.dirname(self._output_name)
if output_dir:
try:
os.makedirs(output_dir, exist_ok=True)
except OSError as e:
error_msg = (
f"Cannot create output directory '{output_dir}': {e.strerror}. "
f"Please provide a valid, writable path for the output file."
)
logger.error(error_msg)
raise OSError(error_msg) from e
with open(self._output_name, "wb") as f:
f.write(excel_workbook_to_stream(report_data))
logger.debug(f"Report written to: {self._output_name}")
except OSError:
raise
except Exception as e:
logger.error(f"Error writing report: {e}")
raise e
Expand Down
10 changes: 10 additions & 0 deletions cdisc_rules_engine/services/reporting/json_report.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import os
from typing import BinaryIO, override

from cdisc_rules_engine.enums.report_types import ReportTypes
Expand Down Expand Up @@ -66,5 +67,14 @@ def write_report(self):
report_data = self.get_export(
raw_report=self._args.raw_report,
)
output_dir = os.path.dirname(self._output_name)
if output_dir:
try:
os.makedirs(output_dir, exist_ok=True)
except OSError as e:
raise OSError(
f"Cannot create output directory '{output_dir}': {e.strerror}. "
f"Please provide a valid, writable path for the output file."
) from e
with open(self._output_name, "w") as f:
json.dump(report_data, f)
7 changes: 6 additions & 1 deletion scripts/run_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,14 @@ def run_validation(args: Validation_args):
datasets, results, elapsed_time, args, data_service, dictionary_versions
)
reporting_services: List[BaseReport] = reporting_factory.get_report_services()
output_files = []
for reporting_service in reporting_services:
reporting_service.write_report()
print(f"Output: {args.output}")
output_files.append(reporting_service._output_name)
if len(output_files) == 1:
print(f"Output: {output_files[0]}")
else:
print(f"Output: {', '.join(output_files)}")
finally:
if created_files:
engine_logger.info(" Report generated, Cleaning up intermediate files")
Expand Down
Loading