예제 #1
0
    def test_can_use_add_price(self):
        res = Price.add_price(shop=self.shop,
                              product=self.product,
                              user=self.user,
                              date_to=Price.parse_date('2018-10-10'),
                              date_from=Price.parse_date('2018-09-09'),
                              price=10.0)

        self.assertTrue(res)
        self.assertEqual(Price.objects.count(), 1)
예제 #2
0
    def post(self, request):
        args = {'user': request.user}

        try:
            shop_id = request.data.get('shopId')
            args['shop'] = Shop.objects.get(pk=int(shop_id))
        except (TypeError, ValueError, Shop.DoesNotExist):
            return ApiMessage400(f'Μη έγκυρο shopId: {shop_id}')

        try:
            product_id = request.data.get('productId')
            args['product'] = Product.objects.get(pk=int(product_id))
        except (TypeError, ValueError, Product.DoesNotExist):
            return ApiMessage400(f'Μη έγκυρο productId: {product_id}')

        try:
            price = request.data.get('price')
            args['price'] = float(price)
        except (TypeError, ValueError):
            return ApiMessage400(f'Μη έγκυρη τιμή προϊόντος: {price}')

        try:
            date_from = request.data.get('dateFrom')
            args['date_from'] = Price.parse_date(date_from)
        except (TypeError, ValueError):
            return ApiMessage400(f'Μη έγκυρη μορφή ημερομηνίας: {date_from}')

        try:
            date_to = request.data.get('dateTo')
            args['date_to'] = Price.parse_date(date_to)
        except (TypeError, ValueError):
            return ApiMessage400(f'Μη έγκυρη μορφή ημερομηνίας: {date_to}')

        # try creating new price
        p = Price.add_price(**args)

        if p is None:
            return ApiMessage400(
                'Ήταν αδύνατη η πρόσθεση της πληροφορίας στο σύστημα')

        # all ok
        objects = p.explode(Price.convert_to_str(p.date_from),
                            Price.convert_to_str(p.date_to))
        res = dict(start=0,
                   count=len(objects),
                   total=len(objects),
                   prices=objects)
        return ApiResponse(res, status=200)
예제 #3
0
class PriceTestCase(TestCase):
    ''' Test suite for price model '''
    def setUp(self):
        User = get_user_model()

        self.shop = Shop(name='hexαδακτυλος',
                         address='Αριστοφανους 32',
                         coordinates=Point(22.18339, 39.89279))
        self.shop.save()

        self.product = Product(name='Αντρικιο',
                               description='Γυναικειο',
                               category='κουρεμα')
        self.product.save()

        userinfo = dict(username='******', password='******')
        self.user = User(**userinfo)
        self.user.save()
        grp, _ = Group.objects.get_or_create(name='Volunteer')
        self.user.groups.add(grp)

        self.entry = Price(shop=self.shop,
                           product=self.product,
                           user=self.user,
                           price=10.0)

    def test_can_add_price(self):
        ''' check if adding price works '''
        prev_count = Price.objects.count()
        self.entry.save()

        self.assertEqual(Price.objects.count(), prev_count + 1)

    def test_can_use_add_price(self):
        res = Price.add_price(shop=self.shop,
                              product=self.product,
                              user=self.user,
                              date_to=Price.parse_date('2018-10-10'),
                              date_from=Price.parse_date('2018-09-09'),
                              price=10.0)

        self.assertTrue(res)
        self.assertEqual(Price.objects.count(), 1)
