Beispiel #1
0
 def put(self, args, shop_id, **_kwargs):
     shop = Shop.query.get_or_404(shop_id)
     shop.name = args['name']
     shop.address = args['address']
     shop.position = from_shape(Point(args['lng'], args['lat']), srid=4326)
     for tag in shop.tags:
         db.session.delete(tag)
     try:
         db.session.flush()
     except IntegrityError:
         db.session.rollback()
         return custom_error('Address/Position/Name', ['Same address, position and name with existing shop']), \
                ErrorCode.BAD_REQUEST
     # flush delete's to db to ensure delete stmts precede insert stmt and avoid integrity error
     shop.tags = [
         ShopTag(name=tag, shop=shop)
         for tag in unique_stripped(args['tags'])
     ]
     try:
         db.session.commit()
     except IntegrityError:
         db.session.rollback()
         return custom_error(
             'tags', ['Duplicate tags'
                      ]), ErrorCode.BAD_REQUEST  # we should never get here
     return shop_schema.dump(shop).data
Beispiel #2
0
    def patch(self, args, prod_id, **_kwargs):
        del args['format']
        if len(args) != 1:
            return custom_error('patch', ['Specify exactly one of: name, description, category or tags']),\
                   ErrorCode.BAD_REQUEST

        product = Product.query.get_or_404(prod_id)
        changed = next(iter(args.keys()))
        if changed == 'tags':
            for tag in product.tags:
                db.session.delete(tag)  # delete old product tags
            db.session.flush()
            product.tags = [
                ProductTag(name=tag, product=product)
                for tag in unique_stripped(args['tags'])
            ]
        else:
            setattr(product, changed, args[changed])

        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            custom_error('tags',
                         ['Duplicate tags'
                          ]), ErrorCode.BAD_REQUEST  # we should never get here

        return prod_schema.dump(product).data
Beispiel #3
0
    def post(self, args):
        user = User.query.filter(User.username == args['username']).first()
        if not user:
            return custom_error('username', ['Invalid username']), ErrorCode.BAD_REQUEST
        elif not user.verify_password(args['password']):
            return custom_error('password', ['Wrong password']), ErrorCode.BAD_REQUEST

        _login(user)

        return {'token': user.token}
Beispiel #4
0
 def decorated(*args, **kwargs):
     token = request.headers.get('X-OBSERVATORY-AUTH')
     if not token:
         return custom_error('token', ['No authorization token provided']), ErrorCode.FORBIDDEN
     user = User.query.filter(User.token == token).first()
     if not user:
         return custom_error('token', ['Invalid token']), ErrorCode.FORBIDDEN
     kwargs['is_admin'] = user.is_admin
     kwargs['token'] = user.token
     return f(*args, **kwargs)
Beispiel #5
0
    def post(self, args):
        user = User(username=args['username'], password=args['password'])
        db.session.add(user)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return custom_error('username', ['Username already in use']), ErrorCode.BAD_REQUEST

        return {'message': 'OK'}
Beispiel #6
0
    def post(self, args, **_kwargs):
        date1 = args['dateFrom']
        date2 = args['dateTo']
        if not (date1 <= date2):
            return custom_error(
                'date',
                ['date(From) must be before date(To)']), ErrorCode.BAD_REQUEST

        shop = Shop.query.filter_by(id=args['shop_id']).first()
        product = Product.query.filter_by(id=args['product_id']).first()
        if not shop:
            return custom_error('shop',
                                ['Invalid shop id']), ErrorCode.NOT_FOUND
        elif not product:
            return custom_error('product',
                                ['Invalid product id']), ErrorCode.NOT_FOUND

        del args['format']
        del args['dateFrom']
        del args['dateTo']

        for d in [
                date1 + timedelta(days=x)
                for x in range((date2 - date1).days + 1)
        ]:
            new_price = dict(**args, date=d)
            upsert = insert(Price).values(new_price).on_conflict_do_update(
                constraint="price_psd_c", set_=new_price)
            db.session.execute(upsert)
        db.session.commit()
        new_prices = Price.query.filter(Price.product_id == args['product_id'],
                                        Price.shop_id == args['shop_id'],
                                        Price.date.between(date1,
                                                           date2)).all()
        return {
            'start': 0,
            'total': (date2 - date1).days + 1,
            'count': (date2 - date1).days + 1,
            # why you ask ? no idea, field must be present, can't find any sensible value
            'prices':
            PricesResource.PriceSchema(many=True).dump(new_prices).data
        }
Beispiel #7
0
 def delete(self, _args, shop_id, is_admin, **_kwargs):
     shop = Shop.query.get(shop_id)
     if not shop:
         return custom_error('shop',
                             ['Invalid shop id']), ErrorCode.NOT_FOUND
     if is_admin:
         db.session.delete(shop)
     else:
         shop.withdrawn = True
     db.session.commit()
     return {'message': 'OK'}
Beispiel #8
0
    def patch(self, args, shop_id, **_kwargs):
        del args['format']
        if len(args) != 1:
            return custom_error('patch', ['Specify exactly one of: name, address, lng, lat, tags']), \
                   ErrorCode.BAD_REQUEST
        shop = Shop.query.get_or_404(shop_id)
        changed = next(iter(args.keys()))
        if changed == 'tags':
            for tag in shop.tags:
                db.session.delete(tag)
            db.session.flush()
            shop.tags = [
                ShopTag(name=tag, shop=shop)
                for tag in unique_stripped(args['tags'])
            ]
        elif changed == 'lat':
            old = to_shape(shop.position)
            shop.position = from_shape(Point(old.x, args['lat']), srid=4326)
        elif changed == 'lng':
            old = to_shape(shop.position)
            shop.position = from_shape(Point(args['lng'], old.y), srid=4326)
        else:
            setattr(shop, changed, args[changed])

        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            if changed == 'tags':
                return custom_error(
                    'tags',
                    ['Duplicate tags'
                     ]), ErrorCode.BAD_REQUEST  # we should never get here
            else:
                return custom_error('Address/Position/Name', ['Same address, position and name with existing shop']), \
                       ErrorCode.BAD_REQUEST

        return ShopSchema().dump(shop).data
