Coverage for oc_meta / run / meta / merge_csv.py: 100%
46 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-25 10:39 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-25 10:39 +0000
1#!/usr/bin/python
3# SPDX-FileCopyrightText: 2026 Arcangelo Massari <arcangelo.massari@unibo.it>
4#
5# SPDX-License-Identifier: ISC
7from __future__ import annotations
9import argparse
10import os
11from typing import List
13from rich.table import Table
14from rich_argparse import RichHelpFormatter
16from oc_meta.lib.console import console, create_progress
17from oc_meta.lib.file_manager import get_csv_data, write_csv
20def get_csv_files(directory: str) -> List[str]:
21 if not os.path.isdir(directory):
22 raise ValueError("The specified path '{}' is not a directory".format(directory))
23 return [
24 os.path.join(directory, f) for f in os.listdir(directory) if f.endswith(".csv")
25 ]
28def resolve_output_path(output: str) -> str:
29 if output.endswith(".csv"):
30 parent_dir = os.path.dirname(output)
31 if parent_dir:
32 os.makedirs(parent_dir, exist_ok=True)
33 return output
34 os.makedirs(output, exist_ok=True)
35 return os.path.join(output, "merged.csv")
38def merge_csv_files(input_dir: str, output_path: str) -> tuple[int, int, List[str]]:
39 csv_files = get_csv_files(input_dir)
40 if not csv_files:
41 return 0, 0, []
43 all_rows: List[dict[str, str]] = []
44 fieldnames: List[str] = []
46 with create_progress() as progress:
47 task = progress.add_task("Merging CSV files", total=len(csv_files))
49 for csv_file in csv_files:
50 data = get_csv_data(csv_file, clean_data=False)
51 if data:
52 if not fieldnames:
53 fieldnames = list(data[0].keys())
54 all_rows.extend(data)
55 progress.advance(task)
57 if all_rows:
58 write_csv(output_path, all_rows, fieldnames=fieldnames)
60 return len(all_rows), len(csv_files), fieldnames
63def print_merge_report(total_rows: int, files_processed: int, output_path: str) -> None:
64 table = Table(title="Merge report")
65 table.add_column("Metric", style="cyan")
66 table.add_column("Value", style="green")
68 table.add_row("Files merged", str(files_processed))
69 table.add_row("Total rows", str(total_rows))
70 table.add_row("Output file", output_path)
72 console.print(table)
75def main() -> int: # pragma: no cover
76 parser = argparse.ArgumentParser(
77 description="Merge multiple CSV files into a single file",
78 formatter_class=RichHelpFormatter,
79 )
80 parser.add_argument("input_dir", help="Directory containing input CSV files")
81 parser.add_argument(
82 "output",
83 help="Output path: if ends with .csv, creates that file; otherwise, creates merged.csv in that folder",
84 )
85 args = parser.parse_args()
87 if not os.path.isdir(args.input_dir):
88 console.print(
89 "[red]Input directory does not exist: {}[/red]".format(args.input_dir)
90 )
91 return 1
93 output_path = resolve_output_path(args.output)
95 csv_files = get_csv_files(args.input_dir)
96 if not csv_files:
97 console.print(
98 "[red]No CSV files found in directory: {}[/red]".format(args.input_dir)
99 )
100 return 1
102 console.print("Found [green]{}[/green] CSV files to merge".format(len(csv_files)))
104 total_rows, files_processed, _ = merge_csv_files(args.input_dir, output_path)
106 print_merge_report(total_rows, files_processed, output_path)
108 return 0
111if __name__ == "__main__": # pragma: no cover
112 main()