class PromoCode(TimestampedModel): objects = PromoCodeQuerySet.as_manager() name = models.CharField(_('Promo Code'), max_length=32, unique=True, db_index=True) discount_percent = models.IntegerField(_('Discount percent')) active = models.BooleanField(_('Active'), default=True) comment = models.TextField(_('Comment'), blank=True, null=True) courses = models.ManyToManyField( 'products.Course', help_text=_('Can not be used for courses not checked here'), blank=True) class Meta: verbose_name = _('Promo Code') verbose_name_plural = _('Promo Codes') def compatible_with(self, course: Course) -> bool: return self.courses.count() == 0 or course in self.courses.all() def apply(self, course: Course) -> Decimal: if not self.compatible_with(course): return course.price return Decimal(course.price * (100 - self.discount_percent) / 100)
class User(AbstractUser): subscribed = models.BooleanField(_('Subscribed to newsletter'), default=False) @classmethod def parse_name(cls, name: str) -> dict: if name is None: return {} parts = name.split(' ', 2) if len(parts) == 1: return {'first_name': parts[0]} if len(parts) == 2: return {'first_name': parts[0], 'last_name': parts[1]} return {'first_name': parts[0], 'last_name': ' '.join(parts[1:])} def __str__(self): name = self.first_name + ' ' + self.last_name if len(name) < 3: return self.username return name.strip()
class Answer(TreeNode): objects: AnswerQuerySet = AnswerQuerySet.as_manager() created = models.DateTimeField(auto_now_add=True, db_index=True) modified = models.DateTimeField(auto_now=True, db_index=True) slug = models.UUIDField(db_index=True, unique=True, default=uuid.uuid4) question = models.ForeignKey('homework.Question', on_delete=models.CASCADE) author = models.ForeignKey('users.User', on_delete=models.CASCADE) do_not_crosscheck = models.BooleanField(_('Exclude from cross-checking'), default=False) text = MarkdownxField() class Meta: verbose_name = _('Homework answer') verbose_name_plural = _('Homework answers') ordering = ['created'] permissions = [ ('see_all_answers', _('May see answers from every user')), ] def get_root_answer(self): ancesorts = self.ancestors() if ancesorts.count(): return ancesorts[0] return self def get_absolute_url(self): root = self.get_root_answer() url = urljoin(settings.FRONTEND_URL, f'homework/answers/{root.slug}/') if root != self: url = f'{url}#{self.slug}' # append hash with current answer id return url def get_purchased_course(self): latest_purchase = Order.objects.paid().filter( user=self.author, course__in=self.question.courses.all()).order_by('-paid').first() if latest_purchase: return latest_purchase.course def get_first_level_descendants(self): return self.descendants().filter(parent=self.id) def __str__(self): LENGTH = 30 text = self.text[:LENGTH] if len(text) == LENGTH: text += '...' return text
class PromoCode(TimestampedModel): objects = PromoCodeQuerySet.as_manager() name = models.CharField(_('Promo Code'), max_length=32, unique=True, db_index=True) discount_percent = models.IntegerField(_('Discount percent')) active = models.BooleanField(_('Active'), default=True) comment = models.TextField(_('Comment'), blank=True, null=True) class Meta: verbose_name = _('Promo Code') verbose_name_plural = _('Promo Codes') def apply(self, price: Decimal) -> Decimal: return Decimal(price * (100 - self.discount_percent) / 100)
class User(AbstractUser): subscribed = models.BooleanField(_('Subscribed to newsletter'), default=False) first_name_en = models.CharField(_('first name in english'), max_length=150, blank=True) last_name_en = models.CharField(_('last name in english'), max_length=150, blank=True) uuid = models.UUIDField(db_index=True, unique=True, default=uuid.uuid4) @classmethod def parse_name(cls, name: str) -> dict: if name is None: return {} parts = name.split(' ', 2) if len(parts) == 1: return {'first_name': parts[0]} if len(parts) == 2: return {'first_name': parts[0], 'last_name': parts[1]} return {'first_name': parts[0], 'last_name': ' '.join(parts[1:])} def __str__(self): name = self.first_name + ' ' + self.last_name if len(name) < 3: return self.username return name.strip() def add_perm(self, perm): """Add permission to the user. This is a shortcut method for testing, please do not use in production """ [app_label, model, codename] = perm.split('.') permission = Permission.objects.get_by_natural_key( codename, app_label, model) if permission is not None: self.user_permissions.add(permission)
class PaymentNotification(TimestampedModel): STATUS_CHOICES = ( ('AUTHORIZED', _('Authorized')), ('CONFIRMED', _('Confirmed')), ('REVERSED', _('Reversed')), ('REFUNDED', _('Refunded')), ('PARTIAL_REFUNDED', _('Partial refunded')), ('REJECTED', _('Rejected')), ) terminal_key = models.CharField(max_length=512) order_id = models.IntegerField() success = models.BooleanField() status = models.CharField(max_length=128, choices=STATUS_CHOICES) payment_id = models.BigIntegerField() error_code = models.CharField(max_length=512, null=True) amount = models.DecimalField(decimal_places=2, max_digits=9) rebill_id = models.BigIntegerField(null=True) card_id = models.CharField(null=True, max_length=64) pan = models.CharField(max_length=128, null=True) data = models.TextField(null=True) token = models.CharField(max_length=512) exp_date = models.CharField(max_length=32, null=True)
class Order(TimestampedModel): objects = OrderQuerySet.as_manager() # type: OrderQuerySet user = models.ForeignKey('users.User', verbose_name=_('User'), on_delete=models.PROTECT) price = models.DecimalField(_('Price'), max_digits=9, decimal_places=2) promocode = models.ForeignKey('orders.PromoCode', verbose_name=_('Promo Code'), blank=True, null=True, on_delete=models.PROTECT) paid = models.DateTimeField( _('Date when order got paid'), null=True, blank=True, help_text=_('If set during creation, order automaticaly gets shipped'), ) shipped = models.DateTimeField(_('Date when order was shipped'), null=True, blank=True) desired_bank = models.CharField(_('User-requested bank string'), blank=True, max_length=32) course = ItemField(to='products.Course', verbose_name=_('Course'), null=True, blank=True, on_delete=models.PROTECT) record = ItemField(to='products.Record', verbose_name=_('Record'), null=True, blank=True, on_delete=models.PROTECT) bundle = ItemField(to='products.Bundle', verbose_name=_('Bundle'), null=True, blank=True, on_delete=models.PROTECT) giver = models.ForeignKey('users.User', verbose_name=_('Giver'), null=True, blank=True, on_delete=models.SET_NULL, related_name='created_gifts') desired_shipment_date = models.DateTimeField( _('Date when the gift should be shipped'), null=True, blank=True) gift_message = models.TextField(_('Gift message'), default='', blank=True) notification_to_giver_is_sent = models.BooleanField(default=False) class Meta: ordering = ['-id'] verbose_name = _('Order') verbose_name_plural = _('Orders') def __str__(self): return f'Order #{self.pk}' @property def item(self): """Find the attached item. Simple replacement for ContentType framework """ for field in self.__class__._meta.get_fields(): if getattr(field, '_is_item', False): if getattr(self, f'{field.name}_id', None) is not None: return getattr(self, field.name) @classmethod def _iterate_items(cls) -> Iterable[models.fields.Field]: for field in cls._meta.get_fields(): if getattr(field, '_is_item', False): yield field @classmethod def get_item_foreignkey(cls, item) -> Optional[models.fields.Field]: """ Given an item model, returns the ForeignKey to it""" for field in cls._iterate_items(): if field.related_model == item.__class__: return field.name def reset_items(self): for field in self._iterate_items(): setattr(self, field.name, None) def set_item(self, item): foreign_key = self.__class__.get_item_foreignkey(item) if foreign_key is not None: self.reset_items() setattr(self, foreign_key, item) return raise UnknownItemException('There is not foreignKey for {}'.format( item.__class__)) def set_paid(self, silent=False): from orders.services.order_is_paid_setter import Griphook Griphook(self, silent=silent)() def ship(self, silent: bool = False): """Ship the order. Better call it asynchronously""" from orders.services.order_shipper import Pigwidgeon Pigwidgeon(self, silent=silent)()