diff --git a/web/src/padelnomics/api.py b/web/src/padelnomics/api.py
index 37c620d..8db5bae 100644
--- a/web/src/padelnomics/api.py
+++ b/web/src/padelnomics/api.py
@@ -8,7 +8,7 @@ daily when the pipeline runs).
from quart import Blueprint, abort, jsonify
from .analytics import fetch_analytics
-from .core import is_flag_enabled
+from .core import fetch_all, is_flag_enabled
bp = Blueprint("api", __name__)
@@ -59,6 +59,20 @@ async def country_cities(country_slug: str):
""",
[country_slug],
)
+ # Check which cities have published articles (any language).
+ article_rows = await fetch_all(
+ """SELECT url_path FROM articles
+ WHERE url_path LIKE ? AND status = 'published'
+ AND published_at <= datetime('now')""",
+ (f"/markets/{country_slug}/%",),
+ )
+ article_slugs = set()
+ for a in article_rows:
+ parts = a["url_path"].rstrip("/").split("/")
+ if len(parts) >= 4:
+ article_slugs.add(parts[3])
+ for row in rows:
+ row["has_article"] = row["city_slug"] in article_slugs
return jsonify(rows), 200, _CACHE_HEADERS
diff --git a/web/src/padelnomics/static/js/article-maps.js b/web/src/padelnomics/static/js/article-maps.js
index 5824b27..57fa3b6 100644
--- a/web/src/padelnomics/static/js/article-maps.js
+++ b/web/src/padelnomics/static/js/article-maps.js
@@ -43,18 +43,23 @@
data.forEach(function(c) {
if (!c.lat || !c.lon) return;
var size = 10 + 36 * Math.sqrt((c.padel_venue_count || 1) / maxV);
- var color = scoreColor(c.market_score);
+ var hasArticle = c.has_article !== false;
+ var color = hasArticle ? scoreColor(c.market_score) : '#9CA3AF';
var pop = c.population >= 1000000
? (c.population / 1000000).toFixed(1) + 'M'
: (c.population >= 1000 ? Math.round(c.population / 1000) + 'K' : (c.population || ''));
var tip = '' + c.city_name + '
'
+ (c.padel_venue_count || 0) + ' venues'
- + (pop ? ' · ' + pop : '') + '
'
- + 'Score ' + Math.round(c.market_score) + '/100';
- L.marker([c.lat, c.lon], { icon: makeIcon(size, color) })
+ + (pop ? ' · ' + pop : '');
+ if (hasArticle) {
+ tip += '
Score ' + Math.round(c.market_score) + '/100';
+ }
+ var marker = L.marker([c.lat, c.lon], { icon: makeIcon(size, color) })
.bindTooltip(tip, { className: 'map-tooltip', direction: 'top', offset: [0, -Math.round(size / 2)] })
- .on('click', function() { window.location = '/' + lang + '/markets/' + slug + '/' + c.city_slug; })
.addTo(map);
+ if (hasArticle) {
+ marker.on('click', function() { window.location = '/' + lang + '/markets/' + slug + '/' + c.city_slug; });
+ }
bounds.push([c.lat, c.lon]);
});
if (bounds.length) map.fitBounds(bounds, { padding: [24, 24] });