Repository navigation
Expand file tree
/
Copy pathcompare_output.py
More file actions
executable file
·128 lines (100 loc) · 4 KB
/
Copy pathcompare_output.py
File metadata and controls
executable file
·128 lines (100 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
"""Compare CSV files in two directories and report differences.
Script takes two paths, each usually containing 3000 CSV files, then compares
each file, find missing and new files, then prints a summary.
"""
import argparse
import sys
from lib.compare_files import CompareFiles, SummaryStats
def existing_directory(value):
"""Argparse type that validates *value* is an existing directory."""
from pathlib import Path
path = Path(value).expanduser().resolve()
if not path.is_dir():
raise argparse.ArgumentTypeError(f"Directory does not exist: {value}")
return str(path)
def non_negative_int(value):
"""Argparse type that ensures *value* is a non-negative integer."""
try:
parsed = int(value)
except ValueError as exc:
raise argparse.ArgumentTypeError("--max-files must be an integer") from exc
if parsed < 0:
raise argparse.ArgumentTypeError("--max-files must be non-negative")
return parsed
def main(argv):
parser = argparse.ArgumentParser(
description="Compare CSV files between two directories.",
)
parser.add_argument(
"old_dir",
type=existing_directory,
help="Path to the directory with the original CSV files",
)
parser.add_argument(
"new_dir",
type=existing_directory,
help="Path to the directory with the updated CSV files",
)
parser.add_argument(
"--max-files",
type=non_negative_int,
default=None,
metavar="N",
help="Limit comparison to the first N files (after sorting by name).",
)
args = parser.parse_args(argv)
old_dir = args.old_dir
new_dir = args.new_dir
comparer = CompareFiles()
old_files = comparer.list_csv_files(old_dir)
new_files = comparer.list_csv_files(new_dir)
old_names = set(old_files)
new_names = set(new_files)
missing_in_new = sorted(old_names - new_names)
extra_in_new = sorted(new_names - old_names)
for name in missing_in_new:
print(f"Missing in new directory: {name}")
for name in extra_in_new:
print(f"Extra file in new directory: {name}")
intersection = sorted(old_names & new_names)
if args.max_files is not None:
intersection = intersection[: args.max_files]
summary = SummaryStats()
summary.total_files = len(old_names)
summary.compared_files = len(intersection)
summary.missing_in_new = len(missing_in_new)
summary.extra_in_new = len(extra_in_new)
for name in intersection:
stats = comparer.compare_one_file(old_files[name], new_files[name])
added_pct = stats.percentage(stats.added)
deleted_pct = stats.percentage(stats.deleted)
changed_pct = stats.percentage(stats.changed)
print(
f"{name}: "
f"added {stats.added} ({added_pct:.2f}%), "
f"deleted {stats.deleted} ({deleted_pct:.2f}%), "
f"changed {stats.changed} ({changed_pct:.2f}%)"
)
summary.totals.added += stats.added
summary.totals.deleted += stats.deleted
summary.totals.changed += stats.changed
summary.totals.old_lines += stats.old_lines
summary.totals.new_lines += stats.new_lines
totals = summary.totals
added_pct = totals.percentage(totals.added)
deleted_pct = totals.percentage(totals.deleted)
changed_pct = totals.percentage(totals.changed)
print("\nSummary:")
print(f" Total files (old): {summary.total_files}")
print(f" Compared files: {summary.compared_files}")
print(f" Missing in new: {summary.missing_in_new}")
print(f" Extra in new: {summary.extra_in_new}")
print(f" Total lines (old): {totals.old_lines}")
print(f" Total lines (new): {totals.new_lines}")
print(f" Added lines: {totals.added} ({added_pct:.2f}%)")
print(f" Deleted lines: {totals.deleted} ({deleted_pct:.2f}%)")
print(f" Changed lines: {totals.changed} ({changed_pct:.2f}%)")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))