diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py
index e9c13ae6..dd21d5e6 100644
--- a/app/blueprints/api/endpoints.py
+++ b/app/blueprints/api/endpoints.py
@@ -428,7 +428,10 @@ def list_all_reviews():
query = query.filter(PackageReview.author.has(User.username == request.args.get("author")))
if request.args.get("is_positive"):
- query = query.filter(PackageReview.recommends == isYes(request.args.get("is_positive")))
+ if isYes(request.args.get("is_positive")):
+ query = query.filter(PackageReview.rating >= 3)
+ else:
+ query = query.filter(PackageReview.rating < 3)
q = request.args.get("q")
if q:
diff --git a/app/blueprints/donate/__init__.py b/app/blueprints/donate/__init__.py
index 39b806a8..549be043 100644
--- a/app/blueprints/donate/__init__.py
+++ b/app/blueprints/donate/__init__.py
@@ -2,7 +2,7 @@ from flask import Blueprint, render_template
from flask_login import current_user
from sqlalchemy import or_
-from app.models import User, Package, PackageState, db, License
+from app.models import User, Package, PackageState, db, License, PackageReview
bp = Blueprint("donate", __name__)
@@ -13,7 +13,7 @@ def donate():
if current_user.is_authenticated:
reviewed_packages = Package.query.filter(
Package.state == PackageState.APPROVED,
- Package.reviews.any(author_id=current_user.id, recommends=True),
+ Package.reviews.any(PackageReview.author_id == current_user.id, PackageReview.rating >= 3),
or_(Package.donate_url.isnot(None), Package.author.has(User.donate_url.isnot(None)))
).order_by(db.asc(Package.title)).all()
diff --git a/app/blueprints/homepage/__init__.py b/app/blueprints/homepage/__init__.py
index cb5e66d9..cf135dd9 100644
--- a/app/blueprints/homepage/__init__.py
+++ b/app/blueprints/homepage/__init__.py
@@ -47,7 +47,8 @@ def home():
.limit(20)).all()
updated = updated[:4]
- reviews = review_load(PackageReview.query.filter_by(recommends=True).order_by(db.desc(PackageReview.created_at))).limit(5).all()
+ reviews = review_load(PackageReview.query.filter(PackageReview.rating >= 3)
+ .order_by(db.desc(PackageReview.created_at))).limit(5).all()
downloads_result = db.session.query(func.sum(Package.downloads)).one_or_none()
downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0]
diff --git a/app/blueprints/packages/reviews.py b/app/blueprints/packages/reviews.py
index b4c0dd6b..d0dfc197 100644
--- a/app/blueprints/packages/reviews.py
+++ b/app/blueprints/packages/reviews.py
@@ -43,10 +43,11 @@ def list_reviews():
class ReviewForm(FlaskForm):
title = StringField(lazy_gettext("Title"), [InputRequired(), Length(3,100)])
comment = TextAreaField(lazy_gettext("Comment"), [InputRequired(), Length(10, 2000)])
- recommends = RadioField(lazy_gettext("Private"), [InputRequired()],
- choices=[("yes", lazy_gettext("Yes")), ("no", lazy_gettext("No"))])
+ rating = RadioField(lazy_gettext("Rating"), [InputRequired()],
+ choices=[("5", lazy_gettext("Yes")), ("3", lazy_gettext("Neutral")), ("1", lazy_gettext("No"))])
submit = SubmitField(lazy_gettext("Save"))
+
@bp.route("/packages///review/", methods=["GET", "POST"])
@login_required
@is_package_page
@@ -69,7 +70,7 @@ def review(package):
# Set default values
if request.method == "GET" and review:
form.title.data = review.thread.title
- form.recommends.data = "yes" if review.recommends else "no"
+ form.rating.data = str(review.rating)
form.comment.data = review.thread.first_reply.comment
# Validate and submit
@@ -85,7 +86,7 @@ def review(package):
review.author = current_user
db.session.add(review)
- review.recommends = form.recommends.data == "yes"
+ review.rating = int(form.rating.data)
thread = review.thread
if not thread:
@@ -227,7 +228,7 @@ def review_vote(package, review_id):
def review_votes(package):
user_biases = {}
for review in package.reviews:
- review_sign = 1 if review.recommends else -1
+ review_sign = review.asWeight()
for vote in review.votes:
user_biases[vote.user.username] = user_biases.get(vote.user.username, [0, 0])
vote_sign = 1 if vote.is_positive else -1
diff --git a/app/flatpages/help/api.md b/app/flatpages/help/api.md
index 666e4551..238b2db4 100644
--- a/app/flatpages/help/api.md
+++ b/app/flatpages/help/api.md
@@ -304,6 +304,7 @@ curl -X POST https://content.minetest.net/api/packages/username/name/screenshots
* `user`: dictionary with `display_name` and `username`.
* `title`: review title
* `comment`: the text
+ * `rating`: 1 for negative, 3 for neutral, 5 for positive
* `is_positive`: boolean
* `created_at`: iso timestamp
* `votes`: dictionary with `helpful` and `unhelpful`,
@@ -316,6 +317,7 @@ curl -X POST https://content.minetest.net/api/packages/username/name/screenshots
* `page`: page number, integer from 1 to max
* `n`: number of results per page, max 100
* `author`: filter by review author username
+ * `rating`: 1 for negative, 3 for neutral, 5 for positive
* `is_positive`: true or false. Default: null
* `q`: filter by title (case insensitive, no fulltext search)
diff --git a/app/models/packages.py b/app/models/packages.py
index 56194782..078ca82e 100644
--- a/app/models/packages.py
+++ b/app/models/packages.py
@@ -779,7 +779,7 @@ class Package(db.Model):
}
def recalcScore(self):
- review_scores = [ 100 * r.asSign() for r in self.reviews ]
+ review_scores = [ 100 * r.asWeight() for r in self.reviews ]
self.score = self.score_downloads + sum(review_scores)
diff --git a/app/models/threads.py b/app/models/threads.py
index 2ca1d7b4..22bec4bb 100644
--- a/app/models/threads.py
+++ b/app/models/threads.py
@@ -177,7 +177,7 @@ class PackageReview(db.Model):
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
author = db.relationship("User", foreign_keys=[author_id], back_populates="reviews")
- recommends = db.Column(db.Boolean, nullable=False)
+ rating = db.Column(db.Integer, nullable=False)
thread = db.relationship("Thread", uselist=False, back_populates="review")
votes = db.relationship("PackageReviewVote", back_populates="review", cascade="all, delete, delete-orphan")
@@ -194,7 +194,8 @@ class PackageReview(db.Model):
def getAsDictionary(self, include_package=False):
pos, neg, _user = self.get_totals()
ret = {
- "is_positive": self.recommends,
+ "is_positive": self.rating >= 3,
+ "rating": self.rating,
"user": {
"username": self.author.username,
"display_name": self.author.display_name,
@@ -211,8 +212,8 @@ class PackageReview(db.Model):
ret["package"] = self.package.getAsDictionaryKey()
return ret
- def asSign(self):
- return 1 if self.recommends else -1
+ def asWeight(self):
+ return 2.0 * self.rating / 5.0 - 1
def getEditURL(self):
return self.package.getURL("packages.review")
diff --git a/app/templates/index.html b/app/templates/index.html
index 29549f41..239608f6 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -69,9 +69,11 @@
- +{{ package.reviews | selectattr("recommends") | list | length }}
+ +{{ package.reviews | selectattr("rating", "equalto", 5) | list | length }}
/
- -{{ package.reviews | rejectattr("recommends") | list | length }}
+ {{ package.reviews | selectattr("rating", "equalto", 3) | list | length }}
+ /
+ -{{ package.reviews | selectattr("rating", "equalto", 1) | list | length }}
diff --git a/app/templates/macros/reviews.html b/app/templates/macros/reviews.html
index 972b30d9..0c95c80b 100644
--- a/app/templates/macros/reviews.html
+++ b/app/templates/macros/reviews.html
@@ -31,10 +31,12 @@
- {% if review.recommends %}
+ {% if review.rating > 3 %}
- {% else %}
+ {% elif review.rating < 3 %}
+ {% else %}
+
{% endif %}
{% if review.thread %}
@@ -112,11 +114,15 @@
+
@@ -149,11 +155,15 @@
-