Files
OnlineJudge/achievement/serializers.py

96 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.core.cache import cache
from rest_framework import serializers
from account.models import User
from achievement.models import Achievement, UserAchievement
def get_active_user_count():
"""获得率的分母:未禁用用户总数,缓存 1 小时。
不用"有过提交的用户数"这类动态口径:分母波动会让同一个成就的获得率
忽高忽低,学生会当成 bug。
"""
count = cache.get("achievement_active_user_count")
if count is None:
count = User.objects.filter(is_disabled=False).count()
cache.set("achievement_active_user_count", count, 3600)
return count
class AchievementSerializer(serializers.ModelSerializer):
unlocked = serializers.BooleanField(read_only=True)
unlock_time = serializers.DateTimeField(read_only=True, allow_null=True)
backfilled = serializers.BooleanField(read_only=True)
progress = serializers.IntegerField(read_only=True, allow_null=True)
unlock_rate = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
description = serializers.SerializerMethodField()
icon = serializers.SerializerMethodField()
# 条件三件套也必须跟着遮掉:只遮名称和描述、却明文下发
# metric/operator/threshold学生打开 DevTools 就知道"凌晨提交 10 次"
# 隐藏成就的意义全部作废
metric = serializers.SerializerMethodField()
operator = serializers.SerializerMethodField()
threshold = serializers.SerializerMethodField()
class Meta:
model = Achievement
fields = (
"id",
"name",
"description",
"icon",
"rarity",
"hidden",
"metric",
"operator",
"threshold",
"unlocked",
"unlock_time",
"backfilled",
"progress",
"unlock_rate",
)
def _masked(self, obj):
return obj.hidden and not getattr(obj, "unlocked", False)
def get_name(self, obj):
return "???" if self._masked(obj) else obj.name
def get_description(self, obj):
return "达成条件保密" if self._masked(obj) else obj.description
def get_icon(self, obj):
return "" if self._masked(obj) else obj.icon
def get_metric(self, obj):
return None if self._masked(obj) else obj.metric
def get_operator(self, obj):
return None if self._masked(obj) else obj.operator
def get_threshold(self, obj):
return None if self._masked(obj) else obj.threshold
def get_unlock_rate(self, obj):
total = get_active_user_count()
if not total:
return 0.0
return round(obj.unlock_count / total * 100, 1)
class PendingAchievementSerializer(serializers.ModelSerializer):
"""待弹窗的解锁记录,无需打码(已解锁)。"""
id = serializers.IntegerField(source="achievement_id", read_only=True)
name = serializers.CharField(source="achievement.name", read_only=True)
description = serializers.CharField(source="achievement.description", read_only=True)
icon = serializers.CharField(source="achievement.icon", read_only=True)
rarity = serializers.CharField(source="achievement.rarity", read_only=True)
class Meta:
model = UserAchievement
fields = ("id", "name", "description", "icon", "rarity")