Python/FastAPI backend with PostgreSQL for collecting Reddit data via public .json endpoints. React/Vite dashboard for analytics. Docker Compose setup with API and worker services connecting to shared PostgreSQL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
from datetime import datetime
|
|
from sqlalchemy import select, func
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from backend.models.author import Author
|
|
from backend.models.post import Post
|
|
from backend.models.comment import Comment
|
|
|
|
|
|
async def list_authors(
|
|
db: AsyncSession,
|
|
subreddit_id: int | None = None,
|
|
sort_by: str = "total_comments",
|
|
sort_order: str = "desc",
|
|
since: datetime | None = None,
|
|
until: datetime | None = None,
|
|
page: int = 1,
|
|
per_page: int = 25,
|
|
) -> tuple[list[dict], int]:
|
|
base = select(Author)
|
|
|
|
if subreddit_id or since or until:
|
|
# Need to compute activity counts with filters
|
|
post_count = (
|
|
select(func.count(Post.id))
|
|
.where(Post.author_id == Author.id)
|
|
)
|
|
comment_count = (
|
|
select(func.count(Comment.id))
|
|
.where(Comment.author_id == Author.id)
|
|
)
|
|
|
|
if subreddit_id:
|
|
post_count = post_count.where(Post.subreddit_id == subreddit_id)
|
|
comment_count = comment_count.join(Post).where(Post.subreddit_id == subreddit_id)
|
|
if since:
|
|
post_count = post_count.where(Post.created_utc >= since)
|
|
comment_count = comment_count.where(Comment.created_utc >= since)
|
|
if until:
|
|
post_count = post_count.where(Post.created_utc <= until)
|
|
comment_count = comment_count.where(Comment.created_utc <= until)
|
|
|
|
base = select(
|
|
Author,
|
|
post_count.correlate(Author).scalar_subquery().label("filtered_posts"),
|
|
comment_count.correlate(Author).scalar_subquery().label("filtered_comments"),
|
|
)
|
|
else:
|
|
base = select(Author)
|
|
|
|
count_stmt = select(func.count()).select_from(base.subquery())
|
|
total = (await db.execute(count_stmt)).scalar() or 0
|
|
|
|
sort_col = getattr(Author, sort_by, Author.total_comments)
|
|
if sort_order == "asc":
|
|
base = base.order_by(sort_col.asc())
|
|
else:
|
|
base = base.order_by(sort_col.desc())
|
|
|
|
base = base.offset((page - 1) * per_page).limit(per_page)
|
|
|
|
result = await db.execute(base)
|
|
authors = []
|
|
for row in result.all():
|
|
if isinstance(row, tuple):
|
|
author = row[0]
|
|
else:
|
|
author = row
|
|
data = {c.name: getattr(author, c.name) for c in author.__table__.columns}
|
|
authors.append(data)
|
|
|
|
return authors, total
|
|
|
|
|
|
async def get_author(db: AsyncSession, author_id: int) -> dict | None:
|
|
author = await db.get(Author, author_id)
|
|
if not author:
|
|
return None
|
|
return {c.name: getattr(author, c.name) for c in author.__table__.columns}
|