Ejemplo n.º 1
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
Ejemplo n.º 2
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)
Ejemplo n.º 3
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)
Ejemplo n.º 4
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)
Ejemplo n.º 5
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()
Ejemplo n.º 6
0
class Page(DispatcherMixin, PositionMixin, IntIDMixin,
           PathMixin, BreadcrumbsMixin, db.Document):

    name = db.StringField(max_length=255, verbose_name=u'Название', required=True)

    parent = db.ReferenceField('Page', default=None, verbose_name=u'Родитель')
    metas = db.EmbeddedDocumentField(Metas)

    content = db.StringField(verbose_name=u'Описание')

    extra = db.DictField(default={'type': 'None'})

    meta = {
        'ordering': ['+position'],
        'indexes': ['path',
                    'parent',
                    'position']
    }

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

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

    @cached_property
    def get_title(self):
        if self.metas and self.metas.title:
            return self.metas.title

        return self.name

    def get_extra(self):
        return self.extra

    def save(self, *args, **kwargs):
        self.validate_position(kwargs.get('parent', self.parent))
        super(Page, self).save(*args, **kwargs)
Ejemplo n.º 7
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
        }
Ejemplo n.º 8
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
        }
Ejemplo n.º 9
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)
Ejemplo n.º 10
0
class CartOffer(db.EmbeddedDocument):
    offer = db.ReferenceField('Offer')
    quantity = db.IntField(default=1)
    variant = db.StringField(default=None)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
class Category(DispatcherMixin, PositionMixin, IntIDMixin,
               PathMixin, BreadcrumbsMixin, db.Document):
    name = db.StringField(max_length=255, verbose_name=u'Название')
    is_active = db.BooleanField(default=True, verbose_name=u'Включена')
    parent = db.ReferenceField('Category', default=None, verbose_name=u'Родитель')

    description = db.StringField(verbose_name=u'Описание')

    stats = db.EmbeddedDocumentField('CategoryStats')
    metas = db.EmbeddedDocumentField('Metas')

    meta = {
        'ordering': ['+position'],
        'indexes': [{'fields': ['$name', "$description"],
                     'default_language': 'russian',
                     'weights': {'name': 10, 'description': 2}
                    },
                    'path',
                    'parent',
                    'position']
    }

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

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

    def get_childrens(self):
        return self.__class__.objects(parent=self)

    def get_childs(self):
        return self.__class__.objects(__raw__={'path': {'$regex': '^{0}'
                                      .format(self.path)}}).order_by('path')

    def delete(self, *args, **kwargs):
        childrens = self.get_childrens()
        for child in childrens:
            child.delete()
        cache.delete_memoized(self.get_tree)
        super(Category, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.validate_position(kwargs.get('parent', self.parent))
        cache.delete_memoized(self.get_tree)
        super(Category, self).save(*args, **kwargs)

    @cache.memoize(3600)
    def get_tree_from(self):
        paths = self._split_path()
        root_category = self.__class__.objects.get(path=paths[0])
        tree = self.__class__.get_tree(parent=None, paths=paths)

        return tree

    def get_breadcrumbs(self):
        paths = self._split_path()
        breadcrumbs = []

        for path in paths[:-1]:
            obj = self.__class__.objects.get(path=path)
            breadcrumbs.append((obj.name, obj.path))

        return breadcrumbs

    @cache.memoize(60*60*24*7)
    def get_category_root_url(self):
        if not self.parent:
            return None

        root = self.__class__.objects(path=self.path.split('/')[0]).first()
        return urlparse.urljoin(request.url_root, url_for('site.dispatcher', path=root.path))

    @cache.memoize(60*60*24*7)
    def get_root(self):
        if not self.parent:
            return self

        paths = self._split_path()
        root = self.__class__.objects(path=paths[0]).first()
        return root

    @cached_property
    def get_title(self):
        if self.metas.title:
            return self.metas.title

        names = [self.name]
        parent = self.parent
        while parent:
            names.append(parent.name)
            parent = parent.parent

        separator = current_app.config.get('DEFAULT_TITLE_SEPARATOR', ' | ')

        return separator.join(names)

    @property
    def get_offers_count(self):
        count = self.stats.items if self.stats else 0
        return count

    @property
    def depth(self):
        return len(self.path.split('/')) - 1

    @classmethod
    @cache.memoize(3600)
    def get_tree(cls, parent=None, paths=None):
        objects = cls.objects(parent=parent)
        branch = []
        if not paths:
            for obj in objects:
                childs = obj.get_childs()
                if childs.count() > 1:
                    branch.append([obj, obj.__class__.get_tree(parent=obj)])
                else:
                    branch.append(obj)
        else:
            for obj in objects:
                if obj.path in paths:
                    childs = obj.get_childs()
                    if childs.count() > 1:
                        branch.append([obj, obj.__class__.get_tree(parent=obj, paths=paths)])
                    else:
                        branch.append(obj)
                else:
                    branch.append(obj)
        return branch

    def save(self, *args, **kwargs):
        cache.delete_memoized(self.__class__.get_tree)
        cache.delete_memoized(self.get_tree_from)
        cache.delete_memoized(self.get_category_root_url)
        cache.delete_memoized(self.get_root)
        super(Category, self).save(*args, **kwargs)

    @classmethod
    def post_save(cls, sender, document, **kwargs):
        childs = cls.objects(__raw__={'path': {'$regex': '^{0}'
                             .format(document.old_path)}}).order_by('path')
        if len(childs) > 1 and document.old_path != document.path:
            for child in childs:
                child.save()