Reconcile review queue against current catalog state
This commit is contained in:
@@ -6,9 +6,94 @@ from unittest import mock
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
import enrich_costco
|
||||
import review_products
|
||||
|
||||
|
||||
def write_review_source_files(tmpdir, rows):
|
||||
giant_items_csv = Path(tmpdir) / "giant_items.csv"
|
||||
costco_items_csv = Path(tmpdir) / "costco_items.csv"
|
||||
giant_orders_csv = Path(tmpdir) / "giant_orders.csv"
|
||||
costco_orders_csv = Path(tmpdir) / "costco_orders.csv"
|
||||
|
||||
fieldnames = enrich_costco.OUTPUT_FIELDS
|
||||
grouped_rows = {"giant": [], "costco": []}
|
||||
grouped_orders = {"giant": {}, "costco": {}}
|
||||
|
||||
for index, row in enumerate(rows, start=1):
|
||||
retailer = row.get("retailer", "giant")
|
||||
normalized_row = {field: "" for field in fieldnames}
|
||||
normalized_row.update(
|
||||
{
|
||||
"retailer": retailer,
|
||||
"order_id": row.get("order_id", f"{retailer[0]}{index}"),
|
||||
"line_no": row.get("line_no", str(index)),
|
||||
"normalized_row_id": row.get(
|
||||
"normalized_row_id",
|
||||
f"{retailer}:{row.get('order_id', f'{retailer[0]}{index}')}:{row.get('line_no', str(index))}",
|
||||
),
|
||||
"normalized_item_id": row.get("normalized_item_id", ""),
|
||||
"order_date": row.get("purchase_date", ""),
|
||||
"item_name": row.get("raw_item_name", ""),
|
||||
"item_name_norm": row.get("normalized_item_name", ""),
|
||||
"image_url": row.get("image_url", ""),
|
||||
"upc": row.get("upc", ""),
|
||||
"line_total": row.get("line_total", ""),
|
||||
"net_line_total": row.get("net_line_total", ""),
|
||||
"matched_discount_amount": row.get("matched_discount_amount", ""),
|
||||
"qty": row.get("qty", "1"),
|
||||
"unit": row.get("unit", "EA"),
|
||||
"normalized_quantity": row.get("normalized_quantity", ""),
|
||||
"normalized_quantity_unit": row.get("normalized_quantity_unit", ""),
|
||||
"size_value": row.get("size_value", ""),
|
||||
"size_unit": row.get("size_unit", ""),
|
||||
"pack_qty": row.get("pack_qty", ""),
|
||||
"measure_type": row.get("measure_type", "each"),
|
||||
"retailer_item_id": row.get("retailer_item_id", ""),
|
||||
"price_per_each": row.get("price_per_each", ""),
|
||||
"price_per_lb": row.get("price_per_lb", ""),
|
||||
"price_per_oz": row.get("price_per_oz", ""),
|
||||
"is_discount_line": row.get("is_discount_line", "false"),
|
||||
"is_coupon_line": row.get("is_coupon_line", "false"),
|
||||
"is_fee": row.get("is_fee", "false"),
|
||||
"raw_order_path": row.get("raw_order_path", ""),
|
||||
}
|
||||
)
|
||||
grouped_rows[retailer].append(normalized_row)
|
||||
order_id = normalized_row["order_id"]
|
||||
grouped_orders[retailer].setdefault(
|
||||
order_id,
|
||||
{
|
||||
"order_id": order_id,
|
||||
"store_name": row.get("store_name", ""),
|
||||
"store_number": row.get("store_number", ""),
|
||||
"store_city": row.get("store_city", ""),
|
||||
"store_state": row.get("store_state", ""),
|
||||
},
|
||||
)
|
||||
|
||||
for path, source_rows in [
|
||||
(giant_items_csv, grouped_rows["giant"]),
|
||||
(costco_items_csv, grouped_rows["costco"]),
|
||||
]:
|
||||
with path.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
writer.writerows(source_rows)
|
||||
|
||||
order_fields = ["order_id", "store_name", "store_number", "store_city", "store_state"]
|
||||
for path, source_rows in [
|
||||
(giant_orders_csv, grouped_orders["giant"].values()),
|
||||
(costco_orders_csv, grouped_orders["costco"].values()),
|
||||
]:
|
||||
with path.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=order_fields)
|
||||
writer.writeheader()
|
||||
writer.writerows(source_rows)
|
||||
|
||||
return giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv
|
||||
|
||||
|
||||
class ReviewWorkflowTests(unittest.TestCase):
|
||||
def test_build_review_queue_groups_unresolved_purchases(self):
|
||||
queue_rows = review_products.build_review_queue(
|
||||
@@ -114,66 +199,47 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
purchase_fields = [
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
]
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=purchase_fields)
|
||||
writer.writeheader()
|
||||
writer.writerows(
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "costco",
|
||||
"order_id": "c2",
|
||||
"line_no": "2",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "7.49",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-12",
|
||||
"retailer": "costco",
|
||||
"order_id": "c1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "https://example.test/mixed-pepper.jpg",
|
||||
"upc": "",
|
||||
"line_total": "6.99",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-10",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"catalog_id": "cat_mix",
|
||||
"raw_item_name": "MIXED PEPPER",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "5.99",
|
||||
},
|
||||
]
|
||||
)
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "costco",
|
||||
"order_id": "c2",
|
||||
"line_no": "2",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "7.49",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-12",
|
||||
"retailer": "costco",
|
||||
"order_id": "c1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "https://example.test/mixed-pepper.jpg",
|
||||
"upc": "",
|
||||
"line_total": "6.99",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-10",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "5.99",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
@@ -195,11 +261,34 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
"updated_at": "",
|
||||
}
|
||||
)
|
||||
with links_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.PRODUCT_LINK_FIELDS)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
{
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"catalog_id": "cat_mix",
|
||||
"link_method": "manual_link",
|
||||
"link_confidence": "high",
|
||||
"review_status": "approved",
|
||||
"reviewed_by": "",
|
||||
"reviewed_at": "",
|
||||
"link_notes": "",
|
||||
}
|
||||
)
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--giant-items-enriched-csv",
|
||||
str(giant_items_csv),
|
||||
"--costco-items-enriched-csv",
|
||||
str(costco_items_csv),
|
||||
"--giant-orders-csv",
|
||||
str(giant_orders_csv),
|
||||
"--costco-orders-csv",
|
||||
str(costco_orders_csv),
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
@@ -234,40 +323,23 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_ice",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "SB BAGGED ICE 20LB",
|
||||
"normalized_item_name": "BAGGED ICE",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "3.50",
|
||||
}
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
@@ -276,6 +348,14 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--giant-items-enriched-csv",
|
||||
str(giant_items_csv),
|
||||
"--costco-items-enriched-csv",
|
||||
str(costco_items_csv),
|
||||
"--giant-orders-csv",
|
||||
str(giant_orders_csv),
|
||||
"--costco-orders-csv",
|
||||
str(costco_orders_csv),
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
@@ -301,68 +381,47 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerows(
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "costco",
|
||||
"order_id": "c2",
|
||||
"line_no": "2",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "7.49",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-12",
|
||||
"retailer": "costco",
|
||||
"order_id": "c1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "6.99",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-10",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"catalog_id": "cat_mix",
|
||||
"raw_item_name": "MIXED PEPPER",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "5.99",
|
||||
},
|
||||
]
|
||||
)
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "costco",
|
||||
"order_id": "c2",
|
||||
"line_no": "2",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "7.49",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-12",
|
||||
"retailer": "costco",
|
||||
"order_id": "c1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "cnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "6.99",
|
||||
},
|
||||
{
|
||||
"purchase_date": "2026-03-10",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"raw_item_name": "MIXED PEPPER",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "5.99",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
@@ -384,10 +443,33 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
"updated_at": "",
|
||||
}
|
||||
)
|
||||
with links_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.PRODUCT_LINK_FIELDS)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
{
|
||||
"normalized_item_id": "gnorm_mix",
|
||||
"catalog_id": "cat_mix",
|
||||
"link_method": "manual_link",
|
||||
"link_confidence": "high",
|
||||
"review_status": "approved",
|
||||
"reviewed_by": "",
|
||||
"reviewed_at": "",
|
||||
"link_notes": "",
|
||||
}
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--giant-items-enriched-csv",
|
||||
str(giant_items_csv),
|
||||
"--costco-items-enriched-csv",
|
||||
str(costco_items_csv),
|
||||
"--giant-orders-csv",
|
||||
str(giant_orders_csv),
|
||||
"--costco-orders-csv",
|
||||
str(costco_orders_csv),
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
@@ -422,40 +504,23 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_ice",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "SB BAGGED ICE 20LB",
|
||||
"normalized_item_name": "BAGGED ICE",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "3.50",
|
||||
}
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
@@ -481,6 +546,14 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--giant-items-enriched-csv",
|
||||
str(giant_items_csv),
|
||||
"--costco-items-enriched-csv",
|
||||
str(costco_items_csv),
|
||||
"--giant-orders-csv",
|
||||
str(giant_orders_csv),
|
||||
"--costco-orders-csv",
|
||||
str(costco_orders_csv),
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
@@ -506,40 +579,23 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"normalized_item_id": "gnorm_skip",
|
||||
"catalog_id": "",
|
||||
"raw_item_name": "TEST ITEM",
|
||||
"normalized_item_name": "TEST ITEM",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "1.00",
|
||||
}
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
@@ -548,6 +604,14 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--giant-items-enriched-csv",
|
||||
str(giant_items_csv),
|
||||
"--costco-items-enriched-csv",
|
||||
str(costco_items_csv),
|
||||
"--giant-orders-csv",
|
||||
str(giant_orders_csv),
|
||||
"--costco-orders-csv",
|
||||
str(costco_orders_csv),
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
@@ -578,30 +642,12 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "catalog.csv"
|
||||
links_csv = Path(tmpdir) / "product_links.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"normalized_item_id",
|
||||
"catalog_id",
|
||||
"retailer",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
"order_id",
|
||||
"line_no",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
giant_items_csv, costco_items_csv, giant_orders_csv, costco_orders_csv = write_review_source_files(
|
||||
tmpdir,
|
||||
[
|
||||
{
|
||||
"purchase_date": "2026-03-15",
|
||||
"normalized_item_id": "gnorm_ice",
|
||||
"catalog_id": "",
|
||||
"retailer": "giant",
|
||||
"raw_item_name": "SB BAGGED ICE 20LB",
|
||||
"normalized_item_name": "BAGGED ICE",
|
||||
@@ -611,7 +657,8 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
}
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with mock.patch.object(
|
||||
review_products.click,
|
||||
@@ -619,6 +666,10 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
side_effect=["n", "ICE", "frozen", "ice", "manual merge", "q"],
|
||||
):
|
||||
review_products.main.callback(
|
||||
giant_items_enriched_csv=str(giant_items_csv),
|
||||
costco_items_enriched_csv=str(costco_items_csv),
|
||||
giant_orders_csv=str(giant_orders_csv),
|
||||
costco_orders_csv=str(costco_orders_csv),
|
||||
purchases_csv=str(purchases_csv),
|
||||
queue_csv=str(queue_csv),
|
||||
resolutions_csv=str(resolutions_csv),
|
||||
@@ -647,6 +698,63 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
self.assertEqual("ICE", catalog_rows[0]["catalog_name"])
|
||||
self.assertEqual(catalog_rows[0]["catalog_id"], link_rows[0]["catalog_id"])
|
||||
|
||||
def test_build_review_queue_readds_orphaned_and_incomplete_links(self):
|
||||
purchase_rows = [
|
||||
{
|
||||
"normalized_item_id": "gnorm_orphan",
|
||||
"catalog_id": "cat_missing",
|
||||
"retailer": "giant",
|
||||
"raw_item_name": "ORPHAN ITEM",
|
||||
"normalized_item_name": "ORPHAN ITEM",
|
||||
"upc": "",
|
||||
"line_total": "3.50",
|
||||
"is_fee": "false",
|
||||
"is_discount_line": "false",
|
||||
"is_coupon_line": "false",
|
||||
},
|
||||
{
|
||||
"normalized_item_id": "gnorm_incomplete",
|
||||
"catalog_id": "cat_incomplete",
|
||||
"retailer": "giant",
|
||||
"raw_item_name": "INCOMPLETE ITEM",
|
||||
"normalized_item_name": "INCOMPLETE ITEM",
|
||||
"upc": "",
|
||||
"line_total": "4.50",
|
||||
"is_fee": "false",
|
||||
"is_discount_line": "false",
|
||||
"is_coupon_line": "false",
|
||||
},
|
||||
]
|
||||
link_rows = [
|
||||
{
|
||||
"normalized_item_id": "gnorm_orphan",
|
||||
"catalog_id": "cat_missing",
|
||||
},
|
||||
{
|
||||
"normalized_item_id": "gnorm_incomplete",
|
||||
"catalog_id": "cat_incomplete",
|
||||
},
|
||||
]
|
||||
catalog_rows = [
|
||||
{
|
||||
"catalog_id": "cat_incomplete",
|
||||
"catalog_name": "INCOMPLETE ITEM",
|
||||
"product_type": "",
|
||||
}
|
||||
]
|
||||
|
||||
queue_rows = review_products.build_review_queue(
|
||||
purchase_rows,
|
||||
[],
|
||||
link_rows,
|
||||
catalog_rows,
|
||||
[],
|
||||
)
|
||||
|
||||
reasons = {row["normalized_item_id"]: row["reason_code"] for row in queue_rows}
|
||||
self.assertEqual("orphaned_catalog_link", reasons["gnorm_orphan"])
|
||||
self.assertEqual("incomplete_catalog_link", reasons["gnorm_incomplete"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user