///|
/// Human-readable report section.
pub struct ReportSection {
title : String
status : String
lines : Array[String]
fingerprint : UInt64
}
///|
/// CSV serialization policy.
pub struct CsvPolicy {
delimiter : String
decimal_digits : Int
include_header : Bool
missing_token : String
}
///|
/// Creates a CSV policy with safe defaults.
pub fn csv_policy(
delimiter? : String = ",",
decimal_digits? : Int = 6,
include_header? : Bool = true,
missing_token? : String = "",
) -> CsvPolicy {
{
delimiter,
decimal_digits: decimal_digits.max(0).min(15),
include_header,
missing_token,
}
}
///|
fn sr_number(value : Double, missing_token : String) -> String {
if is_finite(value) {
value.to_string()
} else {
missing_token
}
}
///|
/// Serializes a numeric vector as one CSV row.
pub fn serialize_vector(values : Array[Double], policy : CsvPolicy) -> String {
let builder = StringBuilder::new()
for i in 0.. 0 {
builder.write_string(policy.delimiter)
}
builder.write_string(sr_number(values[i], policy.missing_token))
}
builder.to_string()
}
///|
/// Serializes a numeric matrix as CSV.
pub fn serialize_matrix(
matrix : Array[Array[Double]],
policy : CsvPolicy,
headers? : Array[String] = [],
) -> String {
let builder = StringBuilder::new()
if policy.include_header && headers.length() > 0 {
for i in 0.. 0 {
builder.write_string(policy.delimiter)
}
builder.write_string(headers[i])
}
builder.write_string("\n")
}
for row in matrix {
builder.write_string(serialize_vector(row, policy))
builder.write_string("\n")
}
builder.to_string()
}
///|
/// Serializes an estimate into a stable key-value line.
pub fn serialize_estimate(estimate : Estimate) -> String {
let builder = StringBuilder::new()
builder.write_string("estimand=")
builder.write_string(estimate.estimand)
builder.write_string(",estimate=")
builder.write_string(estimate.estimate.to_string())
builder.write_string(",standard_error=")
builder.write_string(estimate.standard_error.to_string())
builder.write_string(",lower=")
builder.write_string(estimate.lower.to_string())
builder.write_string(",upper=")
builder.write_string(estimate.upper.to_string())
builder.to_string()
}
///|
/// Creates a quality report section.
pub fn quality_report_section(quality : DatasetQuality) -> ReportSection {
let lines : Array[String] = Array::new()
lines.push("rows=\{quality.rows}")
lines.push("columns=\{quality.columns}")
lines.push("complete_rows=\{quality.valid_rows}")
lines.push("missing_cells=\{quality.missing_cells}")
lines.push("score=\{quality.score}")
let rows : Array[Array[Double]] = [quality_summary_vector(quality)]
{
title: "data-quality",
status: if quality_gate(quality) {
"passed"
} else {
"warning"
},
lines,
fingerprint: matrix_checksum(rows),
}
}
///|
/// Creates a pipeline report section.
pub fn pipeline_report_section(result : PipelineResult) -> ReportSection {
let lines : Array[String] = Array::new()
lines.push("estimate=\{result.estimate.estimate}")
lines.push("standard_error=\{result.estimate.standard_error}")
lines.push("score=\{result.score}")
lines.push("passes=\{result.passes}")
{
title: "causal-pipeline",
status: if result.passes {
"passed"
} else {
"warning"
},
lines,
fingerprint: pipeline_fingerprint(result),
}
}
///|
/// Serializes a report section.
pub fn serialize_report_section(section : ReportSection) -> String {
let builder = StringBuilder::new()
builder.write_string("[")
builder.write_string(section.title)
builder.write_string("] ")
builder.write_string(section.status)
builder.write_string("\n")
for line in section.lines {
builder.write_string(line)
builder.write_string("\n")
}
builder.write_string("fingerprint=")
builder.write_string(section.fingerprint.to_string())
builder.to_string()
}
///|
/// Serializes a report composed of sections.
pub fn serialize_report(sections : Array[ReportSection]) -> String {
let builder = StringBuilder::new()
for section in sections {
builder.write_string(serialize_report_section(section))
builder.write_string("\n")
}
builder.to_string()
}
///|
/// Returns a compact report fingerprint.
pub fn report_fingerprint(sections : Array[ReportSection]) -> UInt64 {
let rows : Array[Array[Double]] = Array::new()
for section in sections {
rows.push([
section.lines.length().to_double(),
section.fingerprint.to_double(),
])
}
matrix_checksum(rows)
}