From cbe33f7f8ca64387467b74989aa8da5b865774a7 Mon Sep 17 00:00:00 2001 From: Raphael Odini Date: Sun, 22 Dec 2024 15:05:38 +0100 Subject: [PATCH 1/2] feat(Price tags): script to match historical PriceTag with their prices --- .../match_price_tags_with_existing_prices.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py diff --git a/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py b/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py new file mode 100644 index 00000000..dd09ac43 --- /dev/null +++ b/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py @@ -0,0 +1,91 @@ +import sys +from collections import Counter + +from django.core.management.base import BaseCommand + +from open_prices.prices.constants import TYPE_CATEGORY, TYPE_PRODUCT +from open_prices.proofs.models import PriceTag, Proof + + +def stats(): + sys.stdout.write("PriceTag", PriceTag.objects.count()) + sys.stdout.write("Proof PRICE_TAG", Proof.objects.filter(type="PRICE_TAG").count()) + sys.stdout.write( + "PriceTag per status", + Counter(PriceTag.objects.all().values_list("status", flat=True)), + ) + sys.stdout.write( + "PriceTag without a price_id", + PriceTag.objects.filter(price_id__isnull=False).count(), + ) + + +class Command(BaseCommand): + """ + for each proof + try to match generated price_tags with existing prices + - skip proofs without price_tags or without prices + - skip price_tags that already have a price_id or that have no predictions + - finally loop on each price and try to match with the price_tag prediction data # noqa + """ + + help = "Match price tags with existing prices." + + def handle(self, *args, **options) -> None: # type: ignore + self.stdout.write("Stats before...") + stats() + + self.stdout.write("Running...") + for proof in Proof.objects.has_type_price_tag().prefetch_related( + "prices", "price_tags", "price_tags__predictions" + ): + if proof.price_tags.count() == 0: + continue + elif proof.prices.count() == 0: + continue + else: + for price_tag in proof.price_tags.all(): + if price_tag.price_id is not None: + continue + elif price_tag.predictions.count() == 0: + continue + else: + price_tag_prediction_data = price_tag.predictions.first().data + for price in proof.prices.all(): + if price.price_tags.count() > 0: + continue + elif ( + price.type == TYPE_PRODUCT + and ( + price.product_code + == price_tag_prediction_data["barcode"] + ) + and ( + str(price.price) + == str(price_tag_prediction_data["price"]) + ) + ): + price_tag.price_id = price.id + price_tag.status = 1 + price_tag.save() + break + elif ( + price.type == TYPE_CATEGORY + and ( + price.category_tag + == price_tag_prediction_data["product"] + ) + and ( + str(price.price) + == str(price_tag_prediction_data["price"]) + ) + ): + price_tag.price_id = price.id + price_tag.status = 1 + price_tag.save() + break + else: + continue + + self.stdout.write("Stats after...") + stats() From 730c1f64dae3ffd14967f6f587cedf305b6fcb47 Mon Sep 17 00:00:00 2001 From: Raphael Odini Date: Sun, 22 Dec 2024 15:30:58 +0100 Subject: [PATCH 2/2] Refactor --- .../match_price_tags_with_existing_prices.py | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py b/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py index dd09ac43..d80ff113 100644 --- a/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py +++ b/open_prices/proofs/management/commands/match_price_tags_with_existing_prices.py @@ -1,22 +1,38 @@ -import sys from collections import Counter from django.core.management.base import BaseCommand from open_prices.prices.constants import TYPE_CATEGORY, TYPE_PRODUCT +from open_prices.prices.models import Price from open_prices.proofs.models import PriceTag, Proof def stats(): - sys.stdout.write("PriceTag", PriceTag.objects.count()) - sys.stdout.write("Proof PRICE_TAG", Proof.objects.filter(type="PRICE_TAG").count()) - sys.stdout.write( - "PriceTag per status", + print("PriceTag:", PriceTag.objects.count()) + print("Proof PRICE_TAG:", Proof.objects.has_type_price_tag().count()) + print( + "PriceTag per status:", Counter(PriceTag.objects.all().values_list("status", flat=True)), ) - sys.stdout.write( - "PriceTag without a price_id", - PriceTag.objects.filter(price_id__isnull=False).count(), + print( + "PriceTag without a price_id:", + PriceTag.objects.filter(price_id__isnull=True).count(), + ) + + +def match_price_tag_with_product_price(price: Price, price_tag: PriceTag) -> bool: + return ( + price.type == TYPE_PRODUCT + and (price.product_code == price_tag.predictions.first().data["barcode"]) + and (str(price.price) == str(price_tag.predictions.first().data["price"])) + ) + + +def match_price_tag_with_category_price(price: Price, price_tag: PriceTag) -> bool: + return ( + price.type == TYPE_CATEGORY + and (price.product_code == price_tag.predictions.first().data["product"]) + and (str(price.price) == str(price_tag.predictions.first().data["price"])) ) @@ -35,7 +51,7 @@ def handle(self, *args, **options) -> None: # type: ignore self.stdout.write("Stats before...") stats() - self.stdout.write("Running...") + self.stdout.write("Running matching script...") for proof in Proof.objects.has_type_price_tag().prefetch_related( "prices", "price_tags", "price_tags__predictions" ): @@ -50,36 +66,17 @@ def handle(self, *args, **options) -> None: # type: ignore elif price_tag.predictions.count() == 0: continue else: - price_tag_prediction_data = price_tag.predictions.first().data for price in proof.prices.all(): if price.price_tags.count() > 0: continue - elif ( - price.type == TYPE_PRODUCT - and ( - price.product_code - == price_tag_prediction_data["barcode"] - ) - and ( - str(price.price) - == str(price_tag_prediction_data["price"]) - ) - ): + # match product price + elif match_price_tag_with_product_price(price, price_tag): price_tag.price_id = price.id price_tag.status = 1 price_tag.save() break - elif ( - price.type == TYPE_CATEGORY - and ( - price.category_tag - == price_tag_prediction_data["product"] - ) - and ( - str(price.price) - == str(price_tag_prediction_data["price"]) - ) - ): + # match category price + elif match_price_tag_with_category_price(price, price_tag): price_tag.price_id = price.id price_tag.status = 1 price_tag.save()