Refine product review prompt flow
This commit is contained in:
@@ -158,16 +158,186 @@ class ReviewWorkflowTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertEqual(0, result.exit_code)
|
||||
self.assertIn("Review observed product (1/1)", result.output)
|
||||
self.assertIn("Resolve this observed product group", result.output)
|
||||
self.assertIn("actions: [l]ink existing [n]ew canonical e[x]clude [s]kip [q]uit", result.output)
|
||||
first_item = result.output.index("2026-03-14 | 7.49")
|
||||
second_item = result.output.index("2026-03-12 | 6.99")
|
||||
self.assertIn("Review 1/1: Resolve observed_product MIXED PEPPER to canonical_name [__]?", result.output)
|
||||
self.assertIn("2 matched items:", result.output)
|
||||
self.assertIn("[l]ink existing [n]ew canonical e[x]clude [s]kip [q]uit:", result.output)
|
||||
first_item = result.output.index("[1] 2026-03-14 | 7.49")
|
||||
second_item = result.output.index("[2] 2026-03-12 | 6.99")
|
||||
self.assertLess(first_item, second_item)
|
||||
self.assertIn("image: https://example.test/mixed-pepper.jpg", result.output)
|
||||
self.assertIn("gcan_mix | MIXED PEPPER (exact normalized name)", result.output)
|
||||
self.assertIn("https://example.test/mixed-pepper.jpg", result.output)
|
||||
self.assertIn("1 canonical suggestions found:", result.output)
|
||||
self.assertIn("[1] MIXED PEPPER", result.output)
|
||||
self.assertIn("\x1b[", result.output)
|
||||
|
||||
def test_review_products_no_suggestions_is_informational(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
purchases_csv = Path(tmpdir) / "purchases.csv"
|
||||
queue_csv = Path(tmpdir) / "review_queue.csv"
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "canonical_catalog.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"observed_product_id",
|
||||
"canonical_product_id",
|
||||
"raw_item_name",
|
||||
"normalized_item_name",
|
||||
"image_url",
|
||||
"upc",
|
||||
"line_total",
|
||||
],
|
||||
)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
{
|
||||
"purchase_date": "2026-03-14",
|
||||
"retailer": "giant",
|
||||
"order_id": "g1",
|
||||
"line_no": "1",
|
||||
"observed_product_id": "gobs_ice",
|
||||
"canonical_product_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)
|
||||
writer.writeheader()
|
||||
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
str(queue_csv),
|
||||
"--resolutions-csv",
|
||||
str(resolutions_csv),
|
||||
"--catalog-csv",
|
||||
str(catalog_csv),
|
||||
],
|
||||
input="q\n",
|
||||
color=True,
|
||||
)
|
||||
|
||||
self.assertEqual(0, result.exit_code)
|
||||
self.assertIn("no canonical_name suggestions found", result.output)
|
||||
|
||||
def test_link_existing_uses_numbered_selection_and_confirmation(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
purchases_csv = Path(tmpdir) / "purchases.csv"
|
||||
queue_csv = Path(tmpdir) / "review_queue.csv"
|
||||
resolutions_csv = Path(tmpdir) / "review_resolutions.csv"
|
||||
catalog_csv = Path(tmpdir) / "canonical_catalog.csv"
|
||||
|
||||
with purchases_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(
|
||||
handle,
|
||||
fieldnames=[
|
||||
"purchase_date",
|
||||
"retailer",
|
||||
"order_id",
|
||||
"line_no",
|
||||
"observed_product_id",
|
||||
"canonical_product_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",
|
||||
"observed_product_id": "gobs_mix",
|
||||
"canonical_product_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",
|
||||
"observed_product_id": "gobs_mix",
|
||||
"canonical_product_id": "",
|
||||
"raw_item_name": "MIXED PEPPER 6-PACK",
|
||||
"normalized_item_name": "MIXED PEPPER",
|
||||
"image_url": "",
|
||||
"upc": "",
|
||||
"line_total": "6.99",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
with catalog_csv.open("w", newline="", encoding="utf-8") as handle:
|
||||
writer = csv.DictWriter(handle, fieldnames=review_products.build_purchases.CATALOG_FIELDS)
|
||||
writer.writeheader()
|
||||
writer.writerow(
|
||||
{
|
||||
"canonical_product_id": "gcan_mix",
|
||||
"canonical_name": "MIXED PEPPER",
|
||||
"category": "",
|
||||
"product_type": "",
|
||||
"brand": "",
|
||||
"variant": "",
|
||||
"size_value": "",
|
||||
"size_unit": "",
|
||||
"pack_qty": "",
|
||||
"measure_type": "",
|
||||
"notes": "",
|
||||
"created_at": "",
|
||||
"updated_at": "",
|
||||
}
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
review_products.main,
|
||||
[
|
||||
"--purchases-csv",
|
||||
str(purchases_csv),
|
||||
"--queue-csv",
|
||||
str(queue_csv),
|
||||
"--resolutions-csv",
|
||||
str(resolutions_csv),
|
||||
"--catalog-csv",
|
||||
str(catalog_csv),
|
||||
"--limit",
|
||||
"1",
|
||||
],
|
||||
input="l\n1\ny\nlinked by test\n",
|
||||
color=True,
|
||||
)
|
||||
|
||||
self.assertEqual(0, result.exit_code)
|
||||
self.assertIn("Select the canonical_name to associate 2 items with:", result.output)
|
||||
self.assertIn('[1] MIXED PEPPER | gcan_mix', result.output)
|
||||
self.assertIn('2 "MIXED PEPPER" items and future matches will be associated with "MIXED PEPPER".', result.output)
|
||||
self.assertIn("actions: [y]es [n]o [b]ack [s]kip [q]uit", result.output)
|
||||
with resolutions_csv.open(newline="", encoding="utf-8") as handle:
|
||||
rows = list(csv.DictReader(handle))
|
||||
self.assertEqual("gcan_mix", rows[0]["canonical_product_id"])
|
||||
self.assertEqual("link", rows[0]["resolution_action"])
|
||||
|
||||
def test_review_products_creates_canonical_and_resolution(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
purchases_csv = Path(tmpdir) / "purchases.csv"
|
||||
|
||||
Reference in New Issue
Block a user