Compare commits

..

3 commits

Author SHA1 Message Date
17bdd52253 fix /v1/top/guilds not returning 2025-10-07 09:40:44 +02:00
79b1b574a8 add /top/guilds 2025-09-18 12:27:34 +02:00
f51f488567 bump suou requirement 2025-09-14 16:22:00 +02:00
6 changed files with 57 additions and 15 deletions

View file

@ -26,7 +26,7 @@ from suou import twocolon_list, WantsContentType
from .colors import color_themes, theme_classes from .colors import color_themes, theme_classes
__version__ = '0.5.0-dev36' __version__ = '0.5.0-dev40'
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__)) APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@ -228,7 +228,9 @@ async def error_404(body):
except Exception as e: except Exception as e:
logger.error(f'Exception in find_guild_or_user: {e}') logger.error(f'Exception in find_guild_or_user: {e}')
pass pass
print(request.host) if app_config.server_name not in (None, request.host):
logger.warning(f'request host {request.host!r} is different from configured server name {app_config.server_name}')
return redirect('//' + app_config.server_name + request.full_path), 307
return await error_handler_for(404, 'Not found', '404.html') return await error_handler_for(404, 'Not found', '404.html')
@app.errorhandler(405) @app.errorhandler(405)

View file

@ -2,6 +2,7 @@
from flask_login import current_user from flask_login import current_user
from sqlalchemy import and_, distinct, func, select from sqlalchemy import and_, distinct, func, select
from suou import not_implemented
from .models import Comment, Member, Post, Guild, User from .models import Comment, Member, Post, Guild, User
@ -29,10 +30,21 @@ def user_timeline(user: User):
).order_by(Post.created_at.desc()) ).order_by(Post.created_at.desc())
def new_comments(p: Post): def new_comments(p: Post):
return select(Comment).join(Post, Post.id == Comment.parent_post_id).join(User, User.id == Comment.author_id).where(Comment.parent_post_id == p.id, Comment.parent_comment_id == None, return select(Comment).join(Post, Post.id == Comment.parent_post_id).join(User, User.id == Comment.author_id
Comment.not_removed(), User.has_not_blocked(Comment.author_id, cuser_id())).order_by(Comment.created_at.desc()) ).where(Comment.parent_post_id == p.id, Comment.parent_comment_id == None, Comment.not_removed(), User.has_not_blocked(Comment.author_id, cuser_id())
).order_by(Comment.created_at.desc())
def top_guilds_query():
q_post_count = func.count(distinct(Post.id)).label('post_count')
q_sub_count = func.count(distinct(Member.id)).label('sub_count')
qr = select(Guild, q_post_count, q_sub_count)\
.join(Post, Post.topic_id == Guild.id, isouter=True)\
.join(Member, and_(Member.guild_id == Guild.id, Member.is_subscribed == True), isouter=True)\
.group_by(Guild).having(q_post_count > 5).order_by(q_post_count.desc(), q_sub_count.desc())
return qr
@not_implemented()
class Algorithms: class Algorithms:
""" """
Return SQL queries for algorithms. Return SQL queries for algorithms.

View file

@ -503,6 +503,15 @@ class Guild(Base):
gg['type'] = 'guild' gg['type'] = 'guild'
return gg return gg
async def sub_info(self):
"""
Guild info including subscriber count.
"""
gg = self.simple_info()
gg['subscriber_count'] = await self.subscriber_count()
gg['post_count'] = await self.post_count()
return gg
Topic = deprecated('renamed to Guild')(Guild) Topic = deprecated('renamed to Guild')(Guild)

View file

@ -13,7 +13,7 @@ from werkzeug.security import check_password_hash
from suou.quart import add_rest from suou.quart import add_rest
from freak.accounts import LoginStatus, check_login from freak.accounts import LoginStatus, check_login
from freak.algorithms import topic_timeline, user_timeline from freak.algorithms import public_timeline, top_guilds_query, topic_timeline, user_timeline
from ..models import Guild, Post, User, db from ..models import Guild, Post, User, db
from .. import UserLoader, app, app_config, __version__ as freak_version, csrf from .. import UserLoader, app, app_config, __version__ as freak_version, csrf
@ -222,3 +222,29 @@ async def logout():
logout_user() logout_user()
return '', 204 return '', 204
## HOME ##
@bp.get('/home/feed')
@login_required
async def home_feed():
async with db as session:
me = current_user.user
posts = await db.paginate(public_timeline())
feed = []
async for post in posts:
feed.append(post.feed_info())
return dict(feed=feed)
@bp.get('/top/guilds')
async def top_guilds():
async with db as session:
top_g = [await x.sub_info() for x in
(await session.execute(top_guilds_query().limit(10))).scalars()]
return dict(has=top_g)

View file

@ -11,20 +11,13 @@ from freak.utils import get_request_form
from ..search import SearchQuery from ..search import SearchQuery
from ..models import Guild, Member, Post, User, db from ..models import Guild, Member, Post, User, db
from ..algorithms import public_timeline, topic_timeline from ..algorithms import public_timeline, top_guilds_query, topic_timeline
current_user: UserLoader current_user: UserLoader
bp = Blueprint('frontpage', __name__) bp = Blueprint('frontpage', __name__)
def top_guilds_query():
q_post_count = func.count(distinct(Post.id)).label('post_count')
q_sub_count = func.count(distinct(Member.id)).label('sub_count')
qr = select(Guild.name, q_post_count, q_sub_count)\
.join(Post, Post.topic_id == Guild.id, isouter=True)\
.join(Member, and_(Member.guild_id == Guild.id, Member.is_subscribed == True), isouter=True)\
.group_by(Guild).having(q_post_count > 5).order_by(q_post_count.desc(), q_sub_count.desc())
return qr
@bp.route('/') @bp.route('/')
async def homepage(): async def homepage():

View file

@ -19,7 +19,7 @@ dependencies = [
"libsass", "libsass",
"setuptools>=78.1.0", "setuptools>=78.1.0",
"Hypercorn", "Hypercorn",
"sakuragasaki46-suou>=0.6.0" "suou>=0.6.1"
] ]
requires-python = ">=3.10" requires-python = ">=3.10"
classifiers = [ classifiers = [