117 lines
3.7 KiB
Python
117 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
build_locations.py — Location page generator for floorithardwoodfloors.com
|
|
Reads data/locations.json + locations/_template.html
|
|
Outputs flat .html files: locations/{slug}.html
|
|
|
|
Usage:
|
|
python3 build_locations.py
|
|
python3 build_locations.py --slug buffalo # rebuild one city only
|
|
python3 build_locations.py --list # list all cities in database
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
SITE_ROOT = Path(__file__).parent
|
|
DATA_FILE = SITE_ROOT / "data" / "locations.json"
|
|
TEMPLATE_FILE = SITE_ROOT / "locations" / "_template.html"
|
|
OUTPUT_DIR = SITE_ROOT / "locations"
|
|
|
|
|
|
def build_faq_html(faqs: list) -> str:
|
|
"""Render FAQ list to HTML accordion items."""
|
|
items = []
|
|
for faq in faqs:
|
|
items.append(f""" <div class="faq-item">
|
|
<div class="faq-question">
|
|
<h4>{faq['q']}</h4>
|
|
<div class="faq-icon" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="faq-answer">
|
|
<div class="faq-answer-inner">{faq['a']}</div>
|
|
</div>
|
|
</div>""")
|
|
return "\n".join(items)
|
|
|
|
|
|
def render_page(location: dict, template: str) -> str:
|
|
"""Stamp all {{variable}} placeholders in template with location data."""
|
|
html = template
|
|
|
|
# Render complex fields first
|
|
html = html.replace("{{faq_items}}", build_faq_html(location.get("faqs", [])))
|
|
|
|
# Stamp all scalar fields
|
|
for key, value in location.items():
|
|
if isinstance(value, str):
|
|
html = html.replace(f"{{{{{key}}}}}", value)
|
|
|
|
return html
|
|
|
|
|
|
def build_one(location: dict, template: str, verbose: bool = True) -> Path:
|
|
"""Generate one location page. Returns output path."""
|
|
slug = location["slug"]
|
|
city = location.get("city", slug)
|
|
output_path = OUTPUT_DIR / f"{slug}.html"
|
|
|
|
html = render_page(location, template)
|
|
output_path.write_text(html, encoding="utf-8")
|
|
|
|
if verbose:
|
|
print(f" Built: locations/{slug}.html ({city}, {location.get('state','NY')})")
|
|
|
|
return output_path
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Build flat location HTML pages from JSON database.")
|
|
parser.add_argument("--slug", help="Build a single city by slug (e.g. buffalo)")
|
|
parser.add_argument("--list", action="store_true", help="List all cities in database")
|
|
args = parser.parse_args()
|
|
|
|
# Validate paths
|
|
if not DATA_FILE.exists():
|
|
print(f"ERROR: data file not found: {DATA_FILE}")
|
|
sys.exit(1)
|
|
if not TEMPLATE_FILE.exists():
|
|
print(f"ERROR: template not found: {TEMPLATE_FILE}")
|
|
sys.exit(1)
|
|
|
|
locations = json.loads(DATA_FILE.read_text(encoding="utf-8"))
|
|
template = TEMPLATE_FILE.read_text(encoding="utf-8")
|
|
|
|
if args.list:
|
|
print(f"\nLocations in database ({len(locations)} total):")
|
|
for loc in locations:
|
|
print(f" {loc['slug']:20s} {loc.get('city','')}, {loc.get('state','NY')}")
|
|
return
|
|
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
if args.slug:
|
|
matches = [l for l in locations if l["slug"] == args.slug]
|
|
if not matches:
|
|
print(f"ERROR: slug '{args.slug}' not found in database.")
|
|
sys.exit(1)
|
|
print(f"\nBuilding single location: {args.slug}")
|
|
build_one(matches[0], template)
|
|
else:
|
|
print(f"\nBuilding {len(locations)} location pages...")
|
|
built = []
|
|
for loc in locations:
|
|
path = build_one(loc, template)
|
|
built.append(path)
|
|
print(f"\nDone. {len(built)} pages written to locations/")
|
|
|
|
print(f"Completed at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|