Example #1
0
class ApishopOffer(TempDBMixin, GetOrCreateMixin, db.Document):
    id = db.IntField(primary_key=True)
    available = db.BooleanField()
    articul = db.StringField()
    price = db.DictField(default=dict(ru=None, by=None, kz=None))
    commissions = db.DictField(default=dict(ru=None, by=None, kz=None))
    category_id = db.IntField()
    name = db.StringField(max_length=255)
    model = db.StringField(max_length=255)
    vendor = db.StringField(max_length=100)
    pictures = db.ListField(db.StringField(max_length=255), default=[])
    description = db.StringField()
    variants = db.ListField(db.DictField())
    store_count = db.IntField()

    enabled_to_copy = db.BooleanField(default=True)

    meta = {'indexes': ['category_id']}

    def __unicode__(self):
        return '%s: %s' % (self.id, self.name)

    def get_picture(self):
        if len(self.pictures):
            return self.pictures[0]

    @property
    def prepare_to_copy(self):
        return dict(aid=self.id,
                    articul=self.articul,
                    available=self.available,
                    price=self.price,
                    commissions=self.commissions,
                    name=self.name,
                    model=self.model,
                    vendor=self.vendor,
                    pictures=[{
                        'url': picture
                    } for picture in self.pictures],
                    description=self.description,
                    stats=dict(store_count=self.store_count),
                    variants=[{
                        'store_count': variant.get('store_count'),
                        'aid': variant.get('id'),
                        'name': variant.get('name')
                    } for variant in self.variants])
Example #2
0
class ApishopCategory(TempDBMixin, GetOrCreateMixin, db.Document):
    id = db.IntField(primary_key=True, unique=True)
    parent_id = db.IntField()
    name = db.StringField(max_length=255)
    category = db.ReferenceField(RealCategory, default=None)

    meta = {'indexes': ['parent_id', 'category']}

    def __unicode__(self):
        return self.name

    @classmethod
    def get_full_tree(cls):
        def build_tree(categories, parent_id=0):
            branch = []
            for index, category in enumerate(categories):
                if category.parent_id == parent_id:
                    form = ApishopCategoryLinkForm(request.form, obj=category)
                    branch.append(
                        dict(id=category.id,
                             name=category.name,
                             offers=category.get_offers_count,
                             form=form,
                             childs=build_tree(categories, category.id)))
            return branch

        categories = cls.objects.order_by('+parent_id')

        return build_tree(categories)

    @property
    def get_offers_count(self):
        return ApishopOffer.objects(category_id=self.id).count()

    @classmethod
    def copy_offers(cls):
        # Для каждой категории со связью отправить товар в RealOffer
        # для дальнейшей обработки и сохранения. После копирования очистить
        # базу временных товаров.
        categories_with_link = cls.objects(category__ne=None)
        for category in categories_with_link:
            for offer in ApishopOffer.objects(category_id=category.id):
                RealOffer.populate(offer, category.category)
Example #3
0
class Process(BaseModel):
    STATUS = PENDING, SUCCEEDED, FAILED = range(3)
    status = db.IntField(choices=STATUS, default=PENDING)

    @property
    def is_success(self):
        return self.status == self.SUCCEEDED

    def make_succeed(self):
        return self.update(status=self.SUCCEEDED)

    def make_fail(self):
        return self.update(status=self.FAILED)
Example #4
0
class DeliveryMethod(db.Document):
    id = db.IntField(primary_key=True)
    name = db.StringField(max_length=400)

    meta = {
        'ordering': ['+id'],
        'indexes': ['name']
    }

    def __unicode__(self):
        return self.name

    def __str__(self):
        return u'%s(%s)' % (self.__class__.__name__, self.id)
Example #5
0
class Song(BaseModel):
    name = db.StringField()
    comment_count = db.IntField()
    comments = db.ListField(db.ReferenceField('Comment'))
    artist = db.ReferenceField('Artist')
    meta = {'indexes': ['name']}

    @property
    def url(self):
        return SONG_URL.format(self.id)

    @property
    def artist_url(self):
        return self.artist.url
Example #6
0
class PositionMixin(object):
    position = db.IntField()

    def validate_position(self, parent=None):
        if self.position is None:
            parent = getattr(self, 'parent', parent)
            filters = dict(parent=parent)
            if self.id:
                filters['id__ne'] = self.id
            last = self.__class__.objects(**filters).order_by('-position').first()

            if last:
                self.position = last.position + 1
            else:
                self.position = 0
