Refine product review prompt flow

This commit is contained in:
ben
2026-03-17 13:25:12 -04:00
parent 7b8141cd42
commit d39497c298
3 changed files with 288 additions and 53 deletions

View File

@@ -177,10 +177,11 @@ def build_canonical_suggestions(related_rows, catalog_rows, limit=3):
def build_display_lines(queue_row, related_rows):
lines = []
for row in sort_related_items(related_rows):
for index, row in enumerate(sort_related_items(related_rows), start=1):
lines.append(
" - {purchase_date} | {line_total} | {raw_item_name} | {normalized_item_name} | "
" [{index}] {purchase_date} | {line_total} | {raw_item_name} | {normalized_item_name} | "
"{upc} | {retailer}".format(
index=index,
purchase_date=row.get("purchase_date", ""),
line_total=row.get("line_total", ""),
raw_item_name=row.get("raw_item_name", ""),
@@ -190,47 +191,81 @@ def build_display_lines(queue_row, related_rows):
)
)
if row.get("image_url"):
lines.append(f" image: {row['image_url']}")
lines.append(f" {row['image_url']}")
if not lines:
lines.append(" - no matched item rows found")
lines.append(" [1] no matched item rows found")
return lines
def observed_name(queue_row, related_rows):
if queue_row.get("normalized_names"):
return queue_row["normalized_names"].split(" | ")[0]
for row in related_rows:
if row.get("normalized_item_name"):
return row["normalized_item_name"]
return queue_row.get("observed_product_id", "")
def choose_existing_canonical(display_rows, observed_label, matched_count):
click.secho(
f"Select the canonical_name to associate {matched_count} items with:",
fg=INFO_COLOR,
)
for index, row in enumerate(display_rows, start=1):
click.echo(f" [{index}] {row['canonical_name']} | {row['canonical_product_id']}")
choice = click.prompt(
click.style("selection", fg=PROMPT_COLOR),
type=click.IntRange(1, len(display_rows)),
)
chosen_row = display_rows[choice - 1]
click.echo(
f'{matched_count} "{observed_label}" items and future matches will be associated '
f'with "{chosen_row["canonical_name"]}".'
)
click.secho(
"actions: [y]es [n]o [b]ack [s]kip [q]uit",
fg=PROMPT_COLOR,
)
confirm = click.prompt(
click.style("confirm", fg=PROMPT_COLOR),
type=click.Choice(["y", "n", "b", "s", "q"]),
)
if confirm == "y":
return chosen_row["canonical_product_id"], ""
if confirm == "s":
return "", "skip"
if confirm == "q":
return "", "quit"
return "", "back"
def prompt_resolution(queue_row, related_rows, catalog_rows, queue_index, queue_total):
suggestions = build_canonical_suggestions(related_rows, catalog_rows)
observed_label = observed_name(queue_row, related_rows)
matched_count = len(related_rows)
click.echo("")
click.secho(
f"Review observed product ({queue_index}/{queue_total})",
f"Review {queue_index}/{queue_total}: Resolve observed_product {observed_label} "
"to canonical_name [__]?",
fg=INFO_COLOR,
)
click.echo(
"Resolve this observed product group to an existing canonical_name, "
"a new canonical_name, exclude it, or skip it."
)
click.echo(f"observed_product_id: {queue_row['observed_product_id']}")
click.echo(f"retailer: {queue_row['retailer']}")
click.echo(f"observed_name(s): {queue_row['normalized_names']}")
click.echo(f"upc(s): {queue_row['upc_values']}")
click.echo(f"seen_count: {queue_row['seen_count']}")
click.secho("matched items:", fg=INFO_COLOR)
click.echo(f"{matched_count} matched items:")
for line in build_display_lines(queue_row, related_rows):
click.echo(line)
if suggestions:
click.secho("suggested canonical_name values:", fg=INFO_COLOR)
for suggestion in suggestions:
click.echo(
f" - {suggestion['canonical_product_id']} | {suggestion['canonical_name']} "
f"({suggestion['reason']})"
)
click.echo(f"{len(suggestions)} canonical suggestions found:")
for index, suggestion in enumerate(suggestions, start=1):
click.echo(f" [{index}] {suggestion['canonical_name']}")
else:
click.secho("no deterministic canonical suggestions found", fg=WARNING_COLOR)
click.echo("no canonical_name suggestions found")
click.secho(
"actions: [l]ink existing [n]ew canonical e[x]clude [s]kip [q]uit",
"[l]ink existing [n]ew canonical e[x]clude [s]kip [q]uit:",
fg=PROMPT_COLOR,
)
action = click.prompt(
click.style("action", fg=PROMPT_COLOR),
"",
type=click.Choice(["l", "n", "x", "s", "q"]),
prompt_suffix=" ",
)
if action == "q":
return None, None
@@ -266,20 +301,27 @@ def prompt_resolution(queue_row, related_rows, catalog_rows, queue_index, queue_
}
for row in catalog_rows[:10]
]
click.secho("existing canonical_name values:", fg=INFO_COLOR)
for row in display_rows:
click.echo(
f" - {row['canonical_product_id']} | {row['canonical_name']} ({row['reason']})"
while True:
canonical_product_id, outcome = choose_existing_canonical(
display_rows,
observed_label,
matched_count,
)
canonical_product_id = click.prompt(
click.style("canonical product id", fg=PROMPT_COLOR),
type=str,
)
notes = click.prompt(
click.style("link notes", fg=PROMPT_COLOR),
default="",
show_default=False,
)
if outcome == "skip":
return {
"observed_product_id": queue_row["observed_product_id"],
"canonical_product_id": "",
"resolution_action": "skip",
"status": "pending",
"resolution_notes": queue_row.get("resolution_notes", ""),
"reviewed_at": str(date.today()),
}, None
if outcome == "quit":
return None, None
if outcome == "back":
continue
break
notes = click.prompt(click.style("link notes", fg=PROMPT_COLOR), default="", show_default=False)
return {
"observed_product_id": queue_row["observed_product_id"],
"canonical_product_id": canonical_product_id,