fix(content): slugify transliteration + article links + country overview ranking

- Add @slugify SQLMesh macro (STRIP_ACCENTS + ß→ss) replacing broken
  inline REGEXP_REPLACE that dropped non-ASCII chars (Düsseldorf → d-sseldorf)
- Apply @slugify to dim_venues, dim_cities, dim_locations
- Fix Python slugify() to pre-replace ß→ss before NFKD normalization
- Add language prefix to B2B article market links (/markets/germany → /de/markets/germany)
- Change country overview top-5 ranking: venue count (not raw market_score)
  for top cities, population for top opportunity cities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-03-03 10:46:30 +01:00
parent 0fc0ca66b1
commit a00c8727d7
8 changed files with 33 additions and 14 deletions

View File

@@ -20,15 +20,15 @@ SELECT
SUM(padel_venue_count) AS total_venues,
ROUND(AVG(market_score), 1) AS avg_market_score,
MAX(market_score) AS top_city_market_score,
-- Top 5 cities by market score for internal linking (DuckDB list slice syntax)
LIST(city_slug ORDER BY market_score DESC NULLS LAST)[1:5] AS top_city_slugs,
LIST(city_name ORDER BY market_score DESC NULLS LAST)[1:5] AS top_city_names,
-- Top 5 cities by venue count (prominence), then score for internal linking
LIST(city_slug ORDER BY padel_venue_count DESC, market_score DESC NULLS LAST)[1:5] AS top_city_slugs,
LIST(city_name ORDER BY padel_venue_count DESC, market_score DESC NULLS LAST)[1:5] AS top_city_names,
-- Opportunity score aggregates (NULL-safe: cities without geoname_id match excluded from AVG)
ROUND(AVG(opportunity_score), 1) AS avg_opportunity_score,
MAX(opportunity_score) AS top_opportunity_score,
-- Top 5 cities by opportunity score (may differ from top market score cities)
LIST(city_slug ORDER BY opportunity_score DESC NULLS LAST)[1:5] AS top_opportunity_slugs,
LIST(city_name ORDER BY opportunity_score DESC NULLS LAST)[1:5] AS top_opportunity_names,
-- Top 5 opportunity cities by population (prominence), then opportunity score
LIST(city_slug ORDER BY population DESC, opportunity_score DESC NULLS LAST)[1:5] AS top_opportunity_slugs,
LIST(city_name ORDER BY population DESC, opportunity_score DESC NULLS LAST)[1:5] AS top_opportunity_names,
-- Pricing medians across cities (NULL when no Playtomic coverage in country)
ROUND(MEDIAN(median_hourly_rate), 0) AS median_hourly_rate,
ROUND(MEDIAN(median_peak_rate), 0) AS median_peak_rate,