Example #7
0
class IntIDMixin(object):
    id = db.IntField(primary_key=True)
    created_at = db.DateTimeField(default=datetime.now)
    updated_at = db.DateTimeField()

    def _generate_id(self):
        last_obj = self.__class__.objects.only('id').order_by('-id').first()
        self.id = last_obj.id + 1 if last_obj else 1

    def save(self, *args, **kwargs):
        if not self.id:
            self._generate_id()
        if not self.created_at:
            self.created_at = datetime.now()
        self.updated_at = datetime.now()
        super(IntIDMixin, self).save(*args, **kwargs)
Example #8
0
class ApishopConfig(TempDBMixin, db.Document):
    login = db.StringField(max_length=50, required=True)
    shop_id = db.IntField(required=True)
    yml_file = db.StringField(max_length=255)
    task = db.DictField(default={'id': None, 'name': None})
    updated_at = db.DateTimeField()

    _password = db.StringField(required=True)

    def __unicode__(self):
        return self.login

    @classmethod
    def get_config(cls):
        return cls.objects.first()

    @property
    def password(self):
        return decode_string(self._password)

    @password.setter
    def password(self, value):
        self._password = encode_string(value)

    def set_task(self, task):
        self.update(set__task=dict(id=task.task_id, name=task.task_name))

    def set_updated_at(self):
        self.update(set__updated_at=datetime.datetime.now())

    @property
    def is_yml_exists(self):
        if self.yml_file:
            return check_file_exists(self.yml_file, relative=True)
        return False

    @property
    def task_is_ready(self):
        if self.task.get('id'):
            return celery.AsyncResult(self.task.get('id')).ready()
        return True

    def delete(self, *args, **kwargs):
        delete_file_by_path(self.yml_file, relative=True)
        ApishopCategory.objects.delete()
        ApishopOffer.objects.delete()
        super(ApishopConfig, self).delete(*args, **kwargs)
Example #9
0
class ApishopPriceChange(TempDBMixin, db.Document):
    oid = db.IntField()
    name = db.StringField()
    old_price = db.FloatField()
    new_price = db.FloatField()
    date = db.DateTimeField(default=datetime.datetime.now)

    meta = {'ordering': ['-date']}

    @classmethod
    def create_or_update(cls, **kwargs):
        oid = kwargs.pop('oid')

        obj = cls.objects(oid=oid)
        if len(obj) >= 1:
            for o in obj:
                o.delete()

        obj = cls(**kwargs)
        obj.save()
        return obj
Example #10
0
class Review(db.Document):
    offer = db.ReferenceField('Offer')
    fullname = db.StringField(max_length=200)
    email = db.StringField(max_length=200)
    text = db.StringField()
    rating = db.IntField(default=0)
    is_moderated = db.BooleanField(default=False)
    is_viewed = db.BooleanField(default=False)
    created_at = db.DateTimeField(default=datetime.now)

    meta = {
        'ordering': ['-created_at']
    }

    def toggle_moderate(self):
        self.update(set__is_moderated=not self.is_moderated)
        self.reload()

    def set_viewed(self):
        if not self.is_viewed:
            self.update(set__is_viewed=True)
            self.reload()
Example #11
0
class BaseModel(db.Document):
    id = db.IntField(primary_key=True)
    create_at = db.DateTimeField(default=datetime.now)
    update_at = db.DateTimeField()

    meta = {'allow_inheritance': True, 'abstract': True, 'strict': False}

    @classmethod
    def get(cls, id):
        coll_name = cls._meta['collection']
        key = OBJ_KEY.format(coll_name=coll_name, id=id)
        rs = cache.get(key)
        if rs:
            return cls.from_json(
                rs)  # Converts json data to an unsaved document instance
        rs = cls.objects.get(id=id)
        cache.set(key, rs.to_json())  # Convert this document to JSON.
        return rs

    @classmethod
    def get_multi(cls, ids):
        return [cls.get(i) for i in ids if i]

    @classmethod
    def get_or_create(cls, **kwargs):
        try:
            return cls.objects.get(id=kwargs['id'])
        except DoesNotExist:
            kwargs.update({'update_at': datetime.now()})
            model = cls(**kwargs)
            model.save()
            return model

    @classmethod
    def get_sample_ids(cls, size):
        samples = list(cls.objects.aggregate({'$sample': {'size': size}}))
        return [s['_id'] for s in samples]
