from json import dumps from django.utils.functional import cached_property from rest_framework.authtoken.models import Token from phonenumber_field.modelfields import PhoneNumberField from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth.models import AbstractUser, UserManager as BaseUserManager from django.contrib.postgres import fields as pgfields from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from api.v1 import serializers from apps.notification.utils import send_email from apps.user.tasks import user_to_mixpanel class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, username, email, password, **extra_fields): if not username: username = email if not password: password = self.make_random_password() email = self.normalize_email(email) username = self.model.normalize_username(username) user = self.model(username=username, email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user class User(AbstractUser): USER_ROLE = 0 AUTHOR_ROLE = 1 TEACHER_ROLE = 2 ADMIN_ROLE = 3 ROLE_CHOICES = ( (USER_ROLE, 'пользователь'), (AUTHOR_ROLE, 'автор'), (TEACHER_ROLE, 'преподаватель'), (ADMIN_ROLE, 'администратор'), ) NOT_DEFINED = 'n' MALE = 'm' FEMALE = 'f' GENDER_CHOICES = ( (NOT_DEFINED, 'не указан'), (MALE, 'Мужчина'), (FEMALE, 'Женщина'), ) email = models.EmailField(_('email address'), unique=True) phone = PhoneNumberField(null=True, blank=True, unique=True) role = models.PositiveSmallIntegerField( 'Роль', default=0, choices=ROLE_CHOICES) gender = models.CharField( 'Пол', max_length=1, default='n', choices=GENDER_CHOICES) birthday = models.DateField('День рождения', null=True, blank=True) country = models.CharField('Страна', max_length=50, null=True, blank=True) city = models.CharField('Город', max_length=85, null=True, blank=True) about = models.CharField('О себе', max_length=1000, null=True, blank=True) instagram = models.URLField(default='', null=True, blank=True) instagram_hashtag = models.CharField('Инстаграм хештег', max_length=100, null=True, blank=True) facebook = models.URLField(default='', null=True, blank=True) twitter = models.URLField(default='', null=True, blank=True) pinterest = models.URLField(default='', null=True, blank=True) youtube = models.URLField(default='', null=True, blank=True) vkontakte = models.URLField('ВКонтакте', default='', null=True, blank=True) fb_id = models.BigIntegerField(null=True, blank=True, unique=True) fb_data = pgfields.JSONField(default={}, null=True, blank=True) is_email_proved = models.BooleanField( 'Верифицирован по email', default=False ) photo = models.ImageField('Фото', null=True, blank=True, upload_to='users') show_in_mainpage = models.BooleanField('Показывать на главной странице', default=False) trial_lesson = models.URLField(default='', null=True, blank=True) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] class Meta(AbstractUser.Meta): ordering = ('-date_joined',) def serialized(self): user_data = serializers.user.UserSerializer(instance=self).data user_data = dumps(user_data, ensure_ascii=False) return user_data @cached_property def balance(self): aggregate = self.balances.filter( type=0, payment__isnull=False, payment__status__isnull=False ).aggregate( models.Sum('amount'), models.Sum('commission'), ) amount = aggregate.get('amount__sum') or 0 commission = aggregate.get('commission__sum') or 0 return amount - commission @cached_property def bonus(self): return int(self.bonuses.aggregate(models.Sum('amount')).get('amount__sum')) or 0 @receiver(post_save, sender=User) def create_auth_token(sender, instance=None, created=False, **kwargs): if ( (instance.is_active or instance.fb_id) and instance.role in [User.AUTHOR_ROLE, User.TEACHER_ROLE, User.ADMIN_ROLE] and not hasattr(instance, 'auth_token') ): Token.objects.create(user=instance) elif ( not (instance.is_active or instance.fb_id) or instance.role not in [User.AUTHOR_ROLE, User.TEACHER_ROLE, User.ADMIN_ROLE] ) and hasattr(instance, 'auth_token'): instance.auth_token.delete() @receiver(post_save, sender=User) def send_user_info_to_mixpanel(sender, instance=None, created=False, **kwargs): user_to_mixpanel.delay( instance.id, instance.email, str(instance.phone), instance.first_name, instance.last_name, instance.date_joined, dict(User.ROLE_CHOICES).get(instance.role), [subscription.title.lower() for subscription in instance.email_subscription.categories.all()] if hasattr(instance, 'email_subscription') else [], ) @receiver(post_save, sender=User) def auto_create_subscription(sender, instance=None, created=False, **kwargs): if created: es, created = EmailSubscription.objects.get_or_create(email=instance.email) es.user = instance es.save() instance.email_subscription.categories.set(SubscriptionCategory.objects.filter(auto_add=True)) class AuthorRequestManager(models.Manager): def create_by_user(self, user): obj = self.model( first_name=user.first_name, last_name=user.last_name, email=user.email, about=user.about, facebook=user.facebook, ) self._for_write = True obj.save(force_insert=True, using=self.db) return obj class AuthorRequest(models.Model): PENDING = 0 ACCEPTED = 1 DECLINED = 2 STATUS_CHOICES = ( (PENDING, 'pending'), (ACCEPTED, 'accepted'), (DECLINED, 'declined'), ) first_name = models.CharField(_('first name'), max_length=30) last_name = models.CharField(_('last name'), max_length=150) email = models.EmailField(_('email address'), unique=True) about = models.CharField('О себе', max_length=1000, null=True, blank=True) facebook = models.URLField(default='', null=True, blank=True) status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=PENDING) cause = models.TextField('Причина отказа', null=True, blank=True) accepted_send_at = models.DateTimeField(null=True, blank=True) declined_send_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) objects = AuthorRequestManager() class Meta: verbose_name = 'Заявка не преподавателя' verbose_name_plural = 'Заявки не преподавателя' ordering = ('-created_at',) @receiver(post_save, sender=AuthorRequest) def handle_authorrequest_update(sender, instance=None, created=False, update_fields=[], **kwargs): if not created: if instance.status == AuthorRequest.DECLINED and not instance.declined_send_at: send_email.delay( 'Отказ заявки на преподавателя', instance.email, 'notification/email/decline_author.html', cause=instance.cause, ) instance.declined_send_at = now() instance.save() elif instance.status == AuthorRequest.ACCEPTED and not instance.accepted_send_at: email = None password = None try: user = User.objects.get(email=instance.email) except User.DoesNotExist: email = instance.email password = User.objects.make_random_password() user = User.objects.create( first_name=instance.first_name, last_name=instance.last_name, username=instance.email, email=instance.email, about=instance.about, facebook=instance.facebook, is_active=True, is_email_proved=True, ) user.set_password(password) user.role = User.AUTHOR_ROLE user.save() send_email.delay( 'Заявка на преподавателя одобрена', instance.email, 'notification/email/accept_author.html', email=email, password=password, ) instance.accepted_send_at = now() instance.save() class SubscriptionCategory(models.Model): title = models.CharField(max_length=100) auto_add = models.BooleanField(default=False) class Meta: verbose_name = 'Категория подписки' verbose_name_plural = 'Категории подписки' ordering = ('title',) def __str__(self): return self.title class EmailSubscription(models.Model): ERROR = 0 SENT = 1 MAILCHIMP_STATUS_CHOICES = ( (ERROR, 'error'), (SENT, 'sent'), ) user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE, related_name='email_subscription') email = models.EmailField(_('email address'), unique=True) categories = models.ManyToManyField(SubscriptionCategory) mailchimp_status = models.PositiveSmallIntegerField(choices=MAILCHIMP_STATUS_CHOICES, default=ERROR) class Referral(models.Model): referral = models.OneToOneField(User, on_delete=models.CASCADE, related_name='referral') referrer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='referrals') bonus = models.IntegerField() referrer_bonus = models.IntegerField() payment = models.OneToOneField('payment.Payment', null=True, blank=True, on_delete=models.CASCADE) class Meta: verbose_name = 'Реферал' verbose_name_plural = 'Рефералы' def send_bonuses(self, referral_bonus, referrer_bonus): from apps.payment.models import UserBonus UserBonus.objects.create(user=self.referral, amount=referral_bonus, payment=self.payment, referral=self) UserBonus.objects.create(user=self.referrer, amount=referrer_bonus, payment=self.payment, referral=self)