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)
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)
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)
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])
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_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']))
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()
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)
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)