Example #12
0
class Comment(BaseModel):
    content = db.StringField()
    like_count = db.IntField()
    user = db.ReferenceField('User')
    movie = db.ReferenceField('Movie', reverse_delete_rule=db.CASCADE)

    meta = {'indexes': ['-like_count']}

    @property
    def url(self):
        return COMMENT_URL.format(self.id)

    @property
    def user_url(self):
        return self.user.url

    @property
    def movie_url(self):
        return self.movie.url

    @classmethod
    def cache_by_key(cls, key, ids):
        cache.delete(key)
        cache.rpush(key, *ids)
        cache.expire(key, TIMEOUT)

    @classmethod
    def order_by_star(cls, start=0, limit=20):
        ids = cache.lrange(START_KEY, start, start + limit)
        if not ids:
            ids = [
                c.id for c in cls.objects.order_by('-like_count')[:TOTAL_SIZE]
            ]
            cache.delete(START_KEY)
            cache.rpush(START_KEY, *ids)
            ids = ids[start:start + limit]
        return cls.get_multi(ids)

    def to_dict(self):
        movie_obj = self.movie
        user_obj = self.user

        movie = {
            'id': movie_obj.id,
            'name': movie_obj.name,
            'mark': movie_obj.mark,
            'url': movie_obj.url,
            'picture': movie_obj.picture
        }

        user = {
            'id': user_obj.id,
            'avatar': user_obj.picture,
            'name': user_obj.name,
        }

        return {
            'movie': movie,
            'user': user,
            'content': self.content,
            'like_count': self.like_count
        }
Example #13
0
class OfferStats(db.EmbeddedDocument):
    store_count = db.IntField(default=0)
    views = db.IntField(default=0)
    add_to_cart = db.IntField(default=0)
    orders = db.IntField(default=0)
    popularity = db.FloatField(default=0)
Example #14
0
class Comment(BaseModel):
    content = db.StringField()
    like_count = db.IntField()
    user = db.ReferenceField('User')
    song = db.ReferenceField('Song')
    meta = {'indexes': ['-like_count']}

    @property
    def user_url(self):
        return self.user.url

    @property
    def artist_url(self):
        return self.song.artist_url

    @classmethod
    def cache_by_key(cls, key, ids):
        """给内部用"""
        cache.delete(key)
        cache.rpush(key, *ids)
        cache.expire(key, TIMEOUT)

    @classmethod
    def get_random_by_session_id(cls, session_id, start=0, limit=20):
        """给API用"""
        key = RANDOM_KEY.format(session_id=session_id)
        if not start % SAMPLE_SIZE:
            ids = cls.get_sample_ids(SAMPLE_SIZE)
            cls.cache_by_key(key, ids)

        else:
            ids = cache.lrange(key, start, start + limit)
            if not ids:
                ids = cls.get_sample_ids(SAMPLE_SIZE)
                cls.cache_by_key(key, ids)

        comments = cls.get_multi(ids)
        return comments

    @classmethod
    def order_by_star(cls, start=0, limit=20):
        """给API用"""
        ids = cache.lrange(STAR_KEY, start, start + limit)
        if not ids:
            ids = [
                c.id for c in cls.objects.order_by('-like_count')[:TOTAL_SIZE]
            ]  # noqa
            cache.delete(STAR_KEY)
            cache.rpush(STAR_KEY, *ids)
            ids = ids[start:start + limit]
        return cls.get_multi(ids)

    def to_dict(self):
        song_obj = self.song
        user_obj = self.user
        artist_obj = song_obj.artist
        song = {'id': song_obj.id, 'url': song_obj.url, 'name': song_obj.name}

        artist = {
            'id': artist_obj.id,
            'avatar': artist_obj.picture,
            'name': artist_obj.name,
            'url': artist_obj.url
        }

        user = {
            'avatar': user_obj.picture,
            'name': user_obj.name,
            'url': user_obj.url
        }
        return {
            'song': song,
            'user': user,
            'artist': artist,
            'content': self.content
        }
Example #15
0
class CartOffer(db.EmbeddedDocument):
    offer = db.ReferenceField('Offer')
    quantity = db.IntField(default=1)
    variant = db.StringField(default=None)
