mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 22:12:24 +01:00
Profile medals: refactor code
This commit is contained in:
parent
e06ac1689c
commit
034e5382ec
@ -15,10 +15,12 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import math
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from flask import *
|
||||
from flask_babel import gettext
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import func, and_, or_
|
||||
|
||||
from app.models import *
|
||||
from app.tasks.forumtasks import checkForumAccount
|
||||
@ -44,6 +46,159 @@ def by_forums_username(username):
|
||||
return render_template("users/forums_no_such_user.html", username=username)
|
||||
|
||||
|
||||
class Medal:
|
||||
description: str
|
||||
color: Optional[str]
|
||||
icon: str
|
||||
title: Optional[str]
|
||||
progress: Optional[Tuple[int, int]]
|
||||
|
||||
def __init__(self, description: str, **kwargs):
|
||||
self.description = description
|
||||
self.color = kwargs.get("color", "white")
|
||||
self.icon = kwargs.get("icon", None)
|
||||
self.title = kwargs.get("title", None)
|
||||
self.progress = kwargs.get("progress", None)
|
||||
|
||||
@classmethod
|
||||
def make_unlocked(cls, color: str, icon: str, title: str, description: str):
|
||||
return Medal(description=description, color=color, icon=icon, title=title)
|
||||
|
||||
@classmethod
|
||||
def make_locked(cls, description: str, progress: Tuple[int, int]):
|
||||
return Medal(description=description, progress=progress)
|
||||
|
||||
|
||||
def place_to_color(place: int) -> str:
|
||||
if place == 1:
|
||||
return "gold"
|
||||
elif place == 2:
|
||||
return "#888"
|
||||
elif place == 3:
|
||||
return "#cd7f32"
|
||||
else:
|
||||
return "white"
|
||||
|
||||
|
||||
def get_user_medals(user: User) -> Tuple[List[Medal], List[Medal]]:
|
||||
unlocked = []
|
||||
locked = []
|
||||
|
||||
#
|
||||
# REVIEWS
|
||||
#
|
||||
|
||||
users_by_reviews = db.session.query(User.username, func.count(PackageReview.id).label("count")) \
|
||||
.select_from(User).join(PackageReview) \
|
||||
.group_by(User.username).order_by(text("count DESC")).all()
|
||||
try:
|
||||
review_boundary = users_by_reviews[math.floor(len(users_by_reviews) * 0.25)][1] + 1
|
||||
except IndexError:
|
||||
review_boundary = None
|
||||
users_by_reviews = [username for username, _ in users_by_reviews]
|
||||
|
||||
review_idx = None
|
||||
review_percent = None
|
||||
try:
|
||||
review_idx = users_by_reviews.index(user.username)
|
||||
review_percent = round(100 * review_idx / len(users_by_reviews), 1)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if review_percent and review_percent < 25:
|
||||
if review_idx == 0:
|
||||
title = gettext(u"Most reviews")
|
||||
description = gettext(
|
||||
u"%(display_name)s has written the most reviews on ContentDB.",
|
||||
display_name=user.display_name)
|
||||
elif review_idx <= 2:
|
||||
if review_idx == 1:
|
||||
title = gettext(u"2nd most reviews")
|
||||
else:
|
||||
title = gettext(u"3rd most reviews")
|
||||
description = gettext(
|
||||
u"This puts %(display_name)s in the top %(perc)s%%",
|
||||
display_name=user.display_name, perc=review_percent)
|
||||
else:
|
||||
title = gettext(u"Top %(perc)s%% reviewer", perc=review_percent)
|
||||
description = gettext(u"Only %(place)d users have written more reviews.", place=review_idx)
|
||||
|
||||
unlocked.append(Medal.make_unlocked(
|
||||
place_to_color(review_idx + 1), "fa-star-half-alt", title, description))
|
||||
else:
|
||||
description = gettext(u"Consider writing more reviews to get a medal.")
|
||||
if review_idx:
|
||||
description += " " + gettext(u"You are in place %(place)s.", place=review_idx + 1)
|
||||
locked.append(Medal.make_locked(
|
||||
description, (len(user.reviews), review_boundary)))
|
||||
|
||||
#
|
||||
# TOP PACKAGES
|
||||
#
|
||||
all_package_ranks = db.session.query(
|
||||
Package.type,
|
||||
Package.author_id,
|
||||
func.rank().over(
|
||||
order_by=db.desc(Package.score),
|
||||
partition_by=Package.type) \
|
||||
.label("rank")).order_by(db.asc(text("rank"))) \
|
||||
.filter_by(state=PackageState.APPROVED).subquery()
|
||||
|
||||
user_package_ranks = db.session.query(all_package_ranks) \
|
||||
.filter_by(author_id=user.id) \
|
||||
.filter(text("rank <= 30")) \
|
||||
.all()
|
||||
|
||||
user_package_ranks = next(
|
||||
(x for x in user_package_ranks if x[0] == PackageType.MOD or x[2] <= 10),
|
||||
None)
|
||||
if user_package_ranks:
|
||||
top_rank = user_package_ranks[2]
|
||||
top_type = PackageType.coerce(user_package_ranks[0]).value
|
||||
if top_rank == 1:
|
||||
title = gettext(u"Top %(type)s", type=top_type.lower())
|
||||
else:
|
||||
title = gettext(u"Top %(group)d %(type)s", group=top_rank, type=top_type.lower())
|
||||
|
||||
description = gettext(u"%(display_name)s has a %(type)s placed at #%(place)d.",
|
||||
display_name=user.display_name, type=top_type.lower(), place=top_rank)
|
||||
unlocked.append(
|
||||
Medal.make_unlocked(place_to_color(top_rank), "fa-trophy", title, description))
|
||||
|
||||
#
|
||||
# DOWNLOADS
|
||||
#
|
||||
total_downloads = db.session.query(func.sum(Package.downloads)) \
|
||||
.select_from(User) \
|
||||
.join(User.packages) \
|
||||
.filter(User.id == user.id,
|
||||
Package.state == PackageState.APPROVED).scalar()
|
||||
if total_downloads is None:
|
||||
pass
|
||||
elif total_downloads < 50000:
|
||||
description = gettext(u"Your packages have %(downloads)d downloads in total.", downloads=total_downloads)
|
||||
description += " " + gettext(u"First medal is at 50k.")
|
||||
locked.append(Medal.make_locked(description, (total_downloads, 50000)))
|
||||
else:
|
||||
if total_downloads >= 300000:
|
||||
place = 1
|
||||
title = gettext(u">300k downloads")
|
||||
elif total_downloads >= 100000:
|
||||
place = 2
|
||||
title = gettext(u">100k downloads")
|
||||
elif total_downloads >= 75000:
|
||||
place = 3
|
||||
title = gettext(u">75k downloads")
|
||||
else:
|
||||
place = 10
|
||||
title = gettext(u">50k downloads")
|
||||
description = gettext(u"Has received %(downloads)d downloads across all packages.",
|
||||
display_name=user.display_name, downloads=total_downloads)
|
||||
unlocked.append(Medal.make_unlocked(place_to_color(place), "fa-users", title, description))
|
||||
|
||||
return unlocked, locked
|
||||
|
||||
|
||||
@bp.route("/users/<username>/")
|
||||
def profile(username):
|
||||
user = User.query.filter_by(username=username).first()
|
||||
@ -62,49 +217,11 @@ def profile(username):
|
||||
.filter(Package.author != user) \
|
||||
.order_by(db.asc(Package.title)).all()
|
||||
|
||||
users_by_reviews = db.session.query(User.username, func.count(PackageReview.id).label("count")) \
|
||||
.select_from(User).join(PackageReview) \
|
||||
.group_by(User.username).order_by(text("count DESC")).all()
|
||||
try:
|
||||
review_boundary = users_by_reviews[math.floor(len(users_by_reviews) * 0.25)][1] + 1
|
||||
except IndexError:
|
||||
review_boundary = None
|
||||
users_by_reviews = [ username for username, _ in users_by_reviews ]
|
||||
|
||||
review_idx = None
|
||||
review_percent = None
|
||||
try:
|
||||
review_idx = users_by_reviews.index(user.username)
|
||||
review_percent = round(100 * review_idx / len(users_by_reviews), 1)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
total_downloads = db.session.query(func.sum(Package.downloads)) \
|
||||
.select_from(User) \
|
||||
.join(User.packages) \
|
||||
.filter(User.id == user.id, Package.state == PackageState.APPROVED).scalar() or 0
|
||||
|
||||
all_package_ranks = db.session.query(
|
||||
Package.type,
|
||||
Package.author_id,
|
||||
func.rank().over(order_by=db.desc(Package.score), partition_by=Package.type) \
|
||||
.label('rank')).order_by(db.asc(text("rank"))) \
|
||||
.filter_by(state=PackageState.APPROVED).subquery()
|
||||
|
||||
user_package_ranks = db.session.query(all_package_ranks) \
|
||||
.filter_by(author_id=user.id).first()
|
||||
min_package_rank = None
|
||||
min_package_type = None
|
||||
if user_package_ranks:
|
||||
min_package_rank = user_package_ranks[2]
|
||||
min_package_type = PackageType.coerce(user_package_ranks[0]).value
|
||||
|
||||
unlocked, locked = get_user_medals(user)
|
||||
# Process GET or invalid POST
|
||||
return render_template("users/profile.html", user=user,
|
||||
packages=packages, maintained_packages=maintained_packages,
|
||||
total_downloads=total_downloads,
|
||||
review_idx=review_idx, review_percent=review_percent, review_boundary=review_boundary,
|
||||
min_package_rank=min_package_rank, min_package_type=min_package_type)
|
||||
medals_unlocked=unlocked, medals_locked=locked)
|
||||
|
||||
|
||||
@bp.route("/users/<username>/check/", methods=["POST"])
|
||||
|
@ -123,153 +123,41 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row mb-5">
|
||||
{% if review_percent is not none and review_percent < 25 and review_idx >= 0 %}
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
{% if review_idx == 0 %}
|
||||
{% set badge_color = "gold" %}
|
||||
{% elif review_idx == 1 %}
|
||||
{% set badge_color = "#888" %}
|
||||
{% elif review_idx == 2 %}
|
||||
{% set badge_color = "#cd7f32" %}
|
||||
{% else %}
|
||||
{% set badge_color = "white" %}
|
||||
{% endif %}
|
||||
<div class="card-body media align-items-center">
|
||||
<i class="fas fa-star-half-alt ml-2 mr-4 text-size" style="font-size: 45px; color: {{ badge_color }};"></i>
|
||||
<div class="media-body">
|
||||
{% if review_idx == 0 %}
|
||||
<h5 class="mt-0">
|
||||
{{ _("Most reviews", perc=review_percent) }}
|
||||
</h5>
|
||||
<p class="my-0">
|
||||
{{ _("%(display_name)s has written the most reviews on ContentDB.",
|
||||
display_name=user.display_name) }}
|
||||
</p>
|
||||
{% elif review_idx <= 2 %}
|
||||
<h5 class="mt-0">
|
||||
{% if review_idx == 1 %}
|
||||
{{ _("2nd most reviews", perc=review_percent) }}
|
||||
{% else %}
|
||||
{{ _("3rd most reviews", perc=review_percent) }}
|
||||
{% endif %}
|
||||
</h5>
|
||||
<p class="my-0">
|
||||
{{ _("This puts %(display_name)s in the top %(perc)s%%",
|
||||
display_name=user.display_name, perc=review_percent) }}
|
||||
</p>
|
||||
{% else %}
|
||||
<h5 class="mt-0">
|
||||
{{ _("Top %(perc)s%% reviewer", perc=review_percent) }}
|
||||
</h5>
|
||||
<p class="my-0">
|
||||
{% if review_idx == 1 %}
|
||||
{{ _("Only 1 user has written more reviews.") }}
|
||||
{% else %}
|
||||
{{ _("Only %(place)d users have written more reviews.", place=review_idx) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% elif current_user == user %}
|
||||
<div class="col-md-4">
|
||||
<div class="border border-dark rounded p-3 text-muted my-0">
|
||||
<p>
|
||||
{{ _("Consider writing more reviews to get a medal.") }}
|
||||
{% if review_idx %}
|
||||
{{ _("You are in place %(place)s.", place=review_idx + 1) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width: {{ [100 * num_reviews / review_boundary, 100] | min }}%;"
|
||||
aria-valuenow="{{ num_reviews }}" aria-valuemin="0" aria-valuemax="{{ review_boundary }}">
|
||||
{{ _("%(value)d / %(target)d", value=num_reviews, target=review_boundary) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if total_downloads >= 50000 %}
|
||||
{% if total_downloads >= 300000 %}
|
||||
{% set badge_color = "gold" %}
|
||||
{% elif total_downloads >= 100000 %}
|
||||
{% set badge_color = "#888" %}
|
||||
{% elif total_downloads >= 75000 %}
|
||||
{% set badge_color = "#cd7f32" %}
|
||||
{% else %}
|
||||
{% set badge_color = "white" %}
|
||||
{% endif %}
|
||||
{% for medal in medals_unlocked %}
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body media align-items-center">
|
||||
<i class="fas fa-users ml-2 mr-4 text-size" style="font-size: 45px; color: {{ badge_color }};"></i>
|
||||
<i class="fas {{ medal.icon }} ml-2 mr-4 text-size"
|
||||
style="font-size: 45px; color: {{ medal.color }};"></i>
|
||||
<div class="media-body">
|
||||
<h5 class="mt-0">
|
||||
{% if total_downloads >= 300000 %}
|
||||
{{ _(">300k downloads") }}
|
||||
{% elif total_downloads >= 100000 %}
|
||||
{{ _(">100k downloads") }}
|
||||
{% elif total_downloads >= 75000 %}
|
||||
{{ _(">75k downloads") }}
|
||||
{% else %}
|
||||
{{ _(">50k downloads") }}
|
||||
{% endif %}
|
||||
{{ medal.title }}
|
||||
</h5>
|
||||
<p class="my-0">
|
||||
{{ _("Has received %(downloads)d downloads across all packages.",
|
||||
display_name=user.display_name, downloads=total_downloads) }}
|
||||
{{ medal.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% elif total_downloads > 0 and current_user == user %}
|
||||
<div class="col-md-4">
|
||||
<div class="border border-dark rounded p-3 text-muted my-0">
|
||||
<p>
|
||||
{{ _("Your packages have %(downloads)d downloads in total.", downloads=total_downloads) }}
|
||||
{{ _("First medal is at 50k.") }}
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width: {{ [100 * total_downloads / 50000, 100] | min }}%;"
|
||||
aria-valuenow="{{ total_downloads }}" aria-valuemin="0" aria-valuemax="50000">
|
||||
{{ _("%(value)d / %(target)d", value=total_downloads, target=50000) }}
|
||||
{% endfor %}
|
||||
{% if current_user == user %}
|
||||
{% for medal in medals_locked %}
|
||||
<div class="col-md-4">
|
||||
<div class="border border-dark rounded p-3 text-muted my-0">
|
||||
<p>
|
||||
{{ medal.description }}
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width: {{ [100 * medal.progress[0] / medal.progress[1], 100] | min }}%;"
|
||||
aria-valuenow="{{ medal.progress[0] }}" aria-valuemin="0" aria-valuemax="{{ medal.progress[1] }}">
|
||||
{{ _("%(value)d / %(target)d", value=medal.progress[0], target=medal.progress[1]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if min_package_rank is not none and min_package_rank <= 20 %}
|
||||
{% if min_package_rank == 1 %}
|
||||
{% set badge_color = "gold" %}
|
||||
{% elif min_package_rank == 2 %}
|
||||
{% set badge_color = "#888" %}
|
||||
{% elif min_package_rank == 3 %}
|
||||
{% set badge_color = "#cd7f32" %}
|
||||
{% else %}
|
||||
{% set badge_color = "white" %}
|
||||
{% endif %}
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body media align-items-center">
|
||||
<i class="fas fa-trophy ml-2 mr-4 text-size" style="font-size: 45px; color: {{ badge_color }};"></i>
|
||||
<div class="media-body">
|
||||
<h5 class="mt-0">
|
||||
{{ _("Top %(place)d %(type)s", place=min_package_rank, type=min_package_type) }}
|
||||
</h5>
|
||||
<p class="my-0">
|
||||
{{ _("%(display_name)s has a package placed at #%(place)d.",
|
||||
display_name=user.display_name, place=min_package_rank) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
Loading…
Reference in New Issue
Block a user