Создание динамической системы оценки квестов и значков в DjangoPython

Программы на Python
Anonymous
Создание динамической системы оценки квестов и значков в Django

Сообщение Anonymous »

Модели

Quest

Код: Выделить всё

class Quest(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
criteria = models.JSONField()  # Store criteria as JSON
reward_points = models.IntegerField(default=0)

def __str__(self):
return self.name

Значок

Код: Выделить всё

class Badge(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
criteria = models.JSONField()  # Store criteria as JSON
reward_points = models.IntegerField(default=0)

def __str__(self):
return self.name

UserQuestProgress

Код: Выделить всё

class UserQuestProgress(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
quest = models.ForeignKey(Quest, on_delete=models.CASCADE)
current_value = models.IntegerField(default=0)
target_value = models.IntegerField()
completed = models.BooleanField(default=False)
completed_at = models.DateTimeField(null=True, blank=True)

def save(self, *args, **kwargs):
if self.current_value >= self.target_value:
self.completed = True
if not self.completed_at:
self.completed_at = timezone.now()
super().save(*args, **kwargs)

UserBadgeProgress

Код: Выделить всё

class UserBadgeProgress(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
badge = models.ForeignKey(Badge, on_delete=models.CASCADE)
current_value = models.IntegerField(default=0)
target_value = models.IntegerField()
completed = models.BooleanField(default=False)
completed_at = models.DateTimeField(null=True, blank=True)

def save(self, *args, **kwargs):
if self.current_value >= self.target_value:
self.completed = True
if not self.completed_at:
self.completed_at = timezone.now()
super().save(*args, **kwargs)
Я работаю над платформой для социальных сетей, где пользователи могут получать награды (квесты и значки) в зависимости от своей деятельности. Моя цель — создать гибкую систему, которая сможет динамически оценивать эти награды без необходимости жестко запрограммировать новые условия каждый раз, когда мы вводим новый квест или значок.
Что мы сделали? Далеко:
  • Модели для квестов и значков: мы определили модели для квестов, значков< /code>, UserQuestProgress и UserBadgeProgress, которые отслеживают прогресс пользователя.
  • Критерий JSON: мы используем JSON для определения критериев. за каждый квест или значок. Например:

    Код: Выделить всё

    {
    "Post": {
    "reaction_type_within_timeframe": ["dislike", 30, 50]
    }
    }
    
  • Функция оценки: у нас есть функция Assessment_quest, которая обрабатывает критерии JSON для вычисления прогресса пользователя:

    Код: Выделить всё

    def evaluate_quest(user, criteria):
    # Evaluate criteria and calculate progress
    
  • Задача «Сельдерей»: когда пользователь переходит на страницу квеста или значка, запускается задача «Сельдерей», чтобы оценить его прогресс и обновить UserQuestProgress или UserBadgeProgress.
Текущие проблемы:
  • Сценарии жесткого кодирования. Текущая реализация по-прежнему требует от нас жестко запрограммировать новые сценарии в функции Assessment_quest. Мы стремимся динамически обрабатывать различные сложные условия, но сталкиваемся с неизвестными будущими сценариями.
  • Примеры сценариев:

    Публикуйте обновление статуса или любое другое сообщение впервые:

    Код: Выделить всё

    {
    "Post": {
    "first_post": true
    }
    }
    
  • Вы заполнили все поля информации своего профиля:

    Код: Выделить всё

    {
    "Profile": {
    "complete_profile": true
    }
    }
    
  • Поставьте 50 лайков и/или лайков на посты своих подписчиков:

    Код: Выделить всё

    {
    "Reaction": {
    "reaction_type": ["like", "love"],
    "count": 50,
    "on_followers_posts": true
    }
    }
    
  • Получено более 100 лайков и более 50 комментариев к одному сообщению:

    Код: Выделить всё

    {
    "Post": {
    "likes": 100,
    "comments": 50,
    "single_post": true
    }
    }
    
  • Пользователь первым отреагировал на сообщения более чем 50 друзей:

    Код: Выделить всё

    {
    "Reaction": {
    "first_reaction": true,
    "on_friends_posts": true,
    "count":  50
    }
    }
    

Рабочий процесс:
  • Когда пользователь переходит на страницу квеста или значка, запускается задача Celery.
  • Задача использует get_or_create(user, quest|badge), чтобы убедиться в наличии экземпляра прогресса.
  • Функция Assessment_quest вызывается для вычисления текущего и целевого значений.
    Модели UserQuestProgress и UserBadgeProgress переопределили методы save, чтобы отметить завершение, если текущее значение соответствует целевому значению или превышает его.
  • При сохранении сигнал post_save уведомляет пользователя через WebSocket, если квест или значок выполнен.

Текущая функция Assessment_quest:

Код: Выделить всё

def evaluate_quest(user, criteria):
criteria = json.loads(criteria)
progress = 0
total_conditions = 0

for model_name, conditions in criteria.items():
Model = apps.get_model('your_app_name', model_name)  # Dynamically get the model
queryset = Model.objects.filter(user=user)

for field, condition in conditions.items():
if field == 'time_range':
start_time, end_time, target = condition
queryset = queryset.filter(
created_at__time__gte=start_time,
created_at__time__lte=end_time
)
current_value = queryset.count()

elif field == 'reaction_type_within_timeframe':
reaction_type, timeframe, target = condition
time_delta = timedelta(minutes=timeframe)
queryset = queryset.annotate(
reactions_within_timeframe=Count(
Case(
When(
reactions__reaction_type=reaction_type,
reactions__created_at__lte=ExpressionWrapper(
F('created_at') + time_delta,
output_field=DurationField()
),
then='reactions'
)
)
)
)
current_value = queryset.filter(reactions_within_timeframe__gte=target).count()

elif field == 'consecutive_days':
target_days = condition
dates = queryset.values_list('created_at', flat=True)
dates = sorted(dates)
current_streak = 0
max_streak = 0

for i in range(1, len(dates)):
if (dates[i] - dates[i - 1]).days == 1:
current_streak += 1
else:
max_streak = max(max_streak, current_streak)
current_streak = 0

max_streak = max(max_streak, current_streak)
current_value = max_streak
target = target_days

elif field == 'linked_social_networks':
target_value = condition
social_fields = [
'facebook_username', 'twitter_username', 'instagram_username',
'twitch_username', 'google_username', 'youtube_username',
'patreon_username', 'discord_username', 'deviantart_username',
'behance_username', 'dribble_username', 'artstation_username'
]
current_value = sum(bool(getattr(queryset.first(), field)) for field in social_fields)
target = target_value

else:
value, target = condition
if isinstance(value, str):
queryset = queryset.filter(**{field: value})
elif isinstance(value, dict) and 'range' in value:
start, end = value['range']
queryset = queryset.filter(**{f"{field}__gte": start, f"{field}__lte": end})
current_value = queryset.count()

# Calculate progress for this condition
if target > 0:
progress += current_value / target
total_conditions += 1

# Average progress across all conditions
if total_conditions >  0:
progress = progress / total_conditions

return progress
Проблемы:
  • Текущая реализация по-прежнему требует от нас жестко запрограммировать конкретные условия в Assessment_quest.
  • Нам нужен более динамичный подход, позволяющий обрабатывать неизвестные будущие сценарии без необходимости изменения кода.
Запрос на помощь:
  • Как мы можем сделать функцию Assessment_quest более динамичной для обработки различных сложных условиях без жесткого кодирования каждого нового сценария?
  • Существуют ли в Django какие-либо библиотеки или шаблоны, которые могут помочь достичь такой гибкости?
  • Примеры сложных условий, которые нам нужно обработать:

    Первую публикацию обновления статуса.
  • Заполнение всех полей с информацией профиля.
  • Поставление 50 лайков и/или лайков на посты подписчиков.
  • Получение 100+ лайков и 50+
комментирует одно сообщение.
  • Реагирует первым на 50 + сообщения друзей.
Текущий рабочий процесс:
  • < li>Пользователь переходит на страницу квеста или значка, запуская задачу Celery.
  • Задача оценивает все квесты или значки для пользователя с помощью get_or_create(user, quest|badge) .
  • Функция Assessment_quest вызывается для расчета прогресса.
  • Прогресс обновляется на основе ответа от Assessment_quest функция.
  • Метод save в UserQuestProgress и UserBadgeProgress автоматически отмечает завершение, если текущее значение соответствует целевому значению или превышает его.
  • Сигнал post_save уведомляет пользователя через WebSocket о выполнении квеста или значка.
Любые идеи или предложения Мы будем очень признательны за то, чтобы сделать эту систему более надежной и динамичной. Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/788 ... -in-django

Вернуться в «Python»