Example #16
0
class OfferSpecial(db.Document):
    is_active = db.BooleanField(default=False)
    type = db.StringField(choices=(('real', u'Понизить цену'), ('fake', u'Повысить цену')), default='real', verbose_name=u'Тип акции')
    price_type = db.StringField(choices=(('percent', u'На проценты'), ('new', u'На сумму')), default='percent')
    price_value = db.IntField(required=True)
    timer_type = db.StringField(choices=(('date', u'До даты'), ('time', u'По времени')), default='date')
    timer_settings = db.DictField(required=True)

    created_at = db.DateTimeField()
    prices = db.EmbeddedDocumentField('OfferPrices')

    def __unicode__(self):
        return str(self.id)

    @classmethod
    def create_or_update(cls, offer, form):
        special = offer.get_special or cls()

        form.populate_obj(special)

        special.populate_price(offer)
        special.set_created_at()
        special.save()

        offer.update(set__special=special)

        return special

    def create_from_self(self):
        offer = Offer.objects(special=self).first()
        if offer:
            data = deepcopy(self._data)
            data.pop('id')
            data.pop('prices')

            new = self.__class__(**data)

            return new

    def set_created_at(self):
        self.is_active = True
        self.created_at = datetime.now()

    def populate_price(self, offer):
        if self.type == 'real':

            if not self.prices:
                self.prices = offer.price

            if self.price_type == 'percent':
                new_price = self.prices.ru * (1 - float(self.price_value) / 100)

            elif self.price_type == 'new':
                new_price = self.prices.ru - self.price_value

            offer.update(set__price__ru=new_price)
            offer.reload()

        else:

            if not self.prices:
                self.prices = offer.price

            if self.price_type == 'percent':
                new_price = self.prices.ru * (1 + float(self.price_value) / 100)

            elif self.price_type == 'new':
                new_price = self.prices.ru + self.price_value

            self.prices.ru = new_price

    def get_timer(self):

        if self.timer_type == 'date':
            date = datetime.strptime(self.timer_settings.get('timer_date'), "%d/%m/%Y").date()
            return date + timedelta(days=1)

        elif self.timer_type == 'time':
            now = datetime.now()
            delta = now - self.created_at
            days_step = int(self.timer_settings.get('timer_days', 1))

            if self.timer_settings.get('timer_repeat') == 'on' and delta.days > days_step:
                full = delta.days - (delta.days % days_step) + days_step
                new_delta = timedelta(days=full)
            else:
                new_delta = timedelta(days=days_step)

            return (self.created_at + new_delta + timedelta(days=1)).date()

    @property
    def timer(self):
        return self.get_timer().strftime('%Y/%m/%d')

    @cached_property
    def is_over(self):
        now = datetime.now()
        is_over = now.date() >= self.get_timer()
        if is_over:
            offer = Offer.objects(special=self).first()
            if offer:
                self.remove(offer)
            else:
                self.update(set__is_active=False)
        return is_over

    def remove(self, offer):
        atomic = dict(set__special=None)
        if self.type == 'real':
            atomic['set__price'] = self.prices

        offer.update(**atomic)

        self.delete()
