- Add RedditClient.fetch_stats() — fetches score/upvotes/comments/awards via by_id API - Add Store.list_posts_needing_poll() — selects successful Reddit posts not checked within recheck window - Add Store.list_posts() LEFT JOIN latest engagement snapshot (avoids N+1 on frontend) - Add app/services/engagement.py — poll_recent_posts() async service with unauthenticated fallback - Register hourly engagement poll job in APScheduler at startup - Add POST /posts/poll-engagement for manual triggers - Update Post interface with engagement fields (score, comment_count, awards, engagement_checked_at) - Add Score/Comments columns and poll button to PostsView Closes: #6
58 lines
1.7 KiB
Python
58 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from app.core.config import get_settings
|
|
from app.db.store import Store
|
|
from app.services.poster import post_campaign_to_sub
|
|
|
|
router = APIRouter(prefix="/posts", tags=["posts"])
|
|
|
|
|
|
def _in_thread(fn):
|
|
store = Store(get_settings().db_path)
|
|
try:
|
|
return fn(store)
|
|
finally:
|
|
store.close()
|
|
|
|
|
|
class PostToSub(BaseModel):
|
|
campaign_id: int
|
|
sub: str
|
|
|
|
|
|
@router.get("")
|
|
async def list_posts(campaign_id: int | None = None, target: str | None = None, limit: int = 50):
|
|
return await asyncio.to_thread(
|
|
_in_thread, lambda s: s.list_posts(campaign_id, target, limit)
|
|
)
|
|
|
|
|
|
@router.post("/trigger")
|
|
async def trigger_single_post(body: PostToSub):
|
|
"""Manually trigger a single post to one sub."""
|
|
campaign = await asyncio.to_thread(_in_thread, lambda s: s.get_campaign(body.campaign_id))
|
|
if campaign is None:
|
|
raise HTTPException(404, "Campaign not found")
|
|
return await post_campaign_to_sub(body.campaign_id, body.sub, triggered_by="manual")
|
|
|
|
|
|
@router.get("/{post_id}/engagement")
|
|
async def get_engagement(post_id: int):
|
|
result = await asyncio.to_thread(_in_thread, lambda s: s.get_latest_engagement(post_id))
|
|
if result is None:
|
|
raise HTTPException(404, "No engagement data for this post")
|
|
return result
|
|
|
|
|
|
@router.post("/poll-engagement")
|
|
async def poll_engagement():
|
|
"""Manually trigger an engagement poll for all recent posts."""
|
|
from app.services.engagement import poll_recent_posts
|
|
settings = get_settings()
|
|
result = await poll_recent_posts(settings.db_path)
|
|
return result
|