fix(avocet): undo — commit-then-clear order, empty-records guard, skip dedup, stronger test
This commit is contained in:
parent
80a8195899
commit
4a76f6ba41
2 changed files with 22 additions and 15 deletions
34
app/api.py
34
app/api.py
|
|
@ -134,28 +134,32 @@ def delete_undo():
|
||||||
if not _last_action:
|
if not _last_action:
|
||||||
raise HTTPException(404, "No action to undo")
|
raise HTTPException(404, "No action to undo")
|
||||||
action = _last_action
|
action = _last_action
|
||||||
_last_action = None
|
|
||||||
|
|
||||||
item = action["item"] # always the original clean queue item
|
item = action["item"] # always the original clean queue item
|
||||||
|
|
||||||
|
# Perform file operations FIRST — only clear _last_action on success
|
||||||
if action["type"] == "label":
|
if action["type"] == "label":
|
||||||
# Remove last entry from score file
|
|
||||||
records = _read_jsonl(_score_file())
|
records = _read_jsonl(_score_file())
|
||||||
|
if not records:
|
||||||
|
raise HTTPException(409, "Score file is empty — cannot undo label")
|
||||||
_write_jsonl(_score_file(), records[:-1])
|
_write_jsonl(_score_file(), records[:-1])
|
||||||
elif action["type"] == "discard":
|
|
||||||
# Remove last entry from discarded file
|
|
||||||
records = _read_jsonl(_discarded_file())
|
|
||||||
_write_jsonl(_discarded_file(), records[:-1])
|
|
||||||
elif action["type"] == "skip":
|
|
||||||
# Item is at back of queue — move it to front
|
|
||||||
items = _read_jsonl(_queue_file())
|
items = _read_jsonl(_queue_file())
|
||||||
reordered = [item] + [x for x in items if x["id"] != item["id"]]
|
_write_jsonl(_queue_file(), [item] + items)
|
||||||
_write_jsonl(_queue_file(), reordered)
|
elif action["type"] == "discard":
|
||||||
return {"undone": {"type": action["type"], "item": item}}
|
records = _read_jsonl(_discarded_file())
|
||||||
|
if not records:
|
||||||
|
raise HTTPException(409, "Discarded file is empty — cannot undo discard")
|
||||||
|
_write_jsonl(_discarded_file(), records[:-1])
|
||||||
|
items = _read_jsonl(_queue_file())
|
||||||
|
_write_jsonl(_queue_file(), [item] + items)
|
||||||
|
elif action["type"] == "skip":
|
||||||
|
items = _read_jsonl(_queue_file())
|
||||||
|
# Remove the item wherever it sits (guards against duplicate insertion),
|
||||||
|
# then prepend it to the front — restoring it to position 0.
|
||||||
|
items = [item] + [x for x in items if x["id"] != item["id"]]
|
||||||
|
_write_jsonl(_queue_file(), items)
|
||||||
|
|
||||||
# For label and discard: restore item to front of queue
|
# Clear AFTER all file operations succeed
|
||||||
items = _read_jsonl(_queue_file())
|
_last_action = None
|
||||||
_write_jsonl(_queue_file(), [item] + items)
|
|
||||||
return {"undone": {"type": action["type"], "item": item}}
|
return {"undone": {"type": action["type"], "item": item}}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,9 @@ def test_undo_label_removes_from_score(client, queue_with_items):
|
||||||
assert data["undone"]["type"] == "label"
|
assert data["undone"]["type"] == "label"
|
||||||
score = api_module._read_jsonl(api_module._score_file())
|
score = api_module._read_jsonl(api_module._score_file())
|
||||||
assert score == []
|
assert score == []
|
||||||
|
# Item should be restored to front of queue
|
||||||
|
queue = api_module._read_jsonl(api_module._queue_file())
|
||||||
|
assert queue[0]["id"] == "id0"
|
||||||
|
|
||||||
def test_undo_discard_removes_from_discarded(client, queue_with_items):
|
def test_undo_discard_removes_from_discarded(client, queue_with_items):
|
||||||
from app import api as api_module
|
from app import api as api_module
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue