-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmakeCodeBook.py
More file actions
143 lines (106 loc) · 4.06 KB
/
Copy pathmakeCodeBook.py
File metadata and controls
143 lines (106 loc) · 4.06 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import argparse
import logging
import os
import re
import yaml
logger = logging.getLogger(__name__)
def make_codeBook(yaml_path):
"""
Reads a variable codebook YAML file and writes a plain text (.txt) report.
"""
if not os.path.exists(yaml_path):
raise FileNotFoundError(f"Could not find codebook file at '{yaml_path}'")
filename = os.path.basename(yaml_path)
pattern = r"codeBook_([a-zA-Z]+)_(\d+)\.yaml"
match = re.search(pattern, filename)
if match:
season = match.group(1)
year = match.group(2)
else:
season, year = None, None
with open(yaml_path, encoding="utf-8") as yf:
codebook = yaml.load(yf, Loader=yaml.SafeLoader)
if not codebook:
raise ValueError(
f"The codebook file at '{yaml_path}' is empty or could not be parsed."
)
report_lines = []
report_lines.append("=" * 70)
report_lines.append(f"{'PUBLIC USE FILE (PUF) CODEBOOK REPORT':^70}")
report_lines.append("=" * 70 + "\n")
for col, info in codebook.items():
fmt_name = info.get("format", " ")
var_label = info.get("description", " ")
report_lines.append(f"Variable Name: {col} (Format: {fmt_name})")
report_lines.append(f"Description: {var_label}")
report_lines.append("-" * 65)
report_lines.append(f"{'Code':<10} | {'Value Label':<35} | {'Frequency':<12}")
report_lines.append("-" * 65)
distributions = info.get("value_distributions", [])
if distributions:
for dist in distributions:
key_display = str(dist.get("code", ""))
label = str(dist.get("label", ""))
freq = dist.get("frequency", 0)
report_lines.append(f"{key_display:<10} | {label:<35} | {freq:<12,}")
report_lines.append("-" * 65)
q_numbers = info.get("question_numbers", [])
if q_numbers:
q_str = ", ".join(str(q) for q in q_numbers)
report_lines.append(f"Question(s): {q_str}")
note_keys = ["notes", "notes2", "notes3"]
found_notes = []
for nk in note_keys:
if info.get(nk):
found_notes.append(info[nk])
if found_notes:
report_lines.append("-" * 65)
report_lines.append("Notes:")
for index, note_content in enumerate(found_notes, start=1):
report_lines.append(f" [{index}] {note_content}")
report_lines.append("=" * 65 + "\n")
report_content = "\n".join(report_lines)
return report_content, (season, year)
def main():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
parser = argparse.ArgumentParser(
description=(
"Convert a generated Codebook YAML file into "
"a human-readable TXT Codebook report."
)
)
parser.add_argument(
"yaml_path",
help="Path to the target input YAML codebook file (default: %(default)s)",
)
parser.add_argument(
"-o",
"--output-dir",
default=".",
help=(
"Directory where the output TXT report should "
"be saved (default: current directory)"
),
)
args = parser.parse_args()
logger.info(f"Parsing YAML codebook file: {args.yaml_path}")
report_payload, metadata = make_codeBook(args.yaml_path)
season, year = metadata
if season and year:
output_filename = f"codebook_{season}_{year}.txt"
else:
output_filename = "codebook_report.txt"
output_path = os.path.abspath(os.path.join(args.output_dir, output_filename))
if os.path.exists(output_path):
logger.info(f"Overwriting existing file at: {output_path}")
os.remove(output_path)
logger.info("Writing text report payload to disk...")
with open(output_path, "w", encoding="utf-8") as f:
f.write(report_payload)
logger.info(f"Clean frequency report built successfully: {output_path}")
if __name__ == "__main__":
main()