예제 #4
0
    def test_post_prices(self):
        ''' check if POST /observatory/api/prices/ works '''
        prev_count = Price.objects.count()

        response = self._get_response(self.request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(prev_count + 1, Price.objects.count())

        # returned price object
        returned = json.loads(response.content)
        between = Price.static_dates_between(
            Price.parse_date(self.request['dateFrom']),
            Price.parse_date(self.request['dateTo']))

        # price object in db
        for price in returned['prices']:
            self.assertIn(Price.parse_date(price['date']), between)

            for x in ['shopId', 'productId', 'price']:
                self.assertEqual(price[x], self.request[x])
예제 #5
0
    def setUp(self):
        User = get_user_model()

        self.shop = Shop(name='hexαδακτυλος',
                         address='Αριστοφανους 32',
                         coordinates=Point(22.18339, 39.89279))
        self.shop.save()

        self.product = Product(name='Αντρικιο',
                               description='Γυναικειο',
                               category='κουρεμα')
        self.product.save()

        userinfo = dict(username='******', password='******')
        self.user = User(**userinfo)
        self.user.save()
        grp, _ = Group.objects.get_or_create(name='Volunteer')
        self.user.groups.add(grp)

        self.entry = Price(shop=self.shop,
                           product=self.product,
                           user=self.user,
                           price=10.0)
예제 #6
0
    def test_post_prices_updates_old_date_for_same_shop_product(self):
        response = self._get_response(self.request)
        old_price = Price.objects.get(shop__id=self.request['shopId'],
                                      date_from=Price.parse_date('2018-10-30'))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(Price.objects.count(), 1)
        self.assertEqual(old_price.date_to, Price.parse_date('2018-11-30'))

        response = self._get_response(self.request_away)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(Price.objects.count(), 2)

        req = dict(self.request)
        req['dateFrom'] = '2018-11-15'
        req['dateTo'] = '2019-02-02'

        response = self._get_response(req)

        # check if request was ok
        self.assertEqual(response.status_code, 200)

        # check if new price was added ok
        old_price = Price.objects.get(shop__id=self.request['shopId'],
                                      date_from=Price.parse_date('2018-10-30'))
        new_price = Price.objects.get(shop__id=self.request['shopId'],
                                      date_from=Price.parse_date('2018-11-15'))
        unaffected_price = Price.objects.get(
            shop__id=self.request_away['shopId'],
            date_from=Price.parse_date('2018-10-30'))

        # check to see if previous `price.dateTo` was updated
        self.assertEqual(old_price.date_to, new_price.date_from)

        # check if new `dateTo` is ok
        self.assertEqual(new_price.date_to, Price.parse_date(req['dateTo']))

        # check if price at other shop remained unaffected
        self.assertEqual(unaffected_price.date_to,
                         Price.parse_date(self.request_away['dateTo']))
예제 #7
0
    def setUp(self):
        User = get_user_model()

        self.factory = ApiRequestFactory()
        self.view = ParseUrlEncodedParametersMiddleware(PricesView.as_view())

        # create two shops
        self.shop = Shop(id=10,
                         name='hexαδακτυλος',
                         address='Αριστοφανους 32',
                         coordinates=Point(22.18339, 39.89279))
        self.shop_away = Shop(id=11,
                              name='χεξadaktylos',
                              address='Devils horn',
                              coordinates=Point(10, 10))
        self.shop.save()
        self.shop_away.save()

        # add a few tags
        shoptag = ShopTag('shoptag')
        shoptag.save()
        self.shop.tags.add(shoptag)
        self.shop.save()
        commontag_shop = ShopTag('commontag')
        commontag_shop.save()
        self.shop_away.tags.add(commontag_shop)
        self.shop_away.save()

        # create two products
        self.product = Product(id=20,
                               name='Αντρικιο',
                               description='Γυναικειο',
                               category='κουρεμα')
        self.product.save()
        self.product_2 = Product(id=21,
                                 name='λαδι μαλλιων',
                                 description='αντρικο',
                                 category='αναλωσιμο')
        self.product_2.save()

        # add tags
        producttag = ProductTag('producttag')
        producttag.save()
        self.product_2.tags.add(producttag)
        self.product_2.save()
        commontag_prod = ProductTag('commontag')
        commontag_prod.save()
        self.product.tags.add(commontag_prod)
        self.product.save()

        # create a user
        userinfo = dict(username='******', password='******')
        self.user = User(**userinfo)
        self.user.save()

        # add a few prices
        price_1 = Price(shop=self.shop,
                        product=self.product,
                        user=self.user,
                        date_from=Price.parse_date('2018-10-15'),
                        date_to=Price.parse_date('2018-10-20'),
                        price=10)
        price_1.save()

        price_2 = Price(shop=self.shop_away,
                        product=self.product,
                        user=self.user,
                        date_from=Price.parse_date('2018-10-16'),
                        date_to=Price.parse_date('2018-10-21'),
                        price=20)
        price_2.save()

        price_3 = Price(shop=self.shop,
                        product=self.product_2,
                        user=self.user,
                        date_from=Price.parse_date('2018-10-10'),
                        date_to=Price.parse_date('2018-10-23'),
                        price=20)
        price_3.save()
예제 #8
0
    def get(self, request):
        ######################################################################
        ## defaults
        ######################################################################

        start_default = 0
        count_default = 20
        sort_default = ['price|ASC']

        ######################################################################
        ## parsing url params
        ######################################################################

        start = request.data.get('start', start_default)
        count = request.data.get('count', count_default)

        geo_dist = request.data.get('geoDist', None)
        geo_lng = request.data.get('geoLng', None)
        geo_lat = request.data.get('geoLat', None)

        date_from_str = request.data.get('dateFrom', None)
        date_to_str = request.data.get('dateTo', None)

        shops = request.data.getlist('shops', None)
        products = request.data.getlist('products', None)
        tags = request.data.getlist('tags', None)

        sort = request.data.getlist('sort', sort_default)

        ######################################################################
        ## filters to apply, none by default
        ######################################################################

        geo_filter = False  # only if `geoXXXX` is passed
        date_filter = True  # always on
        shop_ids_filter = False  # only if `shops` is passed
        product_ids_filter = False  # only if `products` is passed
        tags_filter = False  # only if `tags` is passed

        ######################################################################
        ## check url parameters format
        ## FUTURE: in an ideal world, all fields are checked and multiple
        ##         error messages are returned. not today :)
        ######################################################################

        # start
        try:
            start = int(start)
            if start < 0:
                raise ValueError()
        except ValueError:
            return ApiMessage400('Το `start` πρεπει να ειναι θετικος ακεραιος')

        # count
        try:
            count = int(count)
            if count < 0:
                raise ValueError()
        except ValueError:
            return ApiMessage400('Το `count` πρεπει να ειναι θετικος ακεραιος')

        # geolocation checks
        if (geo_dist or geo_lat or geo_lng) is not None:
            if (geo_dist and geo_lat and geo_lng) is None:
                return ApiMessage400(
                    'Tα `geoDist`, `geoLng` και `geoLat` πρεπει να οριζονται μαζι'
                )

            # distance
            try:
                geo_dist = int(geo_dist)
                if geo_dist < 0:
                    raise ValueError()
            except ValueError:
                return ApiMessage400(
                    'Το `geoDist` πρεπει να ειναι θετικος ακεραιος')

            # latitude
            try:
                geo_lat = float(geo_lat)
                if not (-90 <= geo_lat <= 90):
                    raise ValueError()
            except ValueError:
                return ApiMessage400('Το `geoLat` δεν ειναι εγκυρο')

            # lοngitude
            try:
                geo_lng = float(geo_lng)
                if not (-180 <= geo_lng <= 180):
                    raise ValueError()
            except ValueError:
                return ApiMessage400('Το `geoLng` δεν ειναι εγκυρο')

            geo_point = Point(geo_lng, geo_lat, srid=4326)
            geo_filter = True

        # date checks
        if (date_from_str or date_to_str) is None:
            date_from = datetime.now()
            date_to = date_from + timedelta(days=1)
            date_from_str = date_to_str = Price.convert_to_str(date_from)
        else:
            if (date_from_str and date_to_str) is None:
                return ApiMessage400(
                    'Τα `dateFrom` και `dateTo` πρεπει να οριζονται μαζι')

            try:
                date_from = Price.parse_date(date_from_str)
                date_to = Price.parse_date(date_to_str)

                if not Price.check_dates(date_from, date_to):
                    return ApiMessage400(
                        'Το `dateFrom` πρεπει να ειναι παλαιοτερο του `dateTo`'
                    )
            except ValueError:
                return ApiMessage400(
                    'Οι ημερομηνίες πρέπει να είναι EEEE-MM-HH')

        # shop ids checks
        if shops:
            try:
                shops = list({int(x) for x in shops})  # keep unique
                shop_ids_filter = True
            except ValueError:
                return ApiMessage400(
                    'To `shops` πρεπει να ειναι λιστα ακεραιων')

        # product ids checks
        if products:
            try:
                products = list({int(x) for x in products})  # keep unique
                product_ids_filter = True
            except ValueError:
                return ApiMessage400(
                    'To `products` πρεπει να ειναι λιστα ακεραιων')

        # tags checks
        if tags:
            try:
                tags = list({str(x) for x in tags})  # keep unique
                tags_filter = True
            except ValueError:
                return ApiMessage400(
                    'To `tags` πρεπει να ειναι λιστα απο tags')

        ########################################################################
        ## querying
        ########################################################################
        prices_db = Price.objects.all()

        if shop_ids_filter:
            prices_db = prices_db.filter(shop__id__in=shops)

        if product_ids_filter:
            prices_db = prices_db.filter(product__id__in=products)

        if tags_filter:
            shops_with_tags = Shop.objects.with_tags(tags)
            products_with_tags = Product.objects.with_tags(tags)

            # this could be a model method :)
            prices_db = prices_db.filter(
                Q(shop__in=shops_with_tags)
                | Q(product__in=products_with_tags))

        if date_filter:
            # this could be a model method :)
            prices_db = prices_db.filter(
                Q(date_to__gte=date_from, date_to__lte=date_to)
                | Q(date_from__gte=date_from, date_from__lte=date_to)
                | Q(date_from__lte=date_from, date_to__gte=date_to))

        if geo_filter:
            shops_within_distance = Shop.objects.within_distance_from(
                geo_lat, geo_lng, km=geo_dist)
            prices_db = prices_db.filter(shop__in=shops_within_distance)
            prices_db = prices_db.annotate(
                geoDist=Distance('shop__coordinates', geo_point))

        prices = []
        for p in prices_db:
            # only keep dates within our range
            good = [
                goodone for goodone in p.explode(date_from_str, date_to_str)
                if date_from_str <= goodone['date'] <= date_to_str
            ]
            prices += good

        ########################################################################
        ## sorting
        ########################################################################

        sort_fields = []
        order_by_args = []
        for x in sort:
            try:
                sort_field, sort_type = x.split('|')

                if sort_field not in ALLOWED_SORT_FIELDS:
                    return ApiMessage400(
                        f'Αγνωστο κριτηριο ταξινομησης {sort_field}')
                if sort_field in sort_fields:
                    return ApiMessage400(
                        f'Το κριτηριο ταξινομησης {sort_field} εμφανιζεται πανω απο μια φορες'
                    )
                if sort_type not in ALLOWED_SORT_TYPE:
                    return ApiMessage400(
                        f'Μη εγκυρος τροπος ταξινομησης {sort_field}|{sort_type}'
                    )
                if sort_type == 'geoDist' and not geo_filter:
                    return ApiMessage400(
                        f'Μη εγκυρος τροπος ταξινομησης {sort_field}|{sort_type}, δεν εχει δοθει σημειο αναφορας'
                    )

                sort_fields.append(sort_field)

                if sort_field == 'geoDist':
                    sort_field = 'shopDist'

                order_by_args.append((sort_field, sort_type))
            except ValueError:
                ApiMessage400(f'Μη εγκυρο κριτηριο ταξινομησης {x}')

        # do the actual sorting
        if prices and order_by_args:
            Price.sort_objects(prices, order_by_args)

        ########################################################################
        ## paging
        ## NOTE: django pagination does not allow arbitrary `start` and `count`
        ## Just slice the objects properly
        ########################################################################
        total = len(prices)
        prices = prices[start:(start + count)]

        ########################################################################
        ## bake json and return it
        ########################################################################
        result = dict(start=start, count=count, total=total, prices=prices)
        return ApiResponse(result)
예제 #9
0
    def handle(self, *args, **options):
        fake = Faker('el_GR')
        lorem = fake.provider('faker.providers.lorem')
        name_provider = fake.provider('faker.providers.person')
        uncommon_words = lorem.word_list[2 * len(lorem.common_words):]
        company_names = uncommon_words + name_provider.last_names

        # categories
        categories = fake.words(NUM_CATEGORIES, ext_word_list=uncommon_words)

        # shop and product tags
        product_tags = fake.words(NUM_TAGS,
                                  ext_word_list=uncommon_words,
                                  unique=True)
        product_tags = [ProductTag(tag=x) for x in product_tags]
        ProductTag.objects.bulk_create(product_tags)
        product_tags = ProductTag.objects.all()

        shop_tags = fake.words(NUM_TAGS,
                               ext_word_list=uncommon_words,
                               unique=True)
        shop_tags = [ShopTag(tag=x) for x in shop_tags]
        ShopTag.objects.bulk_create(shop_tags)
        shop_tags = ShopTag.objects.all()

        # shops
        shops = []
        shop_names = fake.words(options['count'], ext_word_list=company_names)
        for name in shop_names:
            s = Shop(name=name.capitalize(),
                     address=fake.address(),
                     coordinates=Point(float(fake.local_longitude()),
                                       float(fake.local_latitude())))
            s.save()
            shops.append(s)

            for t in pick_max_count(shop_tags, 2):
                s.tags.add(t)

        # products
        products = []
        product_names = fake.words(options['count'],
                                   ext_word_list=uncommon_words)
        for name in product_names:
            p = Product(name=name.capitalize(),
                        description=fake.text(max_nb_chars=200,
                                              ext_word_list=None),
                        category=random.choice(categories))
            p.save()
            products.append(p)

            for t in pick_max_count(product_tags, 2):
                p.tags.add(t)

        # user is asoures
        User = get_user_model()
        asoures_user = User.objects.get(username='******')

        # for each shop, add prices for at most `products/3` products

        prices = []
        for s in shops:
            prods = pick_max_count(products, options['count'] // 3)
            for prod in prods:
                date_from = fake.date_between('-30d', 'today')
                date_to = fake.date_between('today', '+30d')
                p = Price(
                    shop=s,
                    product=prod,
                    user=asoures_user,
                    price=random.randint(5, 60),
                    date_from=date_from,
                    date_to=date_to,
                )
                prices.append(p)

        Price.objects.bulk_create(prices)