feat: new “show achievement” page

This commit is contained in:
vas3k 2024-03-18 16:38:41 +01:00
parent 12dfe11fc2
commit 4b9675bf43
10 changed files with 152 additions and 33 deletions

View File

@ -20,7 +20,7 @@ from comments.views import create_comment, edit_comment, delete_comment, show_co
from common.feature_flags import feature_switch
from landing.views import landing, docs, godmode_network_settings, godmode_digest_settings, godmode_settings, \
godmode_invite
from misc.views import stats, network, robots, generate_ical_invite, generate_google_invite
from misc.views import stats, network, robots, generate_ical_invite, generate_google_invite, show_achievement
from rooms.views import redirect_to_room_chat, list_rooms
from notifications.views import render_weekly_digest, email_unsubscribe, email_confirm, render_daily_digest, \
email_digest_switch, link_telegram
@ -123,6 +123,7 @@ urlpatterns = [
path("intro/", intro, name="intro"),
path("people/", people, name="people"),
path("achievements/", RedirectView.as_view(url="/stats", permanent=True), name="achievements"),
path("achievements/<slug:achievement_code>/", show_achievement, name="show_achievement"),
path("stats/", stats, name="stats"),
path("profile/tag/<str:tag_code>/toggle/", toggle_tag, name="toggle_tag"),

View File

@ -0,0 +1,57 @@
{% extends "layout.html" %}
{% load static %}
{% load users %}
{% load text_filters %}
{% block title %}
Ачивка «{{ achievement.name }}» — {{ block.super }}
{% endblock %}
{% block og_tags %}
<meta property="og:title" content="Ачивка «{{ achievement.name }}» — {{ settings.APP_NAME }}">
<meta property="og:site_name" content="{{ settings.APP_NAME }}">
<meta property="og:url" content="{{ settings.APP_HOST }}">
<meta property="og:type" content="website" />
<meta property="og:description" content="">
<meta property="og:image" content="{% static "images/share.png" %}">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Ачивка «{{ achievement.name }}» — {{ settings.APP_NAME }}">
<meta name="twitter:description" content="">
<meta name="twitter:image" content="{% static "images/share.png" %}">
{% endblock %}
{% block content %}
<div class="stats">
<div class="stats-achievement-details">
{% include "achievements/widgets/achievement.html" %}
<div class="stats-achievement-details__info">
<span><strong>Название:</strong> {{ achievement.name }}</span>
<span><strong>Описание:</strong> {{ achievement.description }}</span>
<span><strong>Награждено:</strong> {{ users|length }} {{ users|length|rupluralize:"человек,человека,человек" }}</span>
<span><strong>Редкость:</strong> {{ rarity }}%</span>
{% if rarity > 75 %}
<span>Легендарная и редкая ачивка. Тут только самые лучшие!</span>
{% elif rarity > 45 %}
<span>Довольно сложная ачивка. Нужно было постараться, чтобы ее заслужить.</span>
{% elif rarity > 10 %}
<span>Хорошая ачивка за хорошую работу!</span>
{% else %}
<span>Довольно популярная ачивка, много у кого есть.</span>
{% endif %}
</div>
</div>
<div class="stats-achievements-users">
{% for user in users %}
<a href="{% url "profile" user.slug %}" class="top-user top-user-medium zoom-on-hover">
<span class="avatar top-user-avatar">
<img src="{{ user.get_avatar }}" alt="Аватар {{ user.full_name }}" loading="lazy" />
</span>
<span class="top-user-name">{{ user.full_name }}</span>
</a>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
<a href="{% url "show_achievement" achievement.code %}" class="user-achievement">
<span class="user-achievement-wrapper">
<span class="user-achievement-front" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
<img src="{{ achievement.image }}" class="user-badge-image" alt="{{ achievement.code }}">
<span class="user-achievement-name">{{ achievement.name }}</span>
</span>
<span class="user-achievement-back" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
{{ achievement.description }}
</span>
</span>
</a>

View File

@ -133,17 +133,7 @@
{% for achievement in achievements %}
<div class="stats-achievements">
<div class="stats-achievements-badge">
<span class="user-achievement">
<span class="user-achievement-wrapper">
<div class="user-achievement-front" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
<img src="{{ achievement.image }}" class="user-badge-image" alt="{{ achievement.code }}">
<span class="user-achievement-name">{{ achievement.name }}</span>
</div>
<div class="user-achievement-back" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
{{ achievement.description }}
</div>
</span>
</span>
{% include "achievements/widgets/achievement.html" %}
</div>
<div class="stats-achievements-users">
{% for user in achievement.achievement_users|slice:":25" %}

View File

@ -313,17 +313,7 @@
<div class="profile-achievements">
<div class="user-achievements">
{% for achievement in achievements %}
<span class="user-achievement">
<span class="user-achievement-wrapper">
<div class="user-achievement-front" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
<img src="{{ achievement.image }}" class="user-badge-image" alt="{{ achievement.code }}">
<span class="user-achievement-name">{{ achievement.name }}</span>
</div>
<div class="user-achievement-back" {% if achievement.style %}style="{{ achievement.style }}"{% endif %}>
{{ achievement.description }}
</div>
</span>
</span>
{% include "achievements/widgets/achievement.html" %}
{% endfor %}
</div>
</div>

View File

@ -243,6 +243,11 @@
perspective: 1000px;
}
.user-achievement:hover {
color: #FFF;
text-decoration: none;
}
.user-achievement:hover .user-achievement-wrapper {
transform: rotateY(180deg);
}

