mod: модели и админка (6) драфт
This commit is contained in:
@@ -206,6 +206,10 @@ LVL_PRE_MODERATED = 10
|
|||||||
LVL_MODERATED = 30
|
LVL_MODERATED = 30
|
||||||
LVL_LOCK_FOR_DELETION = 1000
|
LVL_LOCK_FOR_DELETION = 1000
|
||||||
|
|
||||||
|
# ====== Направление и веса для голосования (ТОЛЛЬКО ЦЕЛЫЕ ЧИСЛА) ======
|
||||||
|
VOTE_LIKE = 1
|
||||||
|
VOTE_CLAIM = -2
|
||||||
|
|
||||||
# ====== Гамма для вычисления рейтинга ======
|
# ====== Гамма для вычисления рейтинга ======
|
||||||
GAMMA = 1.5
|
GAMMA = 1.5
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class TbHypn0Item(models.Model):
|
|||||||
• f_score: оценка для стратегии хранения и умной очистки (Smart Retention)
|
• f_score: оценка для стратегии хранения и умной очистки (Smart Retention)
|
||||||
• i_level: уровень хранения (свежее, модерированное, защищенное от удаления)
|
• i_level: уровень хранения (свежее, модерированное, защищенное от удаления)
|
||||||
• is_public: публичный (доступный по внешней ссылке)
|
• is_public: публичный (доступный по внешней ссылке)
|
||||||
|
• s_author_fingerprint: хеш-код браузера автора
|
||||||
• s_promo_url: промо-ссылка (с дополнительной об авторе-спонсоре)
|
• s_promo_url: промо-ссылка (с дополнительной об авторе-спонсоре)
|
||||||
• s_promo_title: заголовок промо-ссылки или бейджа
|
• s_promo_title: заголовок промо-ссылки или бейджа
|
||||||
• i_promo_clicks: количество кликов по промо-ссылке
|
• i_promo_clicks: количество кликов по промо-ссылке
|
||||||
@@ -105,6 +106,14 @@ class TbHypn0Item(models.Model):
|
|||||||
verbose_name="Публичный доступ",
|
verbose_name="Публичный доступ",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Автор
|
||||||
|
s_author_fingerprint = models.CharField(
|
||||||
|
max_length=64,
|
||||||
|
db_index=True, # ??? -- подумать нужно или нет
|
||||||
|
help_text="SHA256(visitor_uuid + SECRET_KEY) для защиты от накрутки и разделения устройств",
|
||||||
|
verbose_name="Хэш устройства/отпечаток",
|
||||||
|
)
|
||||||
|
|
||||||
# Промо-блок (на перспективу)
|
# Промо-блок (на перспективу)
|
||||||
s_promo_url = models.URLField(
|
s_promo_url = models.URLField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -143,19 +152,21 @@ class TbHypn0Item(models.Model):
|
|||||||
|
|
||||||
def increment_views(self):
|
def increment_views(self):
|
||||||
"""Безопасный инкремент просмотров"""
|
"""Безопасный инкремент просмотров"""
|
||||||
TbHypn0Item.objects.filter(id=self.id).update(i_views_count=F('i_views_count') + 1)
|
TbHypn0Item.objects.get(id=self.id).update(i_views_count=F('i_views_count') + 1)
|
||||||
|
|
||||||
def increment_likes(self):
|
def increment_likes(self):
|
||||||
"""Безопасный инкремент лайков"""
|
"""Безопасный инкремент лайков"""
|
||||||
TbHypn0Item.objects.filter(id=self.id).update(i_likes_count=F('i_likes_count') + 1)
|
TbHypn0Item.objects.get(id=self.id).update(i_likes_count=F('i_likes_count') + 1)
|
||||||
|
# Здесь будет код для записи в TbVote
|
||||||
|
|
||||||
def increment_claims(self):
|
def increment_claims(self):
|
||||||
"""Безопасный инкремент жалоб"""
|
"""Безопасный инкремент жалоб"""
|
||||||
TbHypn0Item.objects.filter(id=self.id).update(i_claims_count=F('i_claims_count') + 1)
|
TbHypn0Item.objects.get(id=self.id).update(i_claims_count=F('i_claims_count') + 1)
|
||||||
|
# Здесь будет код для записи в TbVote
|
||||||
|
|
||||||
def increment_promo_clicks(self):
|
def increment_promo_clicks(self):
|
||||||
"""Безопасный инкремент кликов по промо"""
|
"""Безопасный инкремент кликов по промо"""
|
||||||
TbHypn0Item.objects.filter(id=self.id).update(i_promo_clicks=F('i_promo_clicks') + 1)
|
TbHypn0Item.objects.get(id=self.id).update(i_promo_clicks=F('i_promo_clicks') + 1)
|
||||||
|
|
||||||
def rescore(self):
|
def rescore(self):
|
||||||
"""Пересчет рейтинга i_promo_clicks
|
"""Пересчет рейтинга i_promo_clicks
|
||||||
@@ -163,9 +174,9 @@ class TbHypn0Item(models.Model):
|
|||||||
(«гравитации»), а не слепой Х-дневный таймер:
|
(«гравитации»), а не слепой Х-дневный таймер:
|
||||||
$$\text{Score} = \frac{\text{Likes} + \text{Bonus}_{\text{moderator}}}{(\text{Age in hours} + 2)^\gamma}$$
|
$$\text{Score} = \frac{\text{Likes} + \text{Bonus}_{\text{moderator}}}{(\text{Age in hours} + 2)^\gamma}$$
|
||||||
"""
|
"""
|
||||||
age_ih_hours = (timezone.now - self.d_created_at).total_seconds() / 3600 + 1
|
age_ih_hours = (timezone.now() - self.d_created_at).total_seconds() / 3600 + 1
|
||||||
new_score = (self.i_likes_count + self.i_level) / ((age_ih_hours + 2) ** GAMMA)
|
new_score = (self.i_likes_count + self.i_level) / ((age_ih_hours + 2) ** GAMMA)
|
||||||
TbHypn0Item.objects.filter(id=self.id).update(i_promo_clicks=new_score)
|
TbHypn0Item.objects.get(id=self.id).update(f_score=new_score)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -205,19 +216,30 @@ class TbVote(models.Model):
|
|||||||
|
|
||||||
Поля:
|
Поля:
|
||||||
• k_item: ключ объекта (foreign key), за который голосуют
|
• k_item: ключ объекта (foreign key), за который голосуют
|
||||||
|
• i_direction: направление голоса
|
||||||
• s_fingerprint: Хэш-отпечаток клиент/браузер/secret для защиты он накрутки
|
• s_fingerprint: Хэш-отпечаток клиент/браузер/secret для защиты он накрутки
|
||||||
• d_created_at: дата создания
|
• d_created_at: дата создания
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Direction(models.IntegerChoices):
|
||||||
|
LIKE = VOTE_LIKE, 'Like'
|
||||||
|
CLIME = VOTE_CLAIM, 'Clime'
|
||||||
|
|
||||||
k_item = models.ForeignKey(
|
k_item = models.ForeignKey(
|
||||||
TbHypn0Item,
|
TbHypn0Item,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="votes",
|
related_name="votes",
|
||||||
verbose_name="Картина",
|
verbose_name="Картина",
|
||||||
)
|
)
|
||||||
|
i_direction = models.IntegerField(
|
||||||
|
choices=Direction.choices,
|
||||||
|
default=Direction.LIKE,
|
||||||
|
verbose_name="Направление",
|
||||||
|
help_text="Направление голоса",
|
||||||
|
)
|
||||||
s_fingerprint = models.CharField(
|
s_fingerprint = models.CharField(
|
||||||
max_length=64,
|
max_length=64,
|
||||||
db_index=True,
|
db_index=True, # ??? -- подумать нужно или нет
|
||||||
help_text="SHA256(visitor_uuid + SECRET_KEY) для защиты от накрутки и разделения устройств",
|
help_text="SHA256(visitor_uuid + SECRET_KEY) для защиты от накрутки и разделения устройств",
|
||||||
verbose_name="Хэш устройства/отпечаток",
|
verbose_name="Хэш устройства/отпечаток",
|
||||||
)
|
)
|
||||||
@@ -238,7 +260,7 @@ class TbVote(models.Model):
|
|||||||
ordering = ["-d_created_at"]
|
ordering = ["-d_created_at"]
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Голос за {self.k_item.hash_id} [{self.s_fingerprint[:8]}...]"
|
return f"Голос за {self.k_item.s_hash_id} [{self.s_fingerprint[:8]}...]"
|
||||||
|
|
||||||
|
|
||||||
class TbBlogPost(models.Model):
|
class TbBlogPost(models.Model):
|
||||||
|
|||||||
Reference in New Issue
Block a user