pyrom/app/models.py
2025-07-02 13:35:14 +03:00

247 lines
8.5 KiB
Python

from .db import Model, db
from .constants import PermissionLevel
import time
class Users(Model):
table = "users"
def get_avatar_url(self):
return Avatars.find({"id": self.avatar_id}).file_path
def is_default_avatar(self):
return int(Avatars.find({'id': self.avatar_id}).id) == 1
def is_guest(self):
return self.permission == PermissionLevel.GUEST.value
def is_mod(self):
return self.permission >= PermissionLevel.MODERATOR.value
def is_admin(self):
return self.permission == PermissionLevel.ADMIN.value
def is_system(self):
return self.permission == PermissionLevel.SYSTEM.value
def is_default_avatar(self):
return self.avatar_id == 1
def get_latest_posts(self):
q = """SELECT
posts.id, posts.created_at, post_history.content, post_history.edited_at, threads.title AS thread_title, topics.name as topic_name, threads.slug as thread_slug
FROM
posts
JOIN
post_history ON posts.current_revision_id = post_history.id
JOIN
threads ON posts.thread_id = threads.id
JOIN
topics ON threads.topic_id = topics.id
WHERE
posts.user_id = ?
ORDER BY posts.created_at DESC
LIMIT 10"""
return db.query(q, self.id)
def get_post_stats(self):
q = """SELECT
COUNT(DISTINCT posts.id) AS post_count,
COUNT(DISTINCT threads.id) AS thread_count,
MAX(threads.title) FILTER (WHERE threads.created_at = latest.created_at) AS latest_thread_title,
MAX(threads.slug) FILTER (WHERE threads.created_at = latest.created_at) AS latest_thread_slug
FROM users
LEFT JOIN posts ON posts.user_id = users.id
LEFT JOIN threads ON threads.user_id = users.id
LEFT JOIN (
SELECT user_id, MAX(created_at) AS created_at
FROM threads
GROUP BY user_id
) latest ON latest.user_id = users.id
WHERE users.id = ?"""
return db.fetch_one(q, self.id)
def get_all_subscriptions(self):
q = """
SELECT threads.title AS thread_title, threads.slug AS thread_slug
FROM
threads
JOIN
subscriptions ON subscriptions.thread_id = threads.id
WHERE
subscriptions.user_id = ?"""
return db.query(q, self.id)
class Topics(Model):
table = "topics"
@classmethod
def get_list(_cls):
q = """
SELECT
topics.id, topics.name, topics.slug, topics.description, topics.is_locked,
users.username AS latest_thread_username,
threads.title AS latest_thread_title,
threads.slug AS latest_thread_slug,
threads.created_at AS latest_thread_created_at
FROM
topics
LEFT JOIN (
SELECT
*,
row_number() OVER (PARTITION BY threads.topic_id ORDER BY threads.created_at DESC) as rn
FROM
threads
) threads ON threads.topic_id = topics.id AND threads.rn = 1
LEFT JOIN
users on users.id = threads.user_id
ORDER BY
topics.sort_order ASC"""
return db.query(q)
@classmethod
def get_active_threads(cls):
q = """
WITH ranked_threads AS (
SELECT
threads.topic_id, threads.id AS thread_id, threads.title AS thread_title, threads.slug AS thread_slug,
posts.id AS post_id, posts.created_at AS post_created_at,
users.username,
ROW_NUMBER() OVER (PARTITION BY threads.topic_id ORDER BY posts.created_at DESC) AS rn
FROM
threads
JOIN
posts ON threads.id = posts.thread_id
LEFT JOIN
users ON posts.user_id = users.id
)
SELECT
topic_id,
thread_id, thread_title, thread_slug,
post_id, post_created_at,
username
FROM
ranked_threads
WHERE
rn = 1
ORDER BY
topic_id"""
active_threads_raw = db.query(q)
active_threads = {}
for thread in active_threads_raw:
active_threads[int(thread['topic_id'])] = {
'thread_title': thread['thread_title'],
'thread_slug': thread['thread_slug'],
'post_id': thread['post_id'],
'username': thread['username'],
'post_created_at': thread['post_created_at']
}
return active_threads
def get_threads(self, per_page, page, sort_by = "activity"):
order_clause = ""
if sort_by == "thread":
order_clause = "ORDER BY threads.is_stickied DESC, threads.created_at DESC"
else:
order_clause = "ORDER BY threads.is_stickied DESC, latest_post_created_at DESC"
q = """
SELECT
threads.title, threads.slug, threads.created_at, threads.is_locked, threads.is_stickied,
users.username AS started_by,
u.username AS latest_post_username,
ph.content AS latest_post_content,
posts.created_at AS latest_post_created_at,
posts.id AS latest_post_id
FROM
threads
JOIN users ON users.id = threads.user_id
JOIN (
SELECT
posts.thread_id,
posts.id,
posts.user_id,
posts.created_at,
posts.current_revision_id,
ROW_NUMBER() OVER (PARTITION BY posts.thread_id ORDER BY posts.created_at DESC) AS rn
FROM
posts
) posts ON posts.thread_id = threads.id AND posts.rn = 1
JOIN
post_history ph ON ph.id = posts.current_revision_id
JOIN
users u ON u.id = posts.user_id
WHERE
threads.topic_id = ?
""" + order_clause + " LIMIT ? OFFSET ?"
return db.query(q, self.id, per_page, (page - 1) * per_page)
class Threads(Model):
table = "threads"
def get_posts(self, limit, offset):
q = Posts.FULL_POSTS_QUERY + " WHERE posts.thread_id = ? ORDER BY posts.created_at ASC LIMIT ? OFFSET ?"
return db.query(q, self.id, limit, offset)
def locked(self):
return bool(self.is_locked)
def stickied(self):
return bool(self.is_stickied)
class Posts(Model):
FULL_POSTS_QUERY = """
SELECT
posts.id, posts.created_at, post_history.content, post_history.edited_at, users.username, users.status, avatars.file_path AS avatar_path, posts.thread_id, users.id AS user_id, post_history.original_markup, users.signature_rendered, threads.slug AS thread_slug, threads.is_locked AS thread_is_locked
FROM
posts
JOIN
post_history ON posts.current_revision_id = post_history.id
JOIN
users ON posts.user_id = users.id
JOIN
threads ON posts.thread_id = threads.id
LEFT JOIN
avatars ON users.avatar_id = avatars.id"""
table = "posts"
class PostHistory(Model):
table = "post_history"
class Sessions(Model):
table = "sessions"
class Avatars(Model):
table = "avatars"
class Subscriptions(Model):
table = "subscriptions"
class APIRateLimits(Model):
table = 'api_rate_limits'
@classmethod
def is_allowed(cls, user_id, method, seconds):
q = """
SELECT logged_at FROM api_rate_limits
WHERE user_id = ? AND method = ?
ORDER BY logged_at DESC LIMIT 1"""
last_call = db.fetch_one(q, user_id, method)
if last_call is None or (int(time.time()) - int(last_call['logged_at']) >= seconds):
with db.transaction():
db.query(
'DELETE FROM api_rate_limits WHERE user_id = ? AND method = ?',
user_id, method
)
db.query(
'INSERT INTO api_rate_limits (user_id, method) VALUES (?, ?)',
user_id, method
)
return True
else:
return False