refactor: split database.py into package (ref.md 3.2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
from .core import TTLCache, _in_clause, db, defaultdict
|
||||
|
||||
|
||||
_comment_count_cache = TTLCache(ttl=15, max_size=10000)
|
||||
|
||||
|
||||
def get_comment_counts_by_post_uids(post_uids):
|
||||
if not post_uids or "comments" not in db.tables:
|
||||
return {}
|
||||
result = {}
|
||||
misses = []
|
||||
for uid in post_uids:
|
||||
cached = _comment_count_cache.get(uid)
|
||||
if cached is None:
|
||||
misses.append(uid)
|
||||
else:
|
||||
result[uid] = cached
|
||||
if misses:
|
||||
placeholders, params = _in_clause(misses)
|
||||
rows = db.query(
|
||||
f"SELECT target_uid, COUNT(*) as c FROM comments WHERE target_type='post' AND target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid",
|
||||
**params,
|
||||
)
|
||||
fetched = {r["target_uid"]: r["c"] for r in rows}
|
||||
for uid in misses:
|
||||
count = fetched.get(uid, 0)
|
||||
_comment_count_cache.set(uid, count)
|
||||
result[uid] = count
|
||||
return result
|
||||
|
||||
|
||||
def get_post_counts_by_user_uids(user_uids):
|
||||
if not user_uids or "posts" not in db.tables:
|
||||
return {}
|
||||
placeholders, params = _in_clause(user_uids)
|
||||
rows = db.query(
|
||||
f"SELECT user_uid, COUNT(*) as c FROM posts WHERE user_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY user_uid",
|
||||
**params,
|
||||
)
|
||||
return {r["user_uid"]: r["c"] for r in rows}
|
||||
|
||||
|
||||
def get_vote_counts(target_uids):
|
||||
if not target_uids or "votes" not in db.tables:
|
||||
return {}, {}
|
||||
placeholders, params = _in_clause(target_uids)
|
||||
rows = db.query(
|
||||
f"SELECT target_uid, value, COUNT(*) as c FROM votes WHERE target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid, value",
|
||||
**params,
|
||||
)
|
||||
ups = {}
|
||||
downs = {}
|
||||
for r in rows:
|
||||
if r["value"] == 1:
|
||||
ups[r["target_uid"]] = r["c"]
|
||||
else:
|
||||
downs[r["target_uid"]] = r["c"]
|
||||
return ups, downs
|
||||
|
||||
|
||||
def get_user_votes(user_uid, target_uids):
|
||||
if not user_uid or not target_uids or "votes" not in db.tables:
|
||||
return {}
|
||||
placeholders, params = _in_clause(target_uids)
|
||||
params["uid"] = user_uid
|
||||
rows = db.query(
|
||||
f"SELECT target_uid, value FROM votes WHERE user_uid = :uid AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
|
||||
**params,
|
||||
)
|
||||
return {r["target_uid"]: r["value"] for r in rows}
|
||||
|
||||
|
||||
def get_reactions_by_targets(target_type, target_uids, user=None):
|
||||
if not target_uids or "reactions" not in db.tables:
|
||||
return {}
|
||||
placeholders, params = _in_clause(target_uids)
|
||||
params["tt"] = target_type
|
||||
rows = db.query(
|
||||
f"SELECT target_uid, emoji, COUNT(*) as c FROM reactions WHERE target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL GROUP BY target_uid, emoji",
|
||||
**params,
|
||||
)
|
||||
counts = defaultdict(dict)
|
||||
for row in rows:
|
||||
counts[row["target_uid"]][row["emoji"]] = row["c"]
|
||||
mine = defaultdict(list)
|
||||
if user:
|
||||
placeholders, params = _in_clause(target_uids, prefix="m")
|
||||
params["tt"] = target_type
|
||||
params["u"] = user["uid"]
|
||||
for row in db.query(
|
||||
f"SELECT target_uid, emoji FROM reactions WHERE user_uid=:u AND target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
|
||||
**params,
|
||||
):
|
||||
mine[row["target_uid"]].append(row["emoji"])
|
||||
result = {}
|
||||
for uid in target_uids:
|
||||
result[uid] = {
|
||||
"counts": dict(counts.get(uid, {})),
|
||||
"mine": list(mine.get(uid, [])),
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def get_user_bookmarks(user_uid, target_type, target_uids):
|
||||
if not user_uid or not target_uids or "bookmarks" not in db.tables:
|
||||
return set()
|
||||
placeholders, params = _in_clause(target_uids)
|
||||
params["u"] = user_uid
|
||||
params["tt"] = target_type
|
||||
rows = db.query(
|
||||
f"SELECT target_uid FROM bookmarks WHERE user_uid=:u AND target_type=:tt AND target_uid IN ({placeholders}) AND deleted_at IS NULL",
|
||||
**params,
|
||||
)
|
||||
return {row["target_uid"] for row in rows}
|
||||
|
||||
|
||||
def get_polls_by_post_uids(post_uids, user=None):
|
||||
if not post_uids or "polls" not in db.tables:
|
||||
return {}
|
||||
placeholders, params = _in_clause(post_uids)
|
||||
polls = list(
|
||||
db.query(
|
||||
f"SELECT * FROM polls WHERE post_uid IN ({placeholders}) AND deleted_at IS NULL",
|
||||
**params,
|
||||
)
|
||||
)
|
||||
if not polls:
|
||||
return {}
|
||||
poll_uids = [poll["uid"] for poll in polls]
|
||||
option_placeholders, option_params = _in_clause(poll_uids, prefix="o")
|
||||
options = list(
|
||||
db.query(
|
||||
f"SELECT * FROM poll_options WHERE poll_uid IN ({option_placeholders}) AND deleted_at IS NULL ORDER BY position",
|
||||
**option_params,
|
||||
)
|
||||
)
|
||||
counts = defaultdict(dict)
|
||||
totals = defaultdict(int)
|
||||
if "poll_votes" in db.tables:
|
||||
vote_placeholders, vote_params = _in_clause(poll_uids, prefix="v")
|
||||
for row in db.query(
|
||||
f"SELECT poll_uid, option_uid, COUNT(*) as c FROM poll_votes WHERE poll_uid IN ({vote_placeholders}) AND deleted_at IS NULL GROUP BY poll_uid, option_uid",
|
||||
**vote_params,
|
||||
):
|
||||
counts[row["poll_uid"]][row["option_uid"]] = row["c"]
|
||||
totals[row["poll_uid"]] += row["c"]
|
||||
user_choice = {}
|
||||
if user and "poll_votes" in db.tables:
|
||||
placeholders, params = _in_clause(poll_uids, prefix="m")
|
||||
params["u"] = user["uid"]
|
||||
for row in db.query(
|
||||
f"SELECT poll_uid, option_uid FROM poll_votes WHERE user_uid=:u AND poll_uid IN ({placeholders}) AND deleted_at IS NULL",
|
||||
**params,
|
||||
):
|
||||
user_choice[row["poll_uid"]] = row["option_uid"]
|
||||
options_by_poll = defaultdict(list)
|
||||
for option in options:
|
||||
options_by_poll[option["poll_uid"]].append(option)
|
||||
result = {}
|
||||
for poll in polls:
|
||||
poll_uid = poll["uid"]
|
||||
total = totals.get(poll_uid, 0)
|
||||
rendered = []
|
||||
for option in options_by_poll.get(poll_uid, []):
|
||||
count = counts.get(poll_uid, {}).get(option["uid"], 0)
|
||||
rendered.append(
|
||||
{
|
||||
"uid": option["uid"],
|
||||
"label": option["label"],
|
||||
"count": count,
|
||||
"pct": round(count * 100 / total) if total else 0,
|
||||
}
|
||||
)
|
||||
result[poll["post_uid"]] = {
|
||||
"uid": poll_uid,
|
||||
"question": poll["question"],
|
||||
"options": rendered,
|
||||
"total": total,
|
||||
"my_choice": user_choice.get(poll_uid),
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def get_poll_for_post(post_uid, user=None):
|
||||
return get_polls_by_post_uids([post_uid], user).get(post_uid)
|
||||
Reference in New Issue
Block a user