Skip to content

Commit a4df950

Browse files
jeremymanningclaude
andcommitted
Add master build, validation, and pre-push check scripts
- build.py: Master script to build all HTML pages - validate_data.py: Data validation (required fields, URLs, images) - pre_push_check.py: Combined validation + build check - utils.py: Added load_spreadsheet_all_sheets function All 76 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent d8cebf7 commit a4df950

File tree

4 files changed

+429
-0
lines changed

4 files changed

+429
-0
lines changed

scripts/build.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python3
2+
"""Master build script for the Context Lab website.
3+
4+
Generates all HTML pages from spreadsheet data and templates.
5+
Run this script to regenerate all content pages.
6+
"""
7+
import sys
8+
from pathlib import Path
9+
10+
from build_publications import build_publications
11+
from build_people import build_people
12+
from build_software import build_software
13+
14+
15+
def main():
16+
"""Build all content pages."""
17+
project_root = Path(__file__).parent.parent
18+
19+
data_dir = project_root / 'data'
20+
templates_dir = project_root / 'templates'
21+
22+
# Track build results
23+
results = []
24+
25+
# Build publications.html
26+
try:
27+
build_publications(
28+
data_dir / 'publications.xlsx',
29+
templates_dir / 'publications.html',
30+
project_root / 'publications.html'
31+
)
32+
results.append(('publications.html', 'OK'))
33+
except Exception as e:
34+
results.append(('publications.html', f'FAILED: {e}'))
35+
36+
# Build people.html
37+
try:
38+
build_people(
39+
data_dir / 'people.xlsx',
40+
templates_dir / 'people.html',
41+
project_root / 'people.html'
42+
)
43+
results.append(('people.html', 'OK'))
44+
except Exception as e:
45+
results.append(('people.html', f'FAILED: {e}'))
46+
47+
# Build software.html
48+
try:
49+
build_software(
50+
data_dir / 'software.xlsx',
51+
templates_dir / 'software.html',
52+
project_root / 'software.html'
53+
)
54+
results.append(('software.html', 'OK'))
55+
except Exception as e:
56+
results.append(('software.html', f'FAILED: {e}'))
57+
58+
# Print summary
59+
print("\n" + "=" * 50)
60+
print("Build Summary")
61+
print("=" * 50)
62+
63+
failed = 0
64+
for page, status in results:
65+
print(f" {page}: {status}")
66+
if status != 'OK':
67+
failed += 1
68+
69+
print("=" * 50)
70+
71+
if failed > 0:
72+
print(f"Build completed with {failed} error(s)")
73+
sys.exit(1)
74+
else:
75+
print("Build completed successfully!")
76+
sys.exit(0)
77+
78+
79+
if __name__ == '__main__':
80+
main()

scripts/pre_push_check.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python3
2+
"""Pre-push check script for the Context Lab website.
3+
4+
Validates data files and rebuilds all HTML pages.
5+
This should be run before pushing to ensure consistency.
6+
"""
7+
import subprocess
8+
import sys
9+
from pathlib import Path
10+
11+
12+
def run_script(script_name: str) -> bool:
13+
"""Run a Python script and return True if successful."""
14+
script_path = Path(__file__).parent / script_name
15+
print(f"\n{'=' * 50}")
16+
print(f"Running {script_name}...")
17+
print('=' * 50)
18+
19+
result = subprocess.run(
20+
[sys.executable, str(script_path)],
21+
cwd=Path(__file__).parent.parent
22+
)
23+
return result.returncode == 0
24+
25+
26+
def main():
27+
"""Run all pre-push checks."""
28+
print("Context Lab Website Pre-Push Check")
29+
print("=" * 50)
30+
31+
all_passed = True
32+
33+
# Step 1: Validate data
34+
if not run_script('validate_data.py'):
35+
print("\n*** Data validation FAILED ***")
36+
print("Fix validation errors before pushing.")
37+
all_passed = False
38+
39+
# Step 2: Build all pages
40+
if all_passed:
41+
if not run_script('build.py'):
42+
print("\n*** Build FAILED ***")
43+
print("Fix build errors before pushing.")
44+
all_passed = False
45+
46+
# Summary
47+
print("\n" + "=" * 50)
48+
print("Pre-Push Check Summary")
49+
print("=" * 50)
50+
51+
if all_passed:
52+
print("All checks PASSED!")
53+
print("\nYou can safely push your changes.")
54+
sys.exit(0)
55+
else:
56+
print("Some checks FAILED!")
57+
print("\nPlease fix the issues before pushing.")
58+
sys.exit(1)
59+
60+
61+
if __name__ == '__main__':
62+
main()

scripts/utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,48 @@ def load_spreadsheet(filepath: Path) -> List[Dict[str, Any]]:
5151
return rows
5252

5353

54+
def load_spreadsheet_all_sheets(filepath: Path) -> Dict[str, List[Dict[str, Any]]]:
55+
"""Load Excel spreadsheet with all sheets.
56+
57+
Args:
58+
filepath: Path to the .xlsx file
59+
60+
Returns:
61+
Dictionary with sheet names as keys, each containing list of row dicts.
62+
Empty cells are converted to empty strings.
63+
64+
Raises:
65+
FileNotFoundError: If the spreadsheet doesn't exist
66+
"""
67+
wb = openpyxl.load_workbook(filepath, read_only=True, data_only=True)
68+
69+
data = {}
70+
for sheet_name in wb.sheetnames:
71+
sheet = wb[sheet_name]
72+
73+
# Get headers from first row
74+
headers = [cell.value for cell in sheet[1]]
75+
76+
rows = []
77+
for row in sheet.iter_rows(min_row=2, values_only=True):
78+
# Skip completely empty rows
79+
if not any(cell is not None for cell in row):
80+
continue
81+
82+
row_dict = {}
83+
for header, value in zip(headers, row):
84+
if value is None:
85+
row_dict[header] = ''
86+
else:
87+
row_dict[header] = value
88+
rows.append(row_dict)
89+
90+
data[sheet_name] = rows
91+
92+
wb.close()
93+
return data
94+
95+
5496
def inject_content(template_path: Path, output_path: Path,
5597
replacements: Dict[str, str]) -> None:
5698
"""Inject generated content into template at marker locations.

0 commit comments

Comments
 (0)