From 8619433b6690ff85789740774f74a7db7e572a43 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 18 Nov 2023 11:25:22 +0000 Subject: [PATCH] Homepage: Improve performance by adjusting loading options --- app/blueprints/homepage/__init__.py | 53 ++++++++++++++++++----------- app/models/packages.py | 31 ++++++++++++++--- app/models/threads.py | 7 ++++ app/templates/macros/reviews.html | 4 +-- app/templates/macros/threads.html | 2 +- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/app/blueprints/homepage/__init__.py b/app/blueprints/homepage/__init__.py index d10c48c2..b0a75e8b 100644 --- a/app/blueprints/homepage/__init__.py +++ b/app/blueprints/homepage/__init__.py @@ -18,11 +18,11 @@ from flask import Blueprint, render_template, redirect from sqlalchemy import and_ from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag, \ - Collection + Collection, License bp = Blueprint("homepage", __name__) -from sqlalchemy.orm import joinedload, subqueryload +from sqlalchemy.orm import joinedload, subqueryload, load_only, noload from sqlalchemy.sql.expression import func @@ -38,32 +38,47 @@ def gamejam(): def home(): def package_load(query): return query.options( - joinedload(Package.author), + load_only(Package.name, Package.title, Package.short_desc, Package.state, raiseload=True), subqueryload(Package.main_screenshot), + joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True), + joinedload(Package.license).load_only(License.name, License.is_foss, raiseload=True), + joinedload(Package.media_license).load_only(License.name, License.is_foss, raiseload=True)) + + def package_spotlight_load(query): + return query.options( + load_only(Package.name, Package.title, Package.type, Package.short_desc, Package.state, Package.cover_image_id, raiseload=True), + subqueryload(Package.main_screenshot), + joinedload(Package.tags), + joinedload(Package.content_warnings), + joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True), subqueryload(Package.cover_image), - joinedload(Package.license), - joinedload(Package.media_license)) + joinedload(Package.license).load_only(License.name, License.is_foss, raiseload=True), + joinedload(Package.media_license).load_only(License.name, License.is_foss, raiseload=True)) def review_load(query): return query.options( - joinedload(PackageReview.author), - joinedload(PackageReview.thread).subqueryload(Thread.first_reply), - joinedload(PackageReview.package).joinedload(Package.author).load_only(User.username, User.display_name), - joinedload(PackageReview.package).load_only(Package.title, Package.name).subqueryload(Package.main_screenshot)) + load_only(PackageReview.id, PackageReview.rating, PackageReview.created_at, raiseload=True), + joinedload(PackageReview.author).load_only(User.username, User.rank, User.email, User.display_name, User.profile_pic, User.is_active, raiseload=True), + joinedload(PackageReview.votes), + joinedload(PackageReview.thread).load_only(Thread.title, Thread.replies_count, raiseload=True).subqueryload(Thread.first_reply), + joinedload(PackageReview.package) + .load_only(Package.title, Package.name, raiseload=True) + .joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True)) query = Package.query.filter_by(state=PackageState.APPROVED) - count = query.count() + count = db.session.query(Package.id).filter(Package.state == PackageState.APPROVED).count() - spotlight_pkgs = query.filter( - Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB")))) \ - .order_by(func.random()).limit(6).all() + spotlight_pkgs = package_spotlight_load(query.filter( + Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB")))) + .order_by(func.random())).limit(6).all() - new = package_load(query.order_by(db.desc(Package.approved_at))).limit(PKGS_PER_ROW).all() - pop_mod = package_load(query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all() - pop_gam = package_load(query.filter_by(type=PackageType.GAME).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all() - pop_txp = package_load(query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all() - high_reviewed = package_load(query.order_by(db.desc(Package.score - Package.score_downloads))) \ - .filter(Package.reviews.any()).limit(PKGS_PER_ROW).all() + new = package_load(query).order_by(db.desc(Package.approved_at)).limit(PKGS_PER_ROW).all() # 0.06 + pop_mod = package_load(query).filter_by(type=PackageType.MOD).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all() + pop_gam = package_load(query).filter_by(type=PackageType.GAME).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all() + pop_txp = package_load(query).filter_by(type=PackageType.TXP).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all() + + high_reviewed = package_load(query.order_by(db.desc(Package.score - Package.score_downloads)) + .filter(Package.reviews.any()).limit(PKGS_PER_ROW)).all() recent_releases_query = ( db.session.query( diff --git a/app/models/packages.py b/app/models/packages.py index 069aa1f7..dbe571f0 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -21,14 +21,14 @@ import enum from flask import url_for from flask_babel import lazy_gettext from flask_sqlalchemy import BaseQuery -from sqlalchemy import or_ +from sqlalchemy import or_, func +from sqlalchemy.dialects.postgresql import insert from sqlalchemy_searchable import SearchQueryMixin from sqlalchemy_utils.types import TSVectorType -from sqlalchemy.dialects.postgresql import insert +from app import app from . import db from .users import Permission, UserRank, User -from app import app class PackageQuery(BaseQuery, SearchQueryMixin): @@ -438,8 +438,8 @@ class Package(db.Model): threads = db.relationship("Thread", back_populates="package", order_by=db.desc("thread_created_at"), foreign_keys="Thread.package_id", cascade="all, delete, delete-orphan", lazy="dynamic") - reviews = db.relationship("PackageReview", back_populates="package", - order_by=[db.desc("package_review_score"),db.desc("package_review_created_at")], + reviews = db.relationship("PackageReview", back_populates="package", lazy="dynamic", + order_by=[db.desc("package_review_score"), db.desc("package_review_created_at")], cascade="all, delete, delete-orphan") audit_log_entries = db.relationship("AuditLogEntry", foreign_keys="AuditLogEntry.package_id", @@ -785,6 +785,27 @@ class Package(db.Model): elif self.type == PackageType.GAME: return "game.conf" + def get_review_summary(self): + from app.models import PackageReview + rows = (db.session.query(PackageReview.rating, func.count(PackageReview.id)) + .select_from(PackageReview) + .where(PackageReview.package_id == self.id) + .group_by(PackageReview.rating) + .all()) + + negative = 0 + neutral = 0 + positive = 0 + for rating, count in rows: + if rating > 3: + positive += count + elif rating == 3: + neutral += count + else: + negative += count + + return [positive, neutral, negative] + class MetaPackage(db.Model): id = db.Column(db.Integer, primary_key=True) diff --git a/app/models/threads.py b/app/models/threads.py index 5aa26f7a..8d7ebaf8 100644 --- a/app/models/threads.py +++ b/app/models/threads.py @@ -18,6 +18,8 @@ import datetime from typing import Tuple, List from flask import url_for +from sqlalchemy import select, func, text +from sqlalchemy.orm import column_property from . import db from .users import Permission, UserRank, User @@ -59,6 +61,11 @@ class Thread(db.Model): lazy=True, order_by=db.asc("id"), viewonly=True, primaryjoin="Thread.id==ThreadReply.thread_id") + replies_count = column_property(select(func.count(text("thread_reply.id"))) + .select_from(text("thread_reply")) + .where(text("thread_reply.thread_id") == id) + .as_scalar()) + def get_description(self): comment = self.first_reply.comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ") if len(comment) > 100: diff --git a/app/templates/macros/reviews.html b/app/templates/macros/reviews.html index d55240d4..cefcf1d0 100644 --- a/app/templates/macros/reviews.html +++ b/app/templates/macros/reviews.html @@ -79,10 +79,10 @@ {% endif %} - - {{ _("%(num)d comments", num=review.thread.replies.count() - 1) }} + {{ _("%(num)d comments", num=review.thread.replies_count - 1) }} {{ render_review_vote(review, current_user, url_set_anchor(review_anchor)) }} diff --git a/app/templates/macros/threads.html b/app/templates/macros/threads.html index 729c8eab..0a17679a 100644 --- a/app/templates/macros/threads.html +++ b/app/templates/macros/threads.html @@ -216,7 +216,7 @@ {% for t in threads %} - {% set replies = t.replies.count() - 1 %} + {% set replies = t.replies_count - 1 %}