Esempio n. 1
0
class Order(db.Document):
    apishop_id = db.StringField(required=True)
    offers = db.ListField(db.EmbeddedDocumentField('OrderOffer'), default=[])
    ordered_at = db.DateTimeField()
    userinfo = db.DictField(default={})
    delivery_info = db.DictField(default={})
    comment = db.StringField()

    @property
    def get_delivery(self):
        return dict(price=self.delivery_info.get('delivery_price'),
                    total=self.delivery_info.get('order_sum')) \
            if self.delivery_info.get('delivery_price', None) is not None else None

    @property
    def get_total(self):
        return sum([offer.price * offer.quantity for offer in self.offers])

    def get_relevant_offers(self, max_items=4):
        offer_ids_not_to = [item.offer.id for item in self.offers]
        offer_ids = Offer.objects(available=True, id__nin=offer_ids_not_to).distinct('id')

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

        return Offer.objects(id__in=ids)

    def get_timer(self):
        timer = self.ordered_at + timedelta(minutes=5) - timedelta(seconds=20)
        if timer < datetime.now():
            return False
        return timer.strftime('%Y/%m/%d %H:%M:%S')

    def add_offer(self, offer, quantity=1, variant=None):
        o = OrderOffer(offer=offer,
                       price=offer.get_price(),
                       oldprice=offer.get_oldprice or 0.0,
                       quantity=quantity,
                       variant=variant)
        self.update(push__offers=o)
        self.reload()

    def populate_offers(self, sended_offers):

        if not isinstance(sended_offers, (list, tuple)):
            sended_offers = [sended_offers]

        for sended_offer in sended_offers:
            offer = Offer.objects(articul=sended_offer.get('articul')).first()
            if offer:
                self.offers.append(OrderOffer(offer=offer,
                                              price=offer.get_price(),
                                              oldprice=offer.get_oldprice or 0.0,
                                              quantity=sended_offer.get('quantity', 1),
                                              variant=sended_offer.get('variant', None)))
Esempio n. 2
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])
Esempio n. 3
0
class Artist(BaseModel):
    name = db.StringField()
    picture = db.StringField()
    songs = db.ListField(db.ReferenceField('Song'))
    meta = {
        # 'indexes': ['name', '$name']  # 不支持text search?
        'indexes': ['name']
    }

    @property
    def url(self):
        return ARTIST_URL.format(self.id)
Esempio n. 4
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
Esempio n. 5
0
class User(db.Document, UserMixin):
    email = db.StringField(max_length=255, unique=True)
    password = db.StringField(max_length=255, required=True)
    name = db.StringField(max_length=255)
    active = db.BooleanField(default=True)
    roles = db.ListField(db.ReferenceField(Role), default=[])
    registered_at = db.DateTimeField()

    def __unicode__(self):
        return 'User %s' % self.email

    @property
    def is_admin(self):
        return self.has_role('admin')

    def save(self, *args, **kwargs):
        self.registered_at = datetime.now()
        super(User, self).save(*args, **kwargs)
Esempio n. 6
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
Esempio n. 7
0
class Cart(db.Document):
    offers = db.ListField(db.EmbeddedDocumentField('CartOffer'), default=[])
    total = db.EmbeddedDocumentField('CartTotal')
    ordered = db.BooleanField(default=False)

    @classmethod
    def get_or_create(cls):
        cart_id = session.get('cart_id', None)

        if cart_id:
            cart = cls.objects(id=cart_id).first()
        else:
            cart = None

        if not cart:
            cart = cls()
            cart.save()
            session['cart_id'] = cart.id

        return cart

    @property
    def is_empty(self):
        return len(self.offers) == 0

    def get_offer_ids(self):
        return [offer.offer.id for offer in self.offers]

    def get_offer(self, offer_id):
        return Offer.objects(id=offer_id).first()

    def add_offer(self, offer_id, quantity=1, variant=None):
        offer = self.get_offer(offer_id)

        try:
            quantity = int(quantity)
        except (TypeError, ValueError):
            quantity = 1

        if offer and offer.is_in_stock:
            variant = offer.get_variant(variant)
            if variant:
                variant = str(variant.aid)

            for i, cart_offer in enumerate(self.offers):
                if offer.id == cart_offer.offer.id and variant and cart_offer.variant == variant:
                    new_quantity = cart_offer.quantity + quantity
                    self.offers[i] = CartOffer(offer=offer, quantity=new_quantity, variant=variant)
                    self.save()
                    break
                elif not variant and offer.id == cart_offer.offer.id:
                    new_quantity = cart_offer.quantity + quantity
                    self.offers[i] = CartOffer(offer=offer, quantity=new_quantity)
                    self.save()
                    break
            else:
                self.update(push__offers=CartOffer(offer=offer, quantity=quantity, variant=variant))
                self.reload()

                offer.set_add_to_cart()

        self.calculate_total()


    def remove_offer(self, offer_id, variant_id=None):
        try:
            offer_id = int(offer_id)
        except (TypeError, ValueError) as e:
            return

        variant_id = str(variant_id) if variant_id != '' else None

        for idx, offer in enumerate(self.offers):
            if variant_id:
                if offer_id == offer.offer.id and variant_id == offer.variant:
                    self.offers.pop(idx)
                    break
            else:
                if offer_id == offer.offer.id:
                    self.offers.pop(idx)
                    break

        self.save()
        self.calculate_total()


    def calculate_total(self):
        total_price = []
        total_quantity = []
        for offer in self.offers:
            total_price.append(offer.offer.get_price() * offer.quantity)
            total_quantity.append(offer.quantity)

        self.update(set__total=CartTotal(cost=sum(total_price),
                    count=sum(total_quantity)))

    def prepare_offers(self):
        return [dict(articul=offer.offer.articul,
                     aid=offer.offer.aid,
                     quantity=offer.quantity,
                     variant=offer.variant,
                     price=offer.offer.get_price()) for offer in self.offers]

    def send_order(self, order_form):

        from web.site import send_order

        order_id = send_order(self.prepare_offers(), order_form)
        self.clear_cart()

        return order_id

    def clear_cart(self):
        self.update(set__offers=[])
        self.reload()
        self.calculate_total()
Esempio n. 8
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)