Update
All checks were successful
DevPlace CI / test (push) Successful in 5m33s

This commit is contained in:
retoor 2026-05-23 10:54:45 +02:00
parent fe0ed5b7e6
commit 347e5f0f31
4 changed files with 60 additions and 4 deletions

View File

@ -338,6 +338,46 @@ def get_gist_languages() -> set[str]:
return codes
_STARRED_CONTENT_TABLES = ("posts", "projects", "gists")
_top_authors_cache = TTLCache(ttl=60)
def get_top_authors(limit: int = 5) -> list:
cached = _top_authors_cache.get("top")
if cached is not None:
return cached
sources = [table for table in _STARRED_CONTENT_TABLES if table in db.tables]
if not sources:
_top_authors_cache.set("top", [])
return []
union = " UNION ALL ".join(f"SELECT user_uid, stars FROM {table}" for table in sources)
rows = db.query(
f"SELECT user_uid, SUM(stars) AS total FROM ({union}) "
f"GROUP BY user_uid HAVING total > 0 ORDER BY total DESC LIMIT {int(limit)}"
)
ranked = [(row["user_uid"], row["total"]) for row in rows]
users_map = get_users_by_uids([uid for uid, _ in ranked])
authors = []
for uid, total in ranked:
user = users_map.get(uid)
if user:
author = dict(user)
author["stars"] = total
authors.append(author)
_top_authors_cache.set("top", authors)
return authors
def get_user_stars(user_uid: str) -> int:
total = 0
for table in _STARRED_CONTENT_TABLES:
if table in db.tables:
for row in db.query(f"SELECT COALESCE(SUM(stars), 0) AS s FROM {table} WHERE user_uid = :u", u=user_uid):
total += row["s"] or 0
return total
def resolve_by_slug(table, slug):
entry = table.find_one(slug=slug)
if not entry:

View File

@ -1,7 +1,7 @@
import logging
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from devplacepy.database import get_table, get_daily_topic, get_users_by_uids, get_comment_counts_by_post_uids, get_site_stats
from devplacepy.database import get_table, get_daily_topic, get_users_by_uids, get_comment_counts_by_post_uids, get_site_stats, get_top_authors
from devplacepy.attachments import get_attachments_batch
from devplacepy.content import enrich_items
from devplacepy.templating import templates
@ -60,9 +60,8 @@ def get_feed_posts(user, tab: str = "all", topic: str = None, before: str = None
async def feed_page(request: Request, tab: str = "all", topic: str = None, before: str = None):
user = get_current_user(request)
posts, next_cursor = get_feed_posts(user, tab, topic, before)
users_table = get_table("users")
stats = get_site_stats()
top_authors = list(users_table.find(stars={">": 0}, order_by=["-stars"], _limit=5))
top_authors = get_top_authors(5)
daily_topic = get_daily_topic()
post_uids_list = [item["post"]["uid"] for item in posts]

View File

@ -3,7 +3,7 @@ from typing import Annotated
from fastapi import APIRouter, Request, Form
from devplacepy.models import ProfileForm
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from devplacepy.database import get_table, db
from devplacepy.database import get_table, db, get_user_stars
from devplacepy.templating import templates
from devplacepy.utils import get_current_user, require_user, require_user_api, time_ago, clear_user_cache
from devplacepy.seo import base_seo_context, site_url, website_schema, profile_page_schema
@ -35,6 +35,7 @@ async def profile_page(request: Request, username: str, tab: str = "posts"):
profile_user = users.find_one(username=username)
if not profile_user:
return RedirectResponse(url="/feed", status_code=302)
profile_user["stars"] = get_user_stars(profile_user["uid"])
posts = []
if tab == "posts":

View File

@ -49,6 +49,22 @@ def test_post_vote_increment(alice):
assert count == "1", f"expected vote count 1, got {count!r}"
def _profile_stars(page, username):
page.goto(f"{BASE_URL}/profile/{username}", wait_until="domcontentloaded")
value = page.locator(".profile-stat:has(.profile-stat-label:has-text('Stars')) .profile-stat-value").first
return int(value.text_content().strip())
def test_profile_stars_reflect_content_votes(alice):
page, user = alice
before = _profile_stars(page, user["username"])
create_post(page, "devlog", "Reputation contribution post")
page.locator(".post-action-btn.vote-up").first.click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
after = _profile_stars(page, user["username"])
assert after == before + 1, f"expected stars {before + 1}, got {after}"
def test_post_comments_section(alice):
page, _ = alice
create_post(page, "rant", "Comment section test")