feat: multi-row survivor support in match group review
Replace radio + Merge/Keep Both buttons with per-row checkboxes
and a single Confirm button. Users can now:
- Keep all rows (not duplicates) — check all, confirm
- Merge to one row — uncheck all but one, optionally customize columns
- Split a group — keep some rows, remove others (new capability)
Decision format changed from {action, survivor_idx, overrides} to
{keep_indices, overrides}. apply_review_decisions() updated to handle
all three modes. Batch actions updated accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -202,16 +202,14 @@ if uploaded is not None:
|
||||
def _accept_all():
|
||||
for g in result.match_groups:
|
||||
st.session_state["review_decisions"][g.group_id] = {
|
||||
"action": True,
|
||||
"survivor_idx": g.survivor_index,
|
||||
"keep_indices": [g.survivor_index],
|
||||
"overrides": {},
|
||||
}
|
||||
|
||||
def _reject_all():
|
||||
for g in result.match_groups:
|
||||
st.session_state["review_decisions"][g.group_id] = {
|
||||
"action": False,
|
||||
"survivor_idx": g.survivor_index,
|
||||
"keep_indices": list(g.row_indices),
|
||||
"overrides": {},
|
||||
}
|
||||
|
||||
@@ -234,27 +232,46 @@ if uploaded is not None:
|
||||
# Show decision summary
|
||||
if decisions:
|
||||
st.divider()
|
||||
accepted = sum(
|
||||
1 for v in decisions.values()
|
||||
if isinstance(v, dict) and v.get("action") is True
|
||||
)
|
||||
customized = sum(
|
||||
1 for v in decisions.values()
|
||||
if isinstance(v, dict) and v.get("action") is True
|
||||
and v.get("overrides")
|
||||
)
|
||||
rejected = sum(
|
||||
1 for v in decisions.values()
|
||||
if isinstance(v, dict) and v.get("action") is False
|
||||
)
|
||||
pending = len(result.match_groups) - len(decisions)
|
||||
merged = 0
|
||||
customized = 0
|
||||
split = 0
|
||||
kept_all = 0
|
||||
for v in decisions.values():
|
||||
if not isinstance(v, dict):
|
||||
continue
|
||||
ki = v.get("keep_indices", [])
|
||||
# Find the matching group size
|
||||
gid_for_v = next(
|
||||
(gid for gid, d in decisions.items() if d is v),
|
||||
None,
|
||||
)
|
||||
group_size = next(
|
||||
(len(g.row_indices) for g in result.match_groups
|
||||
if g.group_id == gid_for_v),
|
||||
0,
|
||||
)
|
||||
if len(ki) == group_size:
|
||||
kept_all += 1
|
||||
elif len(ki) == 1:
|
||||
if v.get("overrides"):
|
||||
customized += 1
|
||||
else:
|
||||
merged += 1
|
||||
else:
|
||||
split += 1
|
||||
|
||||
summary_parts = [f"{accepted} merged"]
|
||||
pending = len(result.match_groups) - len(decisions)
|
||||
parts = []
|
||||
if merged:
|
||||
parts.append(f"{merged} merged")
|
||||
if customized:
|
||||
summary_parts.append(f"{customized} customized")
|
||||
summary_parts.append(f"{rejected} kept both")
|
||||
summary_parts.append(f"{pending} pending")
|
||||
st.caption("Decisions: " + ", ".join(summary_parts))
|
||||
parts.append(f"{customized} customized")
|
||||
if split:
|
||||
parts.append(f"{split} split")
|
||||
if kept_all:
|
||||
parts.append(f"{kept_all} kept all")
|
||||
parts.append(f"{pending} pending")
|
||||
st.caption("Decisions: " + ", ".join(parts))
|
||||
|
||||
# Apply decisions and offer download
|
||||
if st.button(
|
||||
|
||||
Reference in New Issue
Block a user