Skip to content

Commit

Permalink
feat(Products): Stats: new location country count & price currency co…
Browse files Browse the repository at this point in the history
…unt (#699)
  • Loading branch information
raphodn authored Feb 2, 2025
1 parent 6834f9c commit 3e9c58c
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 22 deletions.
6 changes: 4 additions & 2 deletions open_prices/common/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ def update_product_counts_task():
Update all product field counts
"""
for product in Product.objects.with_stats().filter(price_count_annotated__gte=1):
for field in Product.COUNT_FIELDS:
getattr(product, f"update_{field}")()
product.update_price_count()
product.update_location_count()
product.update_user_count()
product.update_proof_count()


def update_user_counts_task():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.4 on 2025-02-02 17:04

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("products", "0004_product_proof_count"),
]

operations = [
migrations.AddField(
model_name="product",
name="location_type_osm_country_count",
field=models.PositiveIntegerField(blank=True, default=0, null=True),
),
migrations.AddField(
model_name="product",
name="price_currency_count",
field=models.PositiveIntegerField(blank=True, default=0, null=True),
),
]
31 changes: 28 additions & 3 deletions open_prices/products/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ class Product(models.Model):
"nova_group",
"unique_scans_n",
]
COUNT_FIELDS = ["price_count", "location_count", "user_count", "proof_count"]
COUNT_FIELDS = [
"price_count",
"price_currency_count",
"location_count",
"location_type_osm_country_count",
"user_count",
"proof_count",
]

code = models.CharField(unique=True)

Expand All @@ -51,7 +58,11 @@ class Product(models.Model):
unique_scans_n = models.PositiveIntegerField(default=0, blank=True, null=True)

price_count = models.PositiveIntegerField(default=0, blank=True, null=True)
price_currency_count = models.PositiveIntegerField(default=0, blank=True, null=True)
location_count = models.PositiveIntegerField(default=0, blank=True, null=True)
location_type_osm_country_count = models.PositiveIntegerField(
default=0, blank=True, null=True
)
user_count = models.PositiveIntegerField(default=0, blank=True, null=True)
proof_count = models.PositiveIntegerField(default=0, blank=True, null=True)

Expand Down Expand Up @@ -102,9 +113,13 @@ def price__stats(self, exclude_discounted=False):

def update_price_count(self):
self.price_count = self.prices.count()
self.save(update_fields=["price_count"])
self.price_currency_count = (
self.prices.values_list("currency", flat=True).distinct().count()
)
self.save(update_fields=["price_count", "price_currency_count"])

def update_location_count(self):
from open_prices.locations import constants as location_constants
from open_prices.prices.models import Price

self.location_count = (
Expand All @@ -113,7 +128,17 @@ def update_location_count(self):
.distinct()
.count()
)
self.save(update_fields=["location_count"])
self.location_type_osm_country_count = (
Price.objects.filter(
product=self,
location_id__isnull=False,
location__type=location_constants.TYPE_OSM,
)
.values_list("location__osm_address_country", flat=True)
.distinct()
.count()
)
self.save(update_fields=["location_count", "location_type_osm_country_count"])

def update_user_count(self):
from open_prices.prices.models import Price
Expand Down
44 changes: 32 additions & 12 deletions open_prices/products/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
"osm_name": "Monoprix",
"osm_lat": "45.1805534",
"osm_lon": "5.7153387",
"osm_address_city": "Grenoble",
"osm_address_country": "France",
"price_count": 15,
}
LOCATION_ONLINE_DECATHLON = {
"type": location_constants.TYPE_ONLINE,
"website_url": "https://www.decathlon.fr",
"price_count": 15,
}

Expand Down Expand Up @@ -112,27 +119,31 @@ class ProductPropertyTest(TestCase):
def setUpTestData(cls):
cls.product = ProductFactory(code="0123456789100", product_quantity=1000)
cls.user = UserFactory()
cls.location = LocationFactory(**LOCATION_OSM_NODE_652825274)
cls.location_1 = LocationFactory(**LOCATION_OSM_NODE_652825274)
cls.location_2 = LocationFactory(**LOCATION_ONLINE_DECATHLON)
cls.proof = ProofFactory(
location_osm_id=cls.location.osm_id,
location_osm_type=cls.location.osm_type,
location_osm_id=cls.location_1.osm_id,
location_osm_type=cls.location_1.osm_type,
currency="EUR",
owner=cls.user.user_id,
)
PriceFactory(
product_code=cls.product.code,
location_osm_id=cls.location.osm_id,
location_osm_type=cls.location.osm_type,
location_osm_id=cls.location_1.osm_id,
location_osm_type=cls.location_1.osm_type,
proof_id=cls.proof.id,
price=1.0,
currency=cls.proof.currency,
price_is_discounted=True,
price_without_discount=1.5,
owner=cls.user.user_id,
)
PriceFactory(
product_code=cls.product.code,
location_osm_id=cls.location.osm_id,
location_osm_type=cls.location.osm_type,
location_osm_id=cls.location_2.osm_id,
location_osm_type=cls.location_2.osm_type,
price=2.0,
currency="USD",
owner=cls.user.user_id,
)

Expand Down Expand Up @@ -170,32 +181,41 @@ def test_price__stats(self):

def test_update_price_count(self):
self.product.refresh_from_db()
self.assertEqual(self.product.price_count, 2) # price post_save
self.assertEqual(self.product.price_count, 2) # price signals
self.assertEqual(self.product.price_currency_count, 0)
# update_price_count() should fix price counts
self.product.update_price_count()
self.assertEqual(self.product.price_count, 2)
self.assertEqual(self.product.price_currency_count, 2)
# bulk delete prices to skip signals
self.product.prices.all().delete()
self.assertEqual(self.product.price_count, 2) # should be 0
# update_price_count() should fix price_count
self.assertEqual(self.product.price_currency_count, 2) # should be 0
# update_price_count() should fix price counts
self.product.update_price_count()
self.assertEqual(self.product.price_count, 0) # all deleted
self.assertEqual(self.product.price_currency_count, 0)

def test_update_location_count(self):
self.product.refresh_from_db()
self.assertEqual(self.product.location_count, 0)
# update_location_count() should fix location_count
self.assertEqual(self.product.location_type_osm_country_count, 0)
# update_location_count() should fix location counts
self.product.update_location_count()
self.assertEqual(self.product.location_count, 1)
self.assertEqual(self.product.location_type_osm_country_count, 1)

def test_update_user_count(self):
self.product.refresh_from_db()
self.assertEqual(self.product.user_count, 0)
# update_user_count() should fix user_count
# update_user_count() should fix user counts
self.product.update_user_count()
self.assertEqual(self.product.user_count, 1)

def test_update_proof_count(self):
self.product.refresh_from_db()
self.assertEqual(self.product.proof_count, 0)
# update_proof_count() should fix proof_count
# update_proof_count() should fix proof counts
self.product.update_proof_count()
self.assertEqual(self.product.proof_count, 1)

Expand Down
3 changes: 1 addition & 2 deletions open_prices/users/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from django.db import models
from django.utils import timezone

from open_prices.locations import constants as location_constants


class UserQuerySet(models.QuerySet):
def has_prices(self):
Expand Down Expand Up @@ -62,6 +60,7 @@ def update_price_count(self):
self.save(update_fields=["price_count", "price_currency_count"])

def update_location_count(self):
from open_prices.locations import constants as location_constants
from open_prices.proofs.models import Proof

self.location_count = (
Expand Down
6 changes: 3 additions & 3 deletions open_prices/users/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def setUpTestData(cls):
location_osm_id=cls.location_2.osm_id,
location_osm_type=cls.location_2.osm_type,
price=2.0,
currency=cls.proof.currency,
currency="USD",
owner=cls.user.user_id,
)

Expand All @@ -74,11 +74,11 @@ def test_update_price_count(self):
# update_price_count() should fix price counts
self.user.update_price_count()
self.assertEqual(self.user.price_count, 2)
self.assertEqual(self.user.price_currency_count, 1)
self.assertEqual(self.user.price_currency_count, 2)
# bulk delete prices to skip signals
Price.objects.filter(owner=self.user.user_id).delete()
self.assertEqual(self.user.price_count, 2) # should be 0
self.assertEqual(self.user.price_currency_count, 1) # should be 0
self.assertEqual(self.user.price_currency_count, 2) # should be 0
# update_price_count() should fix price counts
self.user.update_price_count()
self.assertEqual(self.user.price_count, 0)
Expand Down

0 comments on commit 3e9c58c

Please sign in to comment.