///|
pub fn scan_tsv_source(source : String) -> TsvScanStats {
let mut total = 0
let mut blank = 0
let mut comments = 0
let mut data = 0
let raw_lines = source.split("\n").to_array()
for index, raw_line in raw_lines {
if index == raw_lines.length() - 1 && raw_line.is_empty() {
continue
}
total += 1
let line = raw_line.trim(chars=" \r\t")
if line.is_empty() {
blank += 1
} else if line.get_char(0) == Some('#') {
comments += 1
} else {
data += 1
}
}
{
total_lines: total,
blank_lines: blank,
comment_lines: comments,
data_lines: data,
}
}
///|
pub fn render_qrels_tsv(qrels : Array[JudgedDoc]) -> String {
let rows : Array[String] = []
let sorted = deduplicate_qrels(qrels)
for item in sorted {
rows.push("\{item.query_id}\t\{item.doc_id}\t\{item.relevance}")
}
rows.join("\n")
}
///|
pub fn render_run_tsv(run : Array[RetrievedDoc]) -> String {
let rows : Array[String] = []
let sorted = deduplicate_run(run)
for item in sorted {
rows.push("\{item.query_id}\t\{item.doc_id}\t\{item.score}")
}
rows.join("\n")
}
///|
pub fn render_pools_tsv(pools : Array[CandidatePool]) -> String {
let rows : Array[String] = []
let sorted = merge_candidate_pools(pools, [])
for pool in sorted {
for doc_id in pool.doc_ids {
rows.push("\{pool.query_id}\t\{doc_id}")
}
}
rows.join("\n")
}
///|
pub fn parse_qrels_tsv_strict(
source : String,
) -> Result[Array[JudgedDoc], String] {
let parsed = match parse_qrels_tsv(source) {
Ok(value) => value
Err(error) => return Err(error)
}
let summary = validate_dataset(parsed, [])
if summary.error_count > 0 {
Err(render_validation_report(summary))
} else {
Ok(deduplicate_qrels(parsed))
}
}
///|
pub fn parse_run_tsv_strict(
source : String,
) -> Result[Array[RetrievedDoc], String] {
let parsed = match parse_run_tsv(source) {
Ok(value) => value
Err(error) => return Err(error)
}
for item in parsed {
if item.score.is_nan() || item.score.is_inf() {
return Err("run contains a non-finite score".to_string())
}
}
Ok(deduplicate_run(parsed))
}