Homepage: Improve performance by adjusting loading options

This commit is contained in:
rubenwardy 2023-11-18 11:25:22 +00:00
parent 96c86cf070
commit 8619433b66
5 changed files with 70 additions and 27 deletions

@ -18,11 +18,11 @@ from flask import Blueprint, render_template, redirect
from sqlalchemy import and_ from sqlalchemy import and_
from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag, \ from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag, \
Collection Collection, License
bp = Blueprint("homepage", __name__) 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 from sqlalchemy.sql.expression import func
@ -38,32 +38,47 @@ def gamejam():
def home(): def home():
def package_load(query): def package_load(query):
return query.options( return query.options(
joinedload(Package.author), load_only(Package.name, Package.title, Package.short_desc, Package.state, raiseload=True),
subqueryload(Package.main_screenshot), 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), subqueryload(Package.cover_image),
joinedload(Package.license), joinedload(Package.license).load_only(License.name, License.is_foss, raiseload=True),
joinedload(Package.media_license)) joinedload(Package.media_license).load_only(License.name, License.is_foss, raiseload=True))
def review_load(query): def review_load(query):
return query.options( return query.options(
joinedload(PackageReview.author), load_only(PackageReview.id, PackageReview.rating, PackageReview.created_at, raiseload=True),
joinedload(PackageReview.thread).subqueryload(Thread.first_reply), joinedload(PackageReview.author).load_only(User.username, User.rank, User.email, User.display_name, User.profile_pic, User.is_active, raiseload=True),
joinedload(PackageReview.package).joinedload(Package.author).load_only(User.username, User.display_name), joinedload(PackageReview.votes),
joinedload(PackageReview.package).load_only(Package.title, Package.name).subqueryload(Package.main_screenshot)) 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) 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( spotlight_pkgs = package_spotlight_load(query.filter(
Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB")))) \ Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB"))))
.order_by(func.random()).limit(6).all() .order_by(func.random())).limit(6).all()
new = package_load(query.order_by(db.desc(Package.approved_at))).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_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_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() 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() 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 = ( recent_releases_query = (
db.session.query( db.session.query(

@ -21,14 +21,14 @@ import enum
from flask import url_for from flask import url_for
from flask_babel import lazy_gettext from flask_babel import lazy_gettext
from flask_sqlalchemy import BaseQuery 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_searchable import SearchQueryMixin
from sqlalchemy_utils.types import TSVectorType from sqlalchemy_utils.types import TSVectorType
from sqlalchemy.dialects.postgresql import insert
from app import app
from . import db from . import db
from .users import Permission, UserRank, User from .users import Permission, UserRank, User
from app import app
class PackageQuery(BaseQuery, SearchQueryMixin): 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"), 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") foreign_keys="Thread.package_id", cascade="all, delete, delete-orphan", lazy="dynamic")
reviews = db.relationship("PackageReview", back_populates="package", reviews = db.relationship("PackageReview", back_populates="package", lazy="dynamic",
order_by=[db.desc("package_review_score"),db.desc("package_review_created_at")], order_by=[db.desc("package_review_score"), db.desc("package_review_created_at")],
cascade="all, delete, delete-orphan") cascade="all, delete, delete-orphan")
audit_log_entries = db.relationship("AuditLogEntry", foreign_keys="AuditLogEntry.package_id", audit_log_entries = db.relationship("AuditLogEntry", foreign_keys="AuditLogEntry.package_id",
@ -785,6 +785,27 @@ class Package(db.Model):
elif self.type == PackageType.GAME: elif self.type == PackageType.GAME:
return "game.conf" 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): class MetaPackage(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

@ -18,6 +18,8 @@ import datetime
from typing import Tuple, List from typing import Tuple, List
from flask import url_for from flask import url_for
from sqlalchemy import select, func, text
from sqlalchemy.orm import column_property
from . import db from . import db
from .users import Permission, UserRank, User from .users import Permission, UserRank, User
@ -59,6 +61,11 @@ class Thread(db.Model):
lazy=True, order_by=db.asc("id"), viewonly=True, lazy=True, order_by=db.asc("id"), viewonly=True,
primaryjoin="Thread.id==ThreadReply.thread_id") 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): def get_description(self):
comment = self.first_reply.comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ") comment = self.first_reply.comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ")
if len(comment) > 100: if len(comment) > 100:

@ -79,10 +79,10 @@
</a> </a>
{% endif %} {% endif %}
<a class="btn {% if review.thread.replies.count() > 1 %} btn-primary {% else %} btn-secondary {% endif %} me-1" <a class="btn {% if review.thread.replies_count > 1 %} btn-primary {% else %} btn-secondary {% endif %} me-1"
href="{{ url_for('threads.view', id=review.thread.id) }}"> href="{{ url_for('threads.view', id=review.thread.id) }}">
<i class="fas fa-comments me-2"></i> <i class="fas fa-comments me-2"></i>
{{ _("%(num)d comments", num=review.thread.replies.count() - 1) }} {{ _("%(num)d comments", num=review.thread.replies_count - 1) }}
</a> </a>
{{ render_review_vote(review, current_user, url_set_anchor(review_anchor)) }} {{ render_review_vote(review, current_user, url_set_anchor(review_anchor)) }}

@ -216,7 +216,7 @@
</div> </div>
{% for t in threads %} {% for t in threads %}
{% set replies = t.replies.count() - 1 %} {% set replies = t.replies_count - 1 %}
<a class="list-group-item list-group-item-action" <a class="list-group-item list-group-item-action"
href="{{ url_for('threads.view', id=t.id) }}"> href="{{ url_for('threads.view', id=t.id) }}">