| 110 | |
| 111 | |
| 112 | def read_csv(csv_path: Path) -> List[PageRow]: |
| 113 | rows: List[PageRow] = [] |
| 114 | with csv_path.open("r", newline="") as f: |
| 115 | reader = csv.DictReader(f) |
| 116 | for r in reader: |
| 117 | try: |
| 118 | filename = r["filename"] |
| 119 | page_number = int(r["page_number"]) if r["page_number"] else -1 |
| 120 | elapsed_sec = float(r["elapsed_sec"]) if r["elapsed_sec"] else math.nan |
| 121 | success = str(r["success"]).strip() in {"1", "true", "True"} |
| 122 | except Exception: |
| 123 | # Skip malformed row |
| 124 | continue |
| 125 | rows.append(PageRow(filename, page_number, elapsed_sec, success)) |
| 126 | return rows |
| 127 | |
| 128 | |
| 129 | def per_document_aggregates(rows: List[PageRow]) -> List[Tuple[str, int, float]]: |