fix(cms): open analytics DB in worker + fix per-language article upsert
Three bugs causing 0 articles after generation: 1. Worker never called open_analytics_db() — fetch_analytics always returned [] since _conn was None. Generation completed silently with 0 rows from DuckDB. 2. generate_articles upserted by url_path alone — after the URL prefix fix, all languages share the same url_path (e.g. /markets/de/berlin). The EN article was overwriting the DE article on every generation. Fixed: deduplicate on (url_path, language). 3. article_page and _filter_articles didn't filter by language — with shared url_paths a DE request could serve an EN article, and the markets hub would show duplicate entries per city. Fixed: both now filter by g.lang from the /<lang> blueprint prefix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -434,9 +434,11 @@ async def generate_articles(
|
|||||||
md_dir.mkdir(parents=True, exist_ok=True)
|
md_dir.mkdir(parents=True, exist_ok=True)
|
||||||
(md_dir / f"{article_slug}.md").write_text(body_md)
|
(md_dir / f"{article_slug}.md").write_text(body_md)
|
||||||
|
|
||||||
# Upsert article in SQLite
|
# Upsert article in SQLite — keyed by (url_path, language) since
|
||||||
|
# multiple languages share the same url_path
|
||||||
existing_article = await fetch_one(
|
existing_article = await fetch_one(
|
||||||
"SELECT id FROM articles WHERE url_path = ?", (url_path,),
|
"SELECT id FROM articles WHERE url_path = ? AND language = ?",
|
||||||
|
(url_path, lang),
|
||||||
)
|
)
|
||||||
if existing_article:
|
if existing_article:
|
||||||
await execute(
|
await execute(
|
||||||
@@ -444,8 +446,8 @@ async def generate_articles(
|
|||||||
SET title = ?, meta_description = ?, template_slug = ?,
|
SET title = ?, meta_description = ?, template_slug = ?,
|
||||||
language = ?, date_modified = ?, updated_at = ?,
|
language = ?, date_modified = ?, updated_at = ?,
|
||||||
seo_head = ?
|
seo_head = ?
|
||||||
WHERE url_path = ?""",
|
WHERE url_path = ? AND language = ?""",
|
||||||
(title, meta_desc, slug, lang, now_iso, now_iso, seo_head, url_path),
|
(title, meta_desc, slug, lang, now_iso, now_iso, seo_head, url_path, lang),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await execute(
|
await execute(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
from quart import Blueprint, abort, render_template, request
|
from quart import Blueprint, abort, g, render_template, request
|
||||||
|
|
||||||
from ..core import capture_waitlist_email, csrf_protect, feature_gate, fetch_all, fetch_one
|
from ..core import capture_waitlist_email, csrf_protect, feature_gate, fetch_all, fetch_one
|
||||||
from ..i18n import get_translations
|
from ..i18n import get_translations
|
||||||
@@ -172,11 +172,14 @@ async def market_results():
|
|||||||
|
|
||||||
|
|
||||||
async def _filter_articles(q: str, country: str, region: str) -> list[dict]:
|
async def _filter_articles(q: str, country: str, region: str) -> list[dict]:
|
||||||
"""Query published articles with optional FTS + country/region filters."""
|
"""Query published articles for the current language."""
|
||||||
|
lang = g.get("lang", "en")
|
||||||
if q:
|
if q:
|
||||||
# FTS query
|
# FTS query
|
||||||
wheres = ["articles_fts MATCH ?"]
|
wheres = ["articles_fts MATCH ?"]
|
||||||
params: list = [q]
|
params: list = [q]
|
||||||
|
wheres.append("a.language = ?")
|
||||||
|
params.append(lang)
|
||||||
if country:
|
if country:
|
||||||
wheres.append("a.country = ?")
|
wheres.append("a.country = ?")
|
||||||
params.append(country)
|
params.append(country)
|
||||||
@@ -194,8 +197,8 @@ async def _filter_articles(q: str, country: str, region: str) -> list[dict]:
|
|||||||
tuple(params),
|
tuple(params),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
wheres = ["status = 'published'", "published_at <= datetime('now')"]
|
wheres = ["status = 'published'", "published_at <= datetime('now')", "language = ?"]
|
||||||
params = []
|
params = [lang]
|
||||||
if country:
|
if country:
|
||||||
wheres.append("country = ?")
|
wheres.append("country = ?")
|
||||||
params.append(country)
|
params.append(country)
|
||||||
@@ -218,12 +221,13 @@ async def _filter_articles(q: str, country: str, region: str) -> list[dict]:
|
|||||||
async def article_page(url_path: str):
|
async def article_page(url_path: str):
|
||||||
"""Serve a published article by its url_path."""
|
"""Serve a published article by its url_path."""
|
||||||
clean_path = "/" + url_path.strip("/")
|
clean_path = "/" + url_path.strip("/")
|
||||||
|
lang = g.get("lang", "en")
|
||||||
|
|
||||||
article = await fetch_one(
|
article = await fetch_one(
|
||||||
"""SELECT * FROM articles
|
"""SELECT * FROM articles
|
||||||
WHERE url_path = ? AND status = 'published'
|
WHERE url_path = ? AND language = ? AND status = 'published'
|
||||||
AND published_at <= datetime('now')""",
|
AND published_at <= datetime('now')""",
|
||||||
(clean_path,),
|
(clean_path, lang),
|
||||||
)
|
)
|
||||||
if not article:
|
if not article:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|||||||
@@ -756,6 +756,9 @@ async def run_worker(poll_interval: float = 1.0) -> None:
|
|||||||
"""Main worker loop."""
|
"""Main worker loop."""
|
||||||
print("[WORKER] Starting...")
|
print("[WORKER] Starting...")
|
||||||
await init_db()
|
await init_db()
|
||||||
|
from .analytics import open_analytics_db
|
||||||
|
open_analytics_db()
|
||||||
|
print("[WORKER] Analytics DB opened.")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user