[NRT-591] Surface duplicate anki_id suggestion error + redesign bulk suggestion summary#1306
Draft
RisingOrange wants to merge 4 commits into
Draft
[NRT-591] Surface duplicate anki_id suggestion error + redesign bulk suggestion summary#1306RisingOrange wants to merge 4 commits into
RisingOrange wants to merge 4 commits into
Conversation
…summary Single suggestion: when a new-note suggestion is rejected because the note already exists in the deck on AnkiHub, show a 'Note already exists in this deck' dialog offering to resubmit the edits as a change suggestion (converting the already-built NewNoteSuggestion, no remote fetch / AnkiHub-DB write). Soft-deleted conflicts route to the existing deleted-on-AnkiHub handling. Bulk suggestion: replace the plain-text showText summary with a real dialog shown for all outcomes - categorized results, a new 'Other errors' bucket with a support link, and an interactive 'Notes already in this deck' action (batched resubmit / ignore) with Close gating and 'Updated' badges. Degrades gracefully when the backend doesn't return the conflicting id. Adds analytics events (error_dialog_shown, resubmit_clicked, bulk_error_category_shown, bulk_resubmit_clicked) and tests.
- Gate X/Escape (reject()/closeEvent()) on the bulk summary dialog so an unresolved action or in-flight resubmit can't be dismissed - previously only the Close button was disabled. Adds a regression test. - Replace the stringly-typed action state + separate hard-error flag with a single _ActionState enum (a hard resubmit failure is now FAILED: retry buttons stay, Close works). - Reuse gui.utils.clear_layout instead of a local duplicate; add missing type annotations.
…icate-anki-id # Conflicts: # ankihub/gui/suggestion_dialog.py # ankihub/main/suggestions.py
- Move the bulk summary dialog, its action-state machine, categories, color helpers, and the bulk-submit callback to gui/bulk_suggestion_summary_dialog.py. - Move the shared panel color helpers to gui/utils.py (avoids a circular import between the two dialog modules). - Drop the change_type plumbing from the bulk callback/dialog - converted resubmits are always 'Updated content', matching the single-note path. - Flatten the nested resubmit task/on_done callbacks in _show_note_already_exists_dialog. - Rename module constants to the no-underscore convention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issues
Proposed changes
When a user submits a new-note suggestion for a note whose
anki_idalready exists in the deck on AnkiHub (note created + auto-accepted, then edited and re-suggested before a sync), the server rejects it with"A deck can't contain multiple notes with the same anki_id.". Previously this failure was effectively hidden (single → rawshowInfo; bulk → an uncategorized blob in the plain-text summary). This PR surfaces it and lets the user recover.Detection (
main/suggestions.py)ANKIHUB_DUPLICATE_ANKI_ID_ERRORconstant +parse_duplicate_anki_id_error()— tolerates the backend's DRF-normalized payload (list-wrapped/stringified: readsconflicting_ankihub_id[0], comparesconflicting_note_deleted[0] == "True", not truthiness)._new_note_to_change_suggestion()converts the already-builtNewNoteSuggestioninto aChangeNoteSuggestionkeyed by the conflictingah_nidand lets the server diff — no remote fetch, no AnkiHub-DB write.resubmit_new_note_as_change_suggestion()(single) andresubmit_new_notes_as_change_suggestions_in_bulk()(one batched call).suggest_notes_in_bulknow returnsalready_in_deck_by_nidon the result; soft-deleted conflicts are routed to the existing deleted-on-AnkiHub category instead (a change suggestion would 404 a tombstoned note).Scenario 1 — single suggestion (
gui/suggestion_dialog.py)NoteAlreadyExistsDialog(info icon, Cancel / Send as change suggestion). The suggestion dialog closes and the modal shows over the editor; on success → toast; soft-deleted conflict → existing "deleted from AnkiHub" dialog; missing conflicting id (older server) → falls back to today's generic error.Scenario 2 — bulk suggestion summary (
gui/suggestion_dialog.py)showTextsummary with a realBulkSuggestionSummaryDialog, shown for all outcomes (full redesign): colored counts, bold(N)category titles, a new "Other errors" bucket with acommunity.ankihub.net/c/supportlink, and the redundant "All notes with failed suggestions" block removed.create_suggestions_in_bulkcall) / Ignore;Close/Xdisabled until resolved; "Updated" badges on changed categories; a hard resubmit failure re-enablesClose(no dead-end).Analytics (structlog → Datadog):
error_dialog_shown,resubmit_clicked,bulk_error_category_shown,bulk_resubmit_clicked.How to reproduce
As a maintainer on staging:
Tests:
pytest tests/addon -k "BulkSuggestionSummaryDialog or MaybeHandleNoteAlreadyExists or ParseDuplicate or NewNoteToChange or OnSuggestNotesInBulkDone or already_in_deck".Screenshots and videos
All dialog states (single modal, bulk default / loading / success / ignored) were verified live against the real add-on code. Design reference: the Figma + mockups on NRT-591. (Can add inline screenshots on request.)
Further comments
first 25, … and N more). A "Copy note IDs" button (copying an Anki Browsenid:…search) is proposed and pending designer confirmation.main, not stacked on the auto-protect flag cleanup ([NRT-771] Post-flag-flip cleanup: drop AUTO_PROTECT_FEATURE_FLAG scaffolding + thread NoteDiff #1305) which touches the same two files; the overlap is mechanical, not architectural.