Example #17
0
class Offer(DispatcherMixin, IntIDMixin, PathMixin, BreadcrumbsMixin, db.Document):
    name = db.StringField(max_length=255, verbose_name=u'Название', required=True)
    model = db.StringField(max_length=255, verbose_name=u'Модель')

    aid = db.IntField(verbose_name=u'ID apishops')
    articul = db.StringField(max_length=50, verbose_name=u'Артикул')
    available = db.BooleanField()

    price = db.EmbeddedDocumentField('OfferPrices', verbose_name=u'Цены')
    commissions = db.EmbeddedDocumentField('OfferPrices', verbose_name=u'Коммиссии')

    vendor = db.ReferenceField('Vendor', verbose_name=u'Производитель')
    parent = db.ReferenceField('Category', verbose_name=u'Категория')
    metas = db.EmbeddedDocumentField('Metas')
    stats = db.EmbeddedDocumentField('OfferStats')
    variants = db.ListField(db.EmbeddedDocumentField('OfferVariant'))
    pictures = db.ListField(db.EmbeddedDocumentField('OfferPicture'))

    special = db.ReferenceField('OfferSpecial')

    short_description = db.StringField(verbose_name=u'Короткое описание')
    description = db.StringField(verbose_name=u'Описание')

    canonical = db.ReferenceField('Offer', default=None, verbose_name=u'Каноникал')

    meta = {
        'indexes': [{'fields': ['$name', "$description"],
                     'default_language': 'russian',
                     'weights': {'name': 10, 'description': 2}
                    },
                    'path', 'parent',
                    'articul', 'aid',
                    'price.ru',
                    'stats.popularity',
                    ['available', 'price.ru'],
                    ['available', 'stats.popularity']]
    }

    def __unicode__(self):
        return self.name

    def __repr__(self):
        return u'%s(%s)' % (self.__class__.__name__, self.id)

    @cache.memoize(60*60*24*7)
    def get_canonical(self):
        if not self.canonical or self.canonical == self:
            return None
        url_root = request.url_root
        return urlparse.urljoin(url_root, url_for('site.dispatcher',
                                                  path=self.canonical.path))

    @classmethod
    def populate(cls, copied_offer, category):
        offer = cls.objects(aid=copied_offer.id, articul=copied_offer.articul).first()

        if not offer:
            offer = cls.objects(aid=copied_offer.id).first()

        offer_info = deepcopy(copied_offer.prepare_to_copy)

        if offer:
            store_count = offer_info.get('stats').get('store_count', None)
            available = offer_info.get('available', None)
            price = offer_info.get('price', None)
            commissions = offer_info.get('commissions', None)
            variants = offer_info.get('variants', None)

            updates = {}
            price_change = False

            if copied_offer.articul != offer.articul:
                updates['set__articul'] = copied_offer.articul

            if store_count != offer.stats.store_count:
                updates['set__stats__store_count'] = store_count

            if available != offer.available:
                updates['set__available'] = available

            if price:
                for key in ('ru', 'by', 'kz'):
                    if float(price.get(key)) != float(offer.get_price(key)):
                        if key == 'ru':
                            price_change = True
                        updates['set__price__{}'.format(key)] = price.get(key)

            if commissions:
                for key in ('ru', 'by', 'kz'):
                    if float(commissions.get(key)) != float(offer.get_commission(key)):
                        updates['set__commissions__{}'.format(key)] = commissions.get(key)

            if variants:
                varts = []
                for variant in variants:
                    varts.append(OfferVariant(**variant))
                updates['set__variants'] = varts


            if len(updates.keys()):
                if price_change:
                    from modules.apishop.models import ApishopPriceChange
                    if offer.special and offer.special.type == 'real':
                        old_price = float(offer.special.prices.ru)
                    else:
                        old_price = float(offer.get_price('ru'))

                    new_price = float(updates['set__price__ru'])
                    if old_price != new_price:
                        change = ApishopPriceChange(oid=offer.id,
                                                    name=offer.name,
                                                    old_price=old_price,
                                                    new_price=new_price)
                        change.save()

                updates['set__updated_at'] = datetime.now()
                offer.update(**updates)
                offer.reload()

                if price_change and offer.get_special is not None:
                    current_special = offer.get_special
                    new = current_special.create_from_self()
                    new.populate_price(offer)
                    new.save()
                    offer.update(set__special=new)
                    current_special.delete()

        else:
            vendor_name = offer_info.pop('vendor', None)
            if vendor_name:
                vendor = Vendor.get_or_create_by_name(vendor_name)
            else:
                vendor = None
            offer_info['vendor'] = vendor
            offer_info['parent'] = category
            offer = cls(**offer_info)
            offer.save()
            task = upload_offer_pictures.apply_async([offer])

    @cache.memoize(60*60)
    def get_delivery_price(self, region_id=None):
        if not region_id:
            region_id = 53

        region = Region.objects(id=region_id).first()

        if region:
            return dict(id=region.id,
                        name=region.name,
                        deliveries=[(d.method, d.price) for d in region.deliveries])
        return None

    @staticmethod
    def sub_text(match):
        id = match.group('id')
        offer = Offer.objects(id=id).only('path').first()

        if not offer:
            return ''

        return url_for('site.dispatcher', path=offer.path)

    @property
    def get_description(self):
        comp = re.compile(r'%%\s*link_to_offer\s+(?P<id>[0-9]+)\s*%%', re.IGNORECASE)
        text = comp.sub(self.sub_text, self.description)
        return u'{}'.format(text)

    @property
    def is_in_favorites(self):
        favorites = session.get('favorites', [])
        return str(str(self.id) in favorites).lower()

    @property
    def generate_picture_name(self):
        uniq = str(uuid.uuid4())[:8]
        return '_'.join([self.slug, uniq])

    def create_pictures_set(self, original):
        big = create_offer_image(original, quality=100)
        medium = create_offer_image(original, width=250, height=200, suffix='med')
        small = create_offer_image(original, width=60, height=60, suffix='sml')

        return dict(original=original,
                    big=big,
                    medium=medium,
                    small=small)

    def get_pictures(self, for_download=False, typ=None):
        if for_download:
            return self.pictures if hasattr(self, 'pictures') else []

        pictures = []
        if self.pictures:
            for picture in self.pictures:
                if typ and getattr(picture, typ, None) is not None:
                    url = url_for('media', filename=getattr(picture, typ))
                elif not typ and getattr(picture, 'big', None) is not None:
                    url = url_for('media', filename=getattr(picture, 'big'))
                else:
                    url = url_for('media', filename=picture.original) if picture.original else picture.url
                pictures.append(url)
        else:
            if not typ:
                typ = 'big'
            pictures = [url_for('static', filename='img/nophoto_{}.svg'.format(typ))]


        return pictures

    @cached_property
    @cache.memoize(3600)
    def parent_cached(self):
        return (self.parent.name,
                self.parent.path)

    def get_variant(self, aid):
        if self.variants:
            try:
                aid = int(aid)
            except (TypeError, ValueError):
                pass

            for variant in self.variants:
                if variant.aid == aid:
                    return variant
            else:
                return self.variants[0]

        return None

    def get_reviews(self):
        return Review.objects(offer=self.id, is_moderated=True)

    @cached_property
    def get_title(self):
        separator = current_app.config.get('DEFAULT_TITLE_SEPARATOR', ' | ')
        return separator.join([self.name, self.parent.get_title])

    @property
    def is_in_stock(self):
        return self.available

    @property
    def get_special(self):
        return self.special or None

    @property
    def get_oldprice(self):
        if self.special:
            return smart_round(self.special.prices.ru)
        return None

    @property
    def get_timer(self):
        if self.special:
            return self.special.timer
        return None

    @classmethod
    def get_special_offers(cls):
        import random
        special_offers = cls.objects(special__ne=None).order_by('-stats.popularity')
        special_offers = [offer for offer in special_offers if offer.special.is_active]
        random.shuffle(special_offers)
        return special_offers

    @cache.memoize(3600)
    def get_picture(self, typ=None, absolute=False):
        pictures = self.get_pictures(typ=typ)

        if len(pictures):
            url = pictures[0]
        else:
            url = url_for('static', filename='img/nophoto.svg')

        if absolute:
            url = urlparse.urljoin(request.url_root, url)

        return url

    @cached_property
    def get_absolute_picture(self):
        pic = self.get_picture()
        if not pic:
            return None
        return ''.join([request.url_root.strip('/'),
                        pic])

    @cached_property
    def get_canonical_url(self):
        return ''.join([request.url_root.strip('/'),
                        url_for('site.dispatcher', path=self.path)])

    @cache.memoize(3600)
    def get_breadcrumbs(self):
        paths = self._split_path()
        breadcrumbs = []

        objs = self.parent.__class__.objects(path__in=paths[:-1])\
                                    .only('name', 'path')\
                                    .order_by('path')
        for obj in objs:
            breadcrumbs.append((obj.name, obj.path))

        return breadcrumbs

    def set_visit(self):
        cache.delete_memoized(self.get_visited)
        visited_offers = session.get('visited_offers', [])

        if self.id not in visited_offers:
            self.update(inc__stats__views=1)
            self.reload()
            visited_offers.insert(0, self.id)

            self.calculate_popularity()
        else:
            visited_offers.remove(self.id)
            visited_offers.insert(0, self.id)

        session['visited_offers'] = visited_offers

    def set_add_to_cart(self):
        added_to_cart = session.get('added_to_cart', [])

        if self.id not in added_to_cart:
            self.update(inc__stats__add_to_cart=1)
            self.reload()
            added_to_cart.append(self.id)

            self.calculate_popularity()
            self.reload()

        session['added_to_cart'] = added_to_cart

    def calculate_popularity(self):
        popularity = (self.stats.views * 1 + self.stats.add_to_cart * 2 + self.stats.orders * 3) / 3
        self.update(set__stats__popularity=popularity)

    @classmethod
    @cache.memoize(60*5)
    def get_visited(cls):
        visited_offers = session.get('visited_offers', [])
        if len(visited_offers):
            visited = list(cls.objects(id__in=visited_offers[:15])
                           .only('id', 'name', 'price', 'pictures', 'path'))
            visited.sort(key=lambda k: visited_offers.index(k.id))
            return visited
        return None

    @classmethod
    def get_popular(cls):
        return cls.objects(available=True).order_by('-stats.popularity')

    @cache.memoize(60*60)
    def get_random_ids(self, offer_id):
        max_items = 12
        all_ids = sorted(Offer.objects(available=True,
                                       id__ne=offer_id).distinct('id'))

        length = len(all_ids)

        ids = []

        if length:
            try:
                ids = random.sample(all_ids, max_items)
            except ValueError:
                ids = random.sample(all_ids, length)

        return ids

    def get_related(self):
        return self.__class__.objects(id__in=self.get_random_ids(self.id))

    def remove_picture(self, idx):

        pictures = self.pictures

        picture = pictures.pop(idx)

        for typ in ('original', 'small', 'medium', 'big'):
            if hasattr(picture, typ):
                path = getattr(picture, typ, None)
                if path:
                    delete_file_by_path(os.path.join(current_app.config['MEDIA_DIR'],
                                                     path))

        self.update(set__pictures=pictures)
        cache.delete_memoized(self.get_picture)

    def get_price(self, key='ru'):
        return smart_round(getattr(self.price, key))

    def get_commission(self, key='ru'):
        return smart_round(getattr(self.commissions, key))
    
    def save(self, *args, **kwargs):
        cache.delete_memoized(self.get_picture)
        cache.delete_memoized(self.get_visited)
        cache.delete_memoized(self.get_canonical)
        super(Offer, self).save(*args, **kwargs)