Beispiel #9
0
 def post(self, args, **_kwargs):
     position = from_shape(Point(args['lng'], args['lat']), srid=4326)
     new_shop = Shop(name=args['name'],
                     address=args['address'],
                     position=position,
                     withdrawn=False)
     new_shop.tags = [
         ShopTag(name=tag, shop=new_shop)
         for tag in unique_stripped(args['tags'])
     ]
     db.session.add(new_shop)
     try:
         db.session.commit()
     except IntegrityError as e:
         db.session.rollback()
         if "shop_pna_c" in e.orig:
             return custom_error('Address/Position/Name', ['Same address, position and name with existing shop']), \
                    ErrorCode.BAD_REQUEST
         else:
             return custom_error(
                 'tags',
                 ['Duplicate tags'
                  ]), ErrorCode.BAD_REQUEST  # we should never get here
     return shop_schema.dump(new_shop).data
Beispiel #10
0
    def post(self, args, **_kwargs):
        product = Product(name=args['name'],
                          description=args['description'],
                          category=args['category'],
                          withdrawn=False)
        product.tags = [
            ProductTag(name=tag, product=product)
            for tag in unique_stripped(args['tags'])
        ]
        db.session.add(product)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return custom_error(
                'tags', ['Duplicate tags'
                         ]), ErrorCode.BAD_REQUEST  # we should never get here

        return prod_schema.dump(product).data
Beispiel #11
0
    def put(self, args, prod_id, **_kwargs):
        product = Product.query.get_or_404(prod_id)
        product.name = args['name']
        product.description = args['description']
        product.category = args['category']
        for tag in product.tags:
            db.session.delete(tag)  # delete old product tags
        db.session.flush()
        # flush delete's to db to ensure delete stmts precedes insert stmt and avoid integrity error
        product.tags = [
            ProductTag(name=tag, product=product)
            for tag in unique_stripped(args['tags'])
        ]

        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            return custom_error(
                'tags', ['Duplicate tags'
                         ]), ErrorCode.BAD_REQUEST  # we should never get here

        return prod_schema.dump(product).data
Beispiel #12
0
    def get(self, args):
        shop_ids = args['shops']
        product_ids = args['products']
        tags = args['tags']

        with_geo = args['geoDist'] is not None and args[
            'geoLng'] is not None and args['geoLat'] is not None
        no_geo = args['geoDist'] is None and args['geoLng'] is None and args[
            'geoLat'] is None
        if not (with_geo or no_geo):
            return custom_error(
                'geo',
                ['Invalid geo parameters combination']), ErrorCode.BAD_REQUEST

        with_date = (args['dateFrom'] is not None and args['dateTo'] is not None) and \
                    (args['dateFrom'] <= args['dateTo'])
        no_date = args['dateFrom'] is None and args['dateTo'] is None
        if not (with_date or no_date):
            return custom_error(
                'date',
                ['Invalid date parameters combination']), ErrorCode.BAD_REQUEST

        sorts = [x.split('|') for x in args['sort']]
        for sort in sorts:
            if (sort[0] == 'geoDist' and no_geo) or (sort[0] == 'date'
                                                     and no_date):
                return custom_error(
                    'sort', ['Invalid sort parameter']), ErrorCode.BAD_REQUEST

        # extra validation, some combinations are illegal

        dist_col = None
        if with_geo:
            dist = func.ST_Distance(Shop.position, from_shape(Point(args['geoLng'], args['geoLat']), srid=4326), True).\
                label('dist')
            query = db.session.query(Price, dist)
        else:
            query = Price.query

        query = query.join(Price.product, Price.shop)
        today = date.today()
        date_from = args['dateFrom'] if args['dateFrom'] else today
        date_to = args['dateTo'] if args['dateTo'] else today
        query = query.filter(Price.date.between(date_from, date_to))

        if shop_ids:
            query = query.filter(Shop.id.in_(shop_ids))
        if product_ids:
            query = query.filter(Product.id.in_(product_ids))

        if tags:
            query = query.filter(
                or_(
                    Product.tags.any(
                        func.lower(ProductTag.name).in_(map(str.lower, tags))),
                    Shop.tags.any(
                        func.lower(ShopTag.name).in_(map(str.lower, tags)))))

        if with_geo:
            subq = query.subquery()
            dist_col = subq.c.dist
            query = db.session.query(Price,
                                     dist_col).select_entity_from(subq).filter(
                                         dist_col < 1000 * args['geoDist'])
            # nested query to avoid recalculating distance expr
            # WHERE and HAVING clauses can't refer to column names (dist)

        def to_sort_operator(field, order):
            sort_field = {
                'geoDist': dist_col,
                'price': Price.price,
                'date': Price.date
            }[field]

            sort_order = {'ASC': asc, 'DESC': desc}[order]

            return sort_order(sort_field)

        query = query.order_by(
            *[to_sort_operator(field, order) for field, order in sorts])

        start = args['start']
        count = args['count']
        total = query.count()
        query = query.offset(start).limit(count)
        prices_page = query.all()
        prices = PricesResource.PriceSchema(many=True).dump(prices_page).data
        return {
            'start': start,
            'count': count,
            'total': total,
            'prices': prices
        }