NewsAtlas/backend/app/clustering.py

49 lines
1.7 KiB
Python

import datetime as dt
from collections import defaultdict
from sqlalchemy import select
from sqlalchemy.orm import Session
from .config import settings
from .models import Article
from .textutil import topic_label as _topic_label
def build_clusters(session: Session) -> list[dict]:
cutoff = dt.datetime.utcnow() - dt.timedelta(hours=settings.article_window_hours)
rows = session.execute(
select(Article)
.where(Article.cluster_key.is_not(None))
.where(Article.published_at >= cutoff)
.order_by(Article.published_at.desc())
).scalars().all()
groups: dict[str, list[Article]] = defaultdict(list)
for article in rows:
groups[article.cluster_key].append(article)
clusters = []
for key, articles in groups.items():
lat = sum(a.lat for a in articles) / len(articles)
lon = sum(a.lon for a in articles) / len(articles)
sources = sorted({a.source for a in articles})
clusters.append(
{
"cluster_key": key,
"lat": lat,
"lon": lon,
"location_name": articles[0].location_name,
"country": articles[0].country,
"article_count": len(articles),
"source_count": len(sources),
"sources": sources,
"topic": _topic_label([a.title for a in articles]),
"latest_published_at": max(a.published_at for a in articles).isoformat(),
"headline": articles[0].title,
"article_ids": [a.id for a in articles],
}
)
clusters.sort(key=lambda c: c["article_count"], reverse=True)
return clusters