Example #18
0
class CartTotal(db.EmbeddedDocument):
    cost = db.FloatField(default=0)
    count = db.IntField(default=0)
Example #19
0
class OfferVariant(db.EmbeddedDocument):
    store_count = db.IntField()
    aid = db.IntField()
    name = db.StringField()
Example #20
0
class RegionDelivery(db.EmbeddedDocument):
    method = db.StringField(max_length=400)
    id = db.IntField()
    price = db.FloatField()
Example #21
0
class OrderOffer(db.EmbeddedDocument):
    offer = db.ReferenceField('Offer')
    price = db.FloatField(default=0.0)
    oldprice = db.FloatField(default=0.0)
    quantity = db.IntField(default=1)
    variant = db.StringField(default=None)
Example #22
0
class Region(db.Document):
    id = db.IntField(primary_key=True)
    name = db.StringField(max_length=400)
    popularity = db.IntField(default=0)
    deliveries = db.ListField(db.EmbeddedDocumentField('RegionDelivery'),
                              default=[])

    updated_at = db.DateTimeField()

    meta = {
        'ordering': ['-popularity', '+id'],
        'indexes': ['name']
    }

    def __unicode__(self):
        return self.name

    def __str__(self):
        return u'%s(%s)' % (self.__class__.__name__, self.id)

    def int_popularity(self):
        popularity = (self.popularity or 0) + 1
        try:
            self.update(set__popularity=popularity)
        except db.OperationError:
            self.popularity = popularity
            self.save()

    def update_deliveries(self, methods):
        now = datetime.now()
        try:
            self.update(set__deliveries=methods, set__updated_at=now)
        except db.OperationError:
            self.deliveries = methods
            self.updated_at = now
            self.save()

    def get_delivery_prices(self, result, aid=None):
        d_methods = {str(d.id): {'id': d.id,
                                 'method': d.name} for d in DeliveryMethod.objects()}
        if not aid:
            aids = Offer.objects(available=True).distinct('aid')
            aid = random.choice(aids)

        methods = []

        if len(result):
            deliveries = result[0].deliveries
            for delivery in deliveries:
                d = d_methods.get(delivery.id, None)
                if d:
                    prices = []
                    for payment in delivery.payments:
                        price = payment.sum
                        if price is not None and price > 0 and price not in prices:
                            prices.append(price)

                    if len(prices):
                        price = min(prices)
                        d['price'] = price
                        methods.append(RegionDelivery(**d))
        return methods
Example #23
0
class CategoryStats(db.EmbeddedDocument):
    views = db.IntField(default=0)
    items = db.IntField(default=0)