start project

This commit is contained in:
2024-08-29 14:21:58 +05:00
commit 456b5cdf3c
126 changed files with 1269 additions and 0 deletions

0
users/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
users/admin.py Normal file
View File

@@ -0,0 +1,16 @@
from django.contrib import admin
from .models import DailyReward, DailyRewardsList, User, Levels, Balance
admin.site.register(User)
admin.site.register(Balance)
admin.site.register(Levels)
admin.site.register(DailyReward)
admin.site.register(DailyRewardsList)

11
users/apps.py Normal file
View File

@@ -0,0 +1,11 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'users'
def ready(self):
from users.tasks import start_scheduler
start_scheduler()

View File

@@ -0,0 +1,123 @@
# Generated by Django 5.0.6 on 2024-08-26 11:43
import django.contrib.auth.models
import django.contrib.auth.validators
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='DailyRewardsList',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('active_day', models.IntegerField(blank=True, verbose_name='Активный день')),
('month_max_day', models.CharField(choices=[('До 28', 'До 28'), ('До 29', 'До 29'), ('До 30', 'До 30'), ('До 31', 'До 31')], max_length=250, verbose_name='Длительность месяца')),
],
options={
'verbose_name': 'Список наград',
'verbose_name_plural': 'Список наград',
},
),
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('photo', models.ImageField(upload_to='static/media/user_photo/', verbose_name='Аватарка пользователя')),
('tg_id', models.CharField(blank=True, max_length=250, verbose_name='Telegram ID')),
('tg_username', models.CharField(blank=True, max_length=250, verbose_name='Имя пользователя')),
('name', models.CharField(blank=True, max_length=300, verbose_name='Имя')),
('referral_code', models.CharField(blank=True, max_length=250, null=True, unique=True, verbose_name='Код рефферала')),
('coins_for_referral', models.BigIntegerField(blank=True, default=0, verbose_name='Монеты полученные от рефералов')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('referred_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='referrals', to=settings.AUTH_USER_MODEL)),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='Balance',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='Имя баланса')),
('wallet', models.CharField(blank=True, max_length=350, null=True, verbose_name='Кошелек')),
('currency', models.CharField(blank=True, max_length=250, verbose_name='Валюта')),
('balance', models.BigIntegerField(default=0, verbose_name='Количество монет/Сумма')),
('color', models.CharField(choices=[('Yl', 'Yellow'), ('BK', 'Black'), ('GR', 'Grey'), ('BL', 'Blue')], default='Yl', max_length=250, verbose_name='Цвет')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Баланс пользователя',
'verbose_name_plural': 'Балансы пользователей',
},
),
migrations.CreateModel(
name='DailyReward',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('day', models.IntegerField(verbose_name='День')),
('reward_amount', models.IntegerField(verbose_name='Сумма награды')),
('claimed', models.BooleanField(default=False, verbose_name='Была ли награда получена')),
('claim_date', models.DateTimeField(blank=True, null=True, verbose_name='Время получения награды')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Ежедневная награда',
'verbose_name_plural': 'Ежедневные награды',
},
),
migrations.CreateModel(
name='Levels',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=300, verbose_name='Имя')),
('level', models.IntegerField(verbose_name='Уровень')),
('photo', models.ImageField(upload_to='static/media/levels/', verbose_name='Фото уровня')),
('need_coins', models.IntegerField(verbose_name='Количество нужных монет')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Уровень',
'verbose_name_plural': 'Уровни',
},
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Сумма')),
('currency', models.CharField(max_length=10, verbose_name='Валюта')),
('status', models.CharField(default='Pending', max_length=50, verbose_name='Статус')),
('transaction_id', models.CharField(max_length=100, unique=True, verbose_name='ID транзакции')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

Binary file not shown.

Binary file not shown.

111
users/models.py Normal file
View File

@@ -0,0 +1,111 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.db.models.signals import post_save
from django.dispatch import receiver
# Users model
class User(AbstractUser):
photo = models.ImageField('Аватарка пользователя', upload_to='static/media/user_photo/')
tg_id = models.CharField('Telegram ID', max_length=250, blank=True)
tg_username = models.CharField('Имя пользователя', max_length=250, blank=True)
name = models.CharField('Имя', max_length=300, blank=True)
referral_code = models.CharField('Код рефферала', max_length=250, unique=True, blank=True, null=True)
referred_by = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='referrals')
coins_for_referral = models.BigIntegerField("Монеты полученные от рефералов", default=0, blank=True)
def __str__(self):
return self.tg_username
# Уровни пользователя
class Levels(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField('Имя',max_length=300, blank=True)
level = models.IntegerField("Уровень")
photo = models.ImageField('Фото уровня', upload_to='static/media/levels/')
need_coins = models.IntegerField("Количество нужных монет")
def __str__(self):
return f"Пользователь {self.user.username} - {self.name} {self.level}"
class Meta:
verbose_name = 'Уровень'
verbose_name_plural = 'Уровни'
#Баланс пользователя
class Balance(models.Model):
class ColorEnum(models.TextChoices):
YELLOW = 'Yl', _('Yellow')
BLACK = 'BK', _('Black')
GREY = 'GR', _('Grey')
BLUE = 'BL', _('Blue')
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField("Имя баланса")
wallet = models.CharField('Кошелек', max_length=350, blank=True, null=True)
currency = models.CharField('Валюта', max_length=250, blank=True)
balance = models.BigIntegerField("Количество монет/Сумма", default=0)
color = models.CharField('Цвет', choices=ColorEnum.choices, default=ColorEnum.YELLOW, max_length=250)
def __str__(self):
return f"Сумма {self.balance} {self.currency} - {self.user.username}"
class Meta:
verbose_name = 'Баланс пользователя'
verbose_name_plural = 'Балансы пользователей'
# Ежедневная награда
class DailyReward(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
day = models.IntegerField("День")
reward_amount = models.IntegerField("Сумма награды")
claimed = models.BooleanField('Была ли награда получена',default=False)
claim_date = models.DateTimeField('Время получения награды',null=True, blank=True)
def __str__(self):
return f"День {self.day} - {self.user.username}"
class Meta:
verbose_name = 'Ежедневная награда'
verbose_name_plural = 'Ежедневные награды'
# Список ежедневных наград
class DailyRewardsList(models.Model):
class MonthEnum(models.TextChoices):
TO28 = 'До 28', _('До 28')
TO29 = 'До 29', _('До 29')
TO30 = 'До 30', _('До 30')
TO31 = 'До 31', _('До 31')
active_day = models.IntegerField('Активный день', blank=True)
month_max_day = models.CharField('Длительность месяца', choices=MonthEnum.choices, max_length=250)
def __str__(self):
return f"Месяц с {self.month_max_day} днями - Активный день: {self.active_day}"
class Meta:
verbose_name = 'Список наград'
verbose_name_plural = 'Список наград'
from django.db import models
class Transaction(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
amount = models.DecimalField("Сумма", max_digits=10, decimal_places=2)
currency = models.CharField("Валюта", max_length=10)
status = models.CharField("Статус", max_length=50, default="Pending")
transaction_id = models.CharField("ID транзакции", max_length=100, unique=True)
created_at = models.DateTimeField("Дата создания", auto_now_add=True)
def __str__(self):
return f"Транзакция {self.transaction_id} пользователя {self.user.username}"

27
users/serializers.py Normal file
View File

@@ -0,0 +1,27 @@
from rest_framework import serializers
from .models import User, DailyReward, DailyRewardsList, Balance, Levels
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class DailyRewardSerializer(serializers.ModelSerializer):
class Meta:
model = DailyReward
fields = '__all__'
class DailyRewardsListSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class BalanceSerializer(serializers.ModelSerializer):
class Meta:
model = Balance
fields = '__all__'
class LevelsSerializer(serializers.ModelSerializer):
class Meta:
model = Levels
fields = '__all__'

66
users/tasks.py Normal file
View File

@@ -0,0 +1,66 @@
import threading
import time
import datetime
from .models import DailyRewardsList, DailyReward, User
def calculate_reward(day):
if day == 1:
return 20
elif day == 2:
return 50
elif day == 3:
return 100
elif day == 4:
return 150
elif day == 5:
return 200
elif day == 6:
return 250
elif day == 7:
return 300
elif day == 8:
return 350
elif day == 9:
return 400
elif day == 10:
return 500
else:
return 500 + (day - 10) * 100
def my_scheduled_job():
# Удаляем старые записи списка наград
DailyRewardsList.objects.all().delete()
# Определяем максимальное количество дней в текущем месяце
now = datetime.datetime.now()
next_month = now.month + 1 if now.month < 12 else 1
next_year = now.year if now.month < 12 else now.year + 1
max_day = (datetime.date(next_year, next_month, 1) - datetime.date(now.year, now.month, 1)).days
# Создаем новый DailyRewardsList
month_max_day = f'До {max_day}'
new_daily_rewards_list = DailyRewardsList.objects.create(active_day=1, month_max_day=month_max_day)
# Очищаем предыдущие награды
DailyReward.objects.all().delete()
# Создаем новые ежедневные награды для каждого пользователя
users = User.objects.all()
for user in users:
for day in range(1, max_day + 1):
reward_amount = calculate_reward(day)
DailyReward.objects.create(user=user, day=day, reward_amount=reward_amount)
def run_scheduler():
while True:
now = datetime.datetime.now()
# Проверяем, что текущее время 00:05
if now.day == 1 and now.hour == 21 and now.minute == 5:
my_scheduled_job()
# Ждем 60 секунд, чтобы избежать повторного выполнения задачи в эту же минуту
time.sleep(60)
def start_scheduler():
thread = threading.Thread(target=run_scheduler, daemon=True)
thread.start()

3
users/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

167
users/views.py Normal file
View File

@@ -0,0 +1,167 @@
from .models import User, DailyReward, DailyRewardsList, Balance, Levels
from .serializers import UserSerializer, DailyRewardSerializer, DailyRewardsListSerializer, BalanceSerializer, LevelsSerializer
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=True, methods=['get'])
def get_datetime(self, request):
# Получаем текущие дату и время
current_datetime = timezone.now()
return Response({'datetime': current_datetime})
@action(detail=True, methods=['get'])
def get_referrals(self, request, pk=None):
user = self.get_object()
# Процентные ставки для каждого уровня
referral_percentages = {
1: 0.10, # 10% за рефералы 1 уровня
2: 0.06, # 6% за рефералы 2 уровня
3: 0.03, # 3% за рефералы 3 уровня
4: 0.01, # 1% за рефералы 4 уровня
}
# Рефералы по уровням
level_1_referrals = user.referrals.all()
level_2_referrals = User.objects.filter(referred_by__in=level_1_referrals)
level_3_referrals = User.objects.filter(referred_by__in=level_2_referrals)
level_4_referrals = User.objects.filter(referred_by__in=level_3_referrals)
referral_levels = {
1: level_1_referrals,
2: level_2_referrals,
3: level_3_referrals,
4: level_4_referrals,
}
total_coins = 0
for level, referrals in referral_levels.items():
# Фильтруем баланс рефералов, у которых валюта 'MCDC'
coins = Balance.objects.filter(user__in=referrals, currency='MCDC').aggregate(total=Sum('balance'))['total'] or 0
bonus_coins = coins * referral_percentages[level]
total_coins += bonus_coins
# Вычитаем уже полученные бонусы
total_coins -= user.coins_for_referral
response_data = {
"total_coins": total_coins,
"level_1_referrals": list(level_1_referrals.values('tg_username', 'tg_id', 'name', 'photo', 'coins_for_referral')),
"level_2_referrals": list(level_2_referrals.values('tg_username', 'tg_id', 'name', 'photo', 'coins_for_referral')),
"level_3_referrals": list(level_3_referrals.values('tg_username', 'tg_id', 'name', 'photo', 'coins_for_referral')),
"level_4_referrals": list(level_4_referrals.values('tg_username', 'tg_id', 'name', 'photo', 'coins_for_referral')),
}
print(response_data) # TODOPROJECT remove it
return Response({'status': '200', 'data': response_data})
class DailyRewardViewSet(viewsets.ModelViewSet):
queryset = DailyReward.objects.all()
serializer_class = DailyRewardSerializer
class DailyRewardsListViewSet(viewsets.ModelViewSet):
queryset = DailyRewardsList.objects.all()
serializer_class = DailyRewardsListSerializer
class BalanceViewSet(viewsets.ModelViewSet):
queryset = Balance.objects.all()
serializer_class = BalanceSerializer
@action(detail=True, methods=['post'])
def add_for_balance(self, request, pk=None):
user = self.get_object().user
amount = request.data.get("amount")
currency = request.data.get("currency")
if not amount or not currency:
return Response({"status": "error", "message": "Пожалуйста, укажите сумму и валюту"}, status=status.HTTP_400_BAD_REQUEST)
# Создаем новую транзакцию в базе данных
with db_transaction.atomic():
transaction = Transaction.objects.create(
user=user,
amount=amount,
currency=currency,
status="Pending"
)
# Формирование данных для запроса
data = {
"amount": amount,
"currency": currency,
"user_id": user.id,
"transaction_id": transaction.id
}
# Отправка POST-запроса к API Turcode
try:
response = requests.post(
"https://api.turcode.cc/v1/payment",
json=data,
headers={"Authorization": f"Bearer {settings.TURCODE_API_KEY}"}
)
# Обработка ответа от API
if response.status_code == 200:
transaction.status = "Success"
transaction.save()
# Обновление баланса пользователя
user_balance = self.get_object()
user_balance.balance += int(amount)
user_balance.save()
return Response({"status": "success", "message": "Баланс успешно пополнен"}, status=status.HTTP_200_OK)
else:
transaction.status = "Failed"
transaction.save()
return Response({"status": "error", "message": "Не удалось выполнить платеж"}, status=status.HTTP_400_BAD_REQUEST)
except requests.RequestException as e:
transaction.status = "Error"
transaction.save()
return Response({"status": "error", "message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class LevelsViewSet(viewsets.ModelViewSet):
queryset = Levels.objects.all()
serializer_class = LevelsSerializer
@action(detail=True, methods=['post'])
def create_new_levels(self, request, pk=None):
user = self.get_object()
levels = []
# Задаем названия и изображения для каждого уровня
level_data = [
{"name": "Huey", "coins": 0, "image": "static/media/levels/character1.jpg"},
{"name": "Dewey", "coins": 10000, "image": "static/media/levels/character2.jpg"},
{"name": "Donald Duck", "coins": 20000, "image": "static/media/levels/character3.jpg"},
{"name": "Launchpad", "coins": 30000, "image": "static/media/levels/character4.jpg"},
{"name": "Gearloose", "coins": 40000, "image": "static/media/levels/character5.jpg"},
{"name": "Magica", "coins": 50000, "image": "static/media/levels/character6.jpg"},
{"name": "Scrooge", "coins": 60000, "image": "static/media/levels/character7.jpg"},
]
for i, data in enumerate(level_data, start=1):
level = Levels(
user=user,
name=data["name"],
level=i,
photo=data["image"],
need_coins=data["coins"]
)
levels.append(level)
# Сохраняем все созданные объекты в базе данных
Levels.objects.bulk_create(levels)
return Response({'status': '200', 'message': f'Created {len(levels)} levels for user {user.username}.'})