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 %}