View File

@ -166,6 +166,30 @@
}
}
.stats-achievement-details {
display: flex;
justify-content: center;
}
.stats-achievement-details .user-achievement {
width: 250px;
height: 350px;
font-size: 180%;
}
.stats-achievement-details .user-badge-image {
max-height: 180px;
}
.stats-achievement-details__info {
max-width: 60%;
font-size: 150%;
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
}
.stats-badges-latest {
display: flex;
flex-direction: row;

View File

@ -3,15 +3,15 @@ from urllib.parse import urlencode
import pytz
from django.db.models import Count, Q, Sum
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.http import HttpResponse, Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_GET
from icalendar import Calendar, Event
from authn.decorators.auth import require_auth
from badges.models import UserBadge
from misc.models import NetworkGroup
from users.models.achievements import Achievement
from users.models.achievements import Achievement, UserAchievement
from users.models.user import User
@ -58,6 +58,34 @@ def stats(request):
})
@require_auth
def show_achievement(request, achievement_code):
achievement = get_object_or_404(Achievement, code=achievement_code)
if not achievement.is_visible:
raise Http404()
users = User.objects.filter(achievements__achievement_id=achievement_code)
# calculate rarity of the achievement
achievement_stats = dict(
UserAchievement.objects.all()
.values("achievement_id")
.annotate(total=Count("achievement_id"))
.order_by("-total")
.values_list("achievement_id", "total")
)
total_count = len(achievement_stats.values())
index = list(achievement_stats.keys()).index(achievement_code)
rarity = (index / total_count) * 100
return render(request, "achievements/show_achievement.html", {
"achievement": achievement,
"users": users,
"rarity": round(rarity, 1)
})
@require_auth
def network(request):
network_groups = NetworkGroup.visible_objects()

View File

@ -5,9 +5,9 @@ from django.conf import settings
from django.core.management import BaseCommand
from django.db.models import Q
from django.template import loader
from django_q.tasks import async_task
from notifications.email.sender import send_mass_email, send_transactional_email
from notifications.telegram.common import send_telegram_message, Chat
from users.models.user import User
log = logging.getLogger(__name__)
@ -18,7 +18,8 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument("--users", nargs=1, type=str, required=True)
parser.add_argument("--template", nargs=1, type=str, required=True)
parser.add_argument("--email-template", nargs=1, type=str, required=True)
parser.add_argument("--telegram-template", nargs=1, type=str, required=False)
parser.add_argument("--title", nargs=1, type=str, required=True)
parser.add_argument("--promo", nargs=1, type=bool, required=False, default=False)
@ -38,9 +39,14 @@ class Command(BaseCommand):
self.stdout.write("Aborted")
return
# find the template
# find all necessary templates for email and telegram
email_template = loader.get_template(options.get("email_template")[0])
telegram_template = None
if options.get("telegram_template"):
telegram_template = loader.get_template(options.get("telegram_template")[0])
is_promo = options.get("promo")
template = loader.get_template(options.get("template")[0])
# send emails to existing users
for user in users:
@ -55,17 +61,24 @@ class Command(BaseCommand):
sender(
recipient=user.email,
subject=options.get("title")[0],
html=template.render({"user": user}),
html=email_template.render({"user": user}),
unsubscribe_link=f"{settings.APP_HOST}/notifications/unsubscribe/{user.id}/{secret_code}/",
)
if telegram_template and user.telegram_id:
self.stdout.write(f"Sending telegram message to {user.telegram_id}...")
send_telegram_message(
chat=Chat(id=user.telegram_id),
text=telegram_template.render({"user": user}),
)
# send emails to not found users
for email in not_found_users:
self.stdout.write(f"Sending email to {email}...")
send_transactional_email(
recipient=email,
subject=options.get("title")[0],
html=template.render(),
html=email_template.render(),
)
self.stdout.write("Done 🥙")

View File

@ -26,7 +26,7 @@ def async_create_or_update_achievement(user_achievement: UserAchievement):
send_telegram_image(
chat=Chat(id=user.telegram_id),
image_url=achievement.image,
text=render_html_message("achievement.html", user=user, achievement=achievement),
text=render_html_message("show_achievement.html", user=user, achievement=achievement),
)
# emails