This commit is contained in:
Concept Agent
2026-05-15 18:02:38 +02:00
parent 72016728e2
commit 307e452251
175 changed files with 9316 additions and 0 deletions
+226
View File
@@ -0,0 +1,226 @@
"""
Lahr Carpet Cleaning — Location page generator.
Creates /locations/<slug>/index.html for each city.
Run: python3 tools/gen-locations.py
"""
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOC_DIR = os.path.join(BASE_DIR, "locations")
CITIES = [
{"name": "Waterloo", "slug": "waterloo-ny", "county": "Seneca County", "note": "Our home base. Fastest response times in the area."},
{"name": "Geneva", "slug": "geneva-ny", "county": "Ontario County", "note": "Full residential and commercial services throughout Geneva."},
{"name": "Seneca Falls", "slug": "seneca-falls-ny", "county": "Seneca County", "note": "Serving homes, vacation rentals, and businesses in Seneca Falls."},
{"name": "Canandaigua", "slug": "canandaigua-ny", "county": "Ontario County", "note": "Lakefront homes, rentals, and businesses along Canandaigua Lake."},
{"name": "Penn Yan", "slug": "penn-yan-ny", "county": "Yates County", "note": "Homes, wineries, and short-term rentals in the Penn Yan area."},
{"name": "Newark", "slug": "newark-ny", "county": "Wayne County", "note": "Carpet and upholstery cleaning for homes and businesses in Newark."},
{"name": "Clifton Springs", "slug": "clifton-springs-ny", "county": "Ontario County", "note": "Residential and commercial cleaning throughout Clifton Springs."},
{"name": "Lodi", "slug": "lodi-ny", "county": "Seneca County", "note": "Serving homes and vacation properties in Lodi and surrounding areas."},
{"name": "Himrod", "slug": "himrod-ny", "county": "Yates County", "note": "Carpet and floor cleaning for homes and rentals in the Himrod area."},
{"name": "Phelps", "slug": "phelps-ny", "county": "Ontario County", "note": "Residential carpet and upholstery cleaning throughout Phelps."},
{"name": "Shortsville", "slug": "shortsville-ny", "county": "Ontario County", "note": "Home and business cleaning services in Shortsville, NY."},
{"name": "Victor", "slug": "victor-ny", "county": "Ontario County", "note": "Residential and commercial carpet cleaning throughout Victor."},
{"name": "Naples", "slug": "naples-ny", "county": "Ontario County", "note": "Serving homes, wineries, and vacation rentals in the Naples area."},
{"name": "Gorham", "slug": "gorham-ny", "county": "Ontario County", "note": "Carpet and floor cleaning for homes and properties in Gorham."},
{"name": "Manchester", "slug": "manchester-ny", "county": "Ontario County", "note": "Residential and commercial cleaning services in Manchester, NY."},
{"name": "Ovid", "slug": "ovid-ny", "county": "Seneca County", "note": "Serving homes and rental properties throughout Ovid."},
{"name": "Clyde", "slug": "clyde-ny", "county": "Wayne County", "note": "Carpet, upholstery, and floor cleaning for homes and businesses in Clyde."},
{"name": "Farmington", "slug": "farmington-ny", "county": "Ontario County", "note": "Residential and commercial carpet cleaning throughout Farmington."},
{"name": "East Bloomfield", "slug": "east-bloomfield-ny", "county": "Ontario County", "note": "Serving homes and properties in East Bloomfield and surrounding areas."},
{"name": "Rushville", "slug": "rushville-ny", "county": "Yates County", "note": "Carpet and upholstery cleaning for homes and rentals in Rushville."},
{"name": "Finger Lakes", "slug": "finger-lakes-ny", "county": "Region", "note": "Serving vacation rentals, wineries, and homes across the Finger Lakes region."},
]
SERVICES = [
{"name": "Carpet Cleaning", "slug": "/services/carpet-cleaning/", "img": "/assets/images/services/carpet-cleaning.jpg", "sub": "In-Home Service", "desc": "Hot water extraction removes deep-seated dirt, allergens, and stains from carpet fibers throughout your home."},
{"name": "Stairs Cleaning", "slug": "/services/stairs/", "img": "/assets/images/services/stairs-cleaning.jpg", "sub": "Step by Step", "desc": "Stairs collect more dirt per square inch than any flat surface. We clean every tread, riser, and landing."},
{"name": "Upholstery Cleaning","slug": "/services/upholstery/", "img": "/assets/images/services/upholstery-cleaning.jpg","sub": "Furniture Refresh", "desc": "Safe, effective cleaning for sofas, chairs, and mattresses. We work with all fabric types and leave no residue."},
{"name": "Floor Cleaning", "slug": "/services/floors/", "img": "/assets/images/services/floor-cleaning.jpg", "sub": "Hard Surface Care", "desc": "Wood floor cleaning and tile and grout restoration that brings hard surfaces back to their original condition."},
{"name": "Area Rug Cleaning", "slug": "/services/area-rugs/", "img": "/assets/images/services/area-rug-cleaning.jpg", "sub": "Delicate Care", "desc": "Gentle, specialized cleaning for oriental, Persian, and delicate rugs that restores color and removes embedded dirt."},
{"name": "Add-On Services", "slug": "/services/add-ons/", "img": "/assets/images/services/add-ons.jpg", "sub": "Extra Care", "desc": "Furniture moving, pet hair removal, odor treatment, and heavily soiled area care available alongside any service."},
]
HERO_IMAGES = [
"/assets/images/hero/hero-living-room.jpg",
"/assets/images/hero/hero-clean-result.jpg",
"/assets/images/hero/hero-technician.jpg",
"/assets/images/hero/hero-before-after.jpg",
"/assets/images/hero/hero-stairs.jpg",
]
def service_card(svc, city_name):
accent_word, rest = svc["name"].split(" ", 1) if " " in svc["name"] else (svc["name"], "")
h3 = f'<span class="text-accent">{accent_word}</span> {rest}'.strip()
return f""" <div class="service-card">
<div class="service-image"><img src="{svc['img']}" alt="{svc['name']} in {city_name}, NY"></div>
<h3 class="heading-3">{h3}</h3>
<div class="text-block-4">{svc['sub']}</div>
<p class="paragraph-2">{svc['desc']}</p>
<a href="{svc['slug']}" class="btn btn-primary">Learn More</a>
</div>"""
def page_html(city, idx):
hero_img = HERO_IMAGES[idx % len(HERO_IMAGES)]
cards = "\n".join(service_card(s, city["name"]) for s in SERVICES)
name = city["name"]
county = city["county"]
note = city["note"]
slug = city["slug"]
return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carpet Cleaning in {name}, NY | Lahr Carpet Cleaning</title>
<meta name="description" content="Professional carpet, upholstery, and floor cleaning in {name}, NY. Lahr Carpet Cleaning serves {county} and the Finger Lakes region. Call 315-719-1218.">
<link rel="stylesheet" href="/assets/css/styles.css?v=5">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<header class="header" id="header">
<div class="container"><div id="site-nav"></div></div>
</header>
<section class="page-hero" style="background-image: url('{hero_img}');">
<div class="container">
<div class="page-hero-content">
<span class="hero-eyebrow">{county} &mdash; Finger Lakes</span>
<h1 class="hero-title">{name},<br><span class="text-accent">NY</span></h1>
<p>{note}</p>
<div class="hero-actions">
<a href="/contact/" class="btn btn-primary btn-large">Book Now</a>
<a href="tel:315-719-1218" class="btn btn-ghost btn-large"><i class="fas fa-phone"></i> 315-719-1218</a>
</div>
</div>
</div>
</section>
<section class="services-overview">
<div class="container">
<div class="section-header">
<span class="section-label">{name}, NY</span>
<h2 class="section-title">Services in {name}</h2>
<p class="section-subtitle">We serve {name} and the surrounding {county} communities. Call to confirm availability for your address.</p>
</div>
<div class="services-grid">
{cards}
</div>
</div>
</section>
<section class="cta-banner">
<div class="cta-content">
<h2 class="heading-4"><strong>Serving </strong><span class="text-accent"><strong>{name}</strong></span></h2>
<p class="paragraph-4">Call 315-719-1218 or submit the form for a free estimate in {name}, NY.</p>
<a href="/contact/" class="btn btn-primary">Get a Free Estimate</a>
</div>
</section>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
"""
def locations_index():
city_cards = []
for i, city in enumerate(CITIES):
img = HERO_IMAGES[i % len(HERO_IMAGES)]
name = city["name"]
county = city["county"]
note = city["note"]
slug = city["slug"]
if " " in name:
accent_word, rest = name.split(" ", 1)
h3 = f'<span class="text-accent">{accent_word}</span> {rest}, NY'
else:
h3 = f'<span class="text-accent">{name}</span>, NY'
city_cards.append(f""" <div class="service-card">
<div class="service-image"><img src="{img}" alt="{name} NY"></div>
<h3 class="heading-3">{h3}</h3>
<div class="text-block-4">{county}</div>
<p class="paragraph-2">{note}</p>
<a href="/locations/{slug}/" class="btn btn-primary">View Services</a>
</div>""")
cards_html = "\n".join(city_cards)
return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Areas | Lahr Carpet Cleaning | Finger Lakes, NY</title>
<meta name="description" content="Lahr Carpet Cleaning serves Waterloo, Geneva, Seneca Falls, Canandaigua, Penn Yan, and 16 more cities across the Finger Lakes region. Call 315-719-1218.">
<link rel="stylesheet" href="/assets/css/styles.css?v=5">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<header class="header" id="header">
<div class="container"><div id="site-nav"></div></div>
</header>
<section class="page-hero" style="background-image: url('/assets/images/hero/hero-technician.jpg');">
<div class="container">
<div class="page-hero-content">
<span class="hero-eyebrow">Finger Lakes Region</span>
<h1 class="hero-title">Service<br><span class="text-accent">Areas</span></h1>
<p>We clean carpets, upholstery, rugs, and hard floors across 21 cities in Upstate New York. Select your city below.</p>
<div class="hero-actions">
<a href="/contact/" class="btn btn-primary btn-large">Book Now</a>
<a href="tel:315-719-1218" class="btn btn-ghost btn-large"><i class="fas fa-phone"></i> 315-719-1218</a>
</div>
</div>
</div>
</section>
<section class="services-overview">
<div class="container">
<div class="section-header">
<span class="section-label">Where We Work</span>
<h2 class="section-title">Cities We Serve</h2>
<p class="section-subtitle">Based in Waterloo, NY. We travel throughout Seneca, Ontario, Yates, Wayne, and Cayuga counties.</p>
</div>
<div class="services-grid">
{cards_html}
</div>
</div>
</section>
<section class="cta-banner">
<div class="cta-content">
<h2 class="heading-4"><strong>Not sure if we cover your area?</strong></h2>
<p class="paragraph-4">Call 315-719-1218 or submit the form and we will confirm availability for your address.</p>
<a href="/contact/" class="btn btn-primary">Get a Free Estimate</a>
</div>
</section>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
"""
if __name__ == "__main__":
# Write locations index
with open(os.path.join(LOC_DIR, "index.html"), "w") as f:
f.write(locations_index())
print("Wrote locations/index.html")
# Write each city page
for i, city in enumerate(CITIES):
city_dir = os.path.join(LOC_DIR, city["slug"])
os.makedirs(city_dir, exist_ok=True)
out_path = os.path.join(city_dir, "index.html")
with open(out_path, "w") as f:
f.write(page_html(city, i))
print(f"Wrote locations/{city['slug']}/index.html")
print(f"\nDone. {len(CITIES)} city pages + index generated.")