feat(news): index news packages and research persistent events
Extract reusable literal keywords once per source revision, assign complete news packages to long-lived events, and research all event members with immutable evidence and references. Add additive research storage, vocabulary snapshots, the two-stage workspace, isolated acceptance coverage, and a paused rollout with an initial fixed 50-news production trial.
This commit is contained in:
1 parent
07252fea0f
commit
4929a32a0a
69 files changed
+8215
-2092
No files matched your search
@@ -15,7 +15,7 @@ from alphapulse.core.db import get_session
|
||||
from alphapulse.core.redis import redis
|
||||
from alphapulse.crawler.events import NEWS_CHANNEL
|
||||
from alphapulse.models import NewsItem, NewsRevision
|
||||
from alphapulse.models.risk import ArticleAnalysis, NewsSummary, SummaryArticle
|
||||
from alphapulse.news_research.models import Package
|
||||
|
||||
router = APIRouter(prefix="/api/news", tags=["news"], dependencies=[Depends(require_admin)])
|
||||
|
||||
@@ -120,8 +120,8 @@ async def list_news(
|
||||
conditions.append(NewsItem.id.in_(ids))
|
||||
total = await session.scalar(select(func.count()).select_from(NewsItem).where(*conditions))
|
||||
rows = (await session.execute(
|
||||
select(NewsItem, func.left(NewsItem.content_text, EXCERPT_LENGTH).label("excerpt"), ArticleAnalysis)
|
||||
.outerjoin(ArticleAnalysis, (ArticleAnalysis.news_id == NewsItem.id) & (ArticleAnalysis.content_hash == NewsItem.content_hash))
|
||||
select(NewsItem, func.left(NewsItem.content_text, EXCERPT_LENGTH).label("excerpt"), Package)
|
||||
.outerjoin(Package, (Package.news_id == NewsItem.id) & (Package.content_hash == NewsItem.content_hash))
|
||||
.where(*conditions)
|
||||
.order_by(NewsItem.published_at.desc(), NewsItem.id.desc())
|
||||
.offset((page - 1) * limit)
|
||||
@@ -129,14 +129,8 @@ async def list_news(
|
||||
)).all()
|
||||
|
||||
items = []
|
||||
for news, excerpt, analysis in rows:
|
||||
summaries = (await session.scalars(select(NewsSummary).join(SummaryArticle).where(
|
||||
SummaryArticle.analysis_id == analysis.id, SummaryArticle.active.is_(True), NewsSummary.result.is_not(None)).order_by(NewsSummary.stars.desc()))).all() if analysis else []
|
||||
risk = None
|
||||
if analysis:
|
||||
risk = {'state': 'pending' if analysis.state == 'done' else analysis.state}
|
||||
if summaries:
|
||||
risk = {**summaries[0].result, 'stars': summaries[0].stars, 'state': 'done'}
|
||||
for news, excerpt, package in rows:
|
||||
risk = {'state': package.state} if package else None
|
||||
items.append(NewsBrief.model_validate({**news.__dict__, 'excerpt': excerpt, 'risk': risk}))
|
||||
return success(NewsPage(items=items, total=total))
|
||||
|
||||
|
||||
Reference in new issue
Block a user