[NRT-771] Post-flag-flip cleanup: drop AUTO_PROTECT_FEATURE_FLAG scaffolding + thread NoteDiff#1305
Draft
RisingOrange wants to merge 5 commits into
Draft
[NRT-771] Post-flag-flip cleanup: drop AUTO_PROTECT_FEATURE_FLAG scaffolding + thread NoteDiff#1305RisingOrange wants to merge 5 commits into
RisingOrange wants to merge 5 commits into
Conversation
…read NoteDiff The auto_protect_fields_when_edited rollout is permanent, so remove the feature-flag scaffolding added by NRT-749: - Delete AUTO_PROTECT_FEATURE_FLAG and inline its 4 gate sites (editor auto-protect hook, bulk empty-state gate, dialog widget gate, deck-options auto-protect box). include_protected_fields is now unconditionally True. - Make BulkSuggestionFilters.fields_to_include_by_mid required (drop the legacy None "no-filter" fallthrough in for_mid); the dialog widget always renders and produces a concrete per-mid selection now. - Thread the open-time NoteDiff map through suggest_notes_in_bulk -> _suggestions_for_notes so the submit path reuses cur/ah_note instead of re-deriving them, and drop the duplicate AnkiHubNote.filter batch query. Tests updated for the simplified contracts: removed the flag-off legacy tests, gave bulk-path tests explicit field allowlists, and updated the shared suggestion-dialog mock to mirror the widget's default selection.
… cleanup - Fix: `_new_note_suggestion` now drops empty fields before applying the allowlist. With `diff.cur` (include_empty_fields=True) and the widget's per-mid aggregated selection, a field empty on one new note but allowlisted via a sibling note of the same type would otherwise ship as an empty field; the pre-threading path excluded it (include_empty_fields=False). Adds a regression test. - Extract the duplicated "build per-mid allowlist from diffs" loop into a shared `bulk_filters_from_diffs` test helper. - Reword the now-misleading "legacy" docstrings/comments around the PerNoteFilters None path and `for_mid`.
Threading NoteDiff only reused the conversions (local_note/ah_note); the builders still re-ran _fields_that_changed / _added_and_removed_tags. Finish the job: - NoteDiff now carries `changed_fields: List[Field]` (the fields a suggestion would ship — changed-vs-AH for existing notes, non-empty for new notes); `edited_fields` becomes a derived property over it. - `_change_note_suggestion` / `_new_note_suggestion` consume the diff directly (apply the user's allowlist to `changed_fields` / `added_tags` / `removed_tags`) instead of recomputing it. `_fields_that_changed` / `_added_and_removed_tags` now live only in `compute_note_diffs`. - The new-note empty-field guard moves into `compute_note_diffs`, so the rule holds for every consumer of the diff in one place. - Drop the per-builder `diff-or-to_note_data` branching: a single line ensures a diff exists (self-computed for callers that don't supply one), then the body reads it uniformly. Also: rename `NoteDiff.cur` -> `local_note` (pairs with `ah_note`), and rewrite the added comments to describe the code as it stands rather than narrate the PR.
…iff, polish
- Make `filters` required on `suggest_notes_in_bulk` / `_suggestions_for_notes`
(drop the silent `filters or {}` default). An empty/absent per-mid map ships
no fields for that note type; there's no implicit "ship everything" in the
bulk path, so a caller omitting filters now fails fast instead of silently
shipping nothing. Fix the `BulkSuggestionFilters` docstring to say so.
- Thread the open-time `NoteDiff` through the single-suggestion path too
(`suggest_note_update` / `suggest_new_note` gain an optional `diff`, fed from
the dialog's open-time map), mirroring the bulk path so submit no longer
recomputes the diff. Self-computed when omitted.
- Rename the `NoteDiff.edited_fields` property to `changed_field_names`
(pairs with `changed_fields`; the `_names` suffix makes the List[str] honest).
- Comment/docstring cleanup: trim the `compute_note_diffs` docstring (was
leaking `to_note_data` internals) and the `changed_fields` field comment;
drop change-narrating phrasing.
…all)
Reconsidered the "make filters required" call from the prior review pass.
`suggest_notes_in_bulk(filters=None)` now means "no filter — ship everything
each note's diff detected", matching the single-note path's `filters=None`
and giving an ergonomic "just suggest these notes" call. This is distinct
from passing an explicit `BulkSuggestionFilters` with a `{}`/partial map,
where a missing per-mid entry still ships no fields for that note type (the
dialog's explicit-selection semantics, unchanged). The dialog always passes
an explicit filter, so None is purely a programmatic convenience.
Restored the natural no-filter form in the basic bulk tests so the
None = ship-all path is covered.
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
notes/nrt749-deferred-refactors.md(feat: authorize add on #4).Proposed changes
auto_protect_fields_when_editedis now permanent for all users, so this removes the feature-flag scaffolding NRT-749 added. It also lands a related performance cleanup that was deliberately deferred until the flag was gone — reusing the per-note diff computed when the suggestion dialog opens instead of recomputing it on submit (see §3 for why the flag blocked it).1. Remove
AUTO_PROTECT_FEATURE_FLAGscaffoldingconfigimport inmain/suggestions.py.editor.py— auto-protect-on-unfocus hook (drops the early-return flag check).suggestion_dialog.py— the bulk empty-state gate and the_setup_uifield-selection-widget gate.decks_dialog.py— the "Automatically protect fields when edited" box is always shown.include_protected_fields=Trueunconditionally.2. Tighten
BulkSuggestionFiltersfields_to_include_by_midis now required (dropsOptional);for_midno longer has aNone"ship-everything" branch. The dialog's widget always renders and produces a concrete per-mid selection, soNonewas unreachable legacy. Empty-construction sites pass{}.3. Reuse the note-diff on submit instead of recomputing it ("thread
NoteDiffto the submit path")Plain version: When you edit a note and open the suggestion dialog, the add-on computes a
NoteDiffper note — what changed versus the version AnkiHub has on record (the changed fields, added/removed tags, new media). That diff is what powers the "Include in suggestion" picker. Previously it was thrown away once the dialog opened: when you hit submit, the builders re-derived the exact same information from scratch — re-converting each note, re-reading the AnkiHub DB, re-comparing fields and tags — plus an extra batch query. This change threads (passes along) that already-computed diff from the dialog through to the submit path, and the submit builders now just apply your selection on top of the diff instead of recomputing it: the diff is computed once and is the single source of truth, and a duplicateAnkiHubNote.filter(...)query is dropped.Why it was deferred: while the flag existed, the open-time conversion always included protected fields but the submit-time conversion only did so when the flag was on — so reusing the open-time diff could have shipped the wrong content. With the flag gone, both sides behave identically, so reuse is now safe. (Hence folding it into this ticket rather than NRT-749.)
Details:
NoteDiffcarrieslocal_note: NoteInfo(the local Anki note, paired withah_note) andchanged_fields: List[Field]— the fields a suggestion would ship (changed-vs-AH for existing notes, non-empty for new notes);changed_field_namesis a derived property over it. Both the bulk and single-suggestion dialogs thread their open-timecompute_note_diffsmap to the submit path:_suggestions_for_notesclassifies notes from the diff (exists_in_ah_db/is_deleted_on_remote, dropping the duplicateAnkiHubNote.filter(anki_note_id__in=...)batch query) and hands each note's diff to_change_note_suggestion/_new_note_suggestion. Those builders just apply the user's allowlist tochanged_fields/added_tags/removed_tags— the field/tag comparison (_fields_that_changed,_added_and_removed_tags) lives only incompute_note_diffs. A builder self-computes the diff when one isn't supplied (standalone callers / tests).BulkSuggestionFilters.fields_to_include_by_midis required, and within an explicit filter a missing per-mid entry ships none of that note type's fields (the dialog's per-section selection). Passing no filter at all (filters=None) means "no filter — ship everything the diff detected", matching the single-note path; the dialog always passes an explicit selection, soNoneis a programmatic convenience.Tests
set_feature_flags({"auto_protect_fields_when_edited": ...})enabling lines (the per-deckset_auto_protect_fields_when_editedsetting stays).fields_to_include_by_mid(the oldNone"ship-everything" default now ships nothing), and updated the sharedmock_suggestion_dialogfixture to build its allowlist from the diffs — mirroring the real widget's default-all-checked selection.How to reproduce
N/a — internal refactor, no user-facing behavior change (given the flag is on for everyone). Covered by tests.
Further comments
Deliberately left
PerNoteFilters/suggest_note_update/suggest_new_notefilters=None"ship-everything" default in place: unlike theBulkSuggestionFiltersmap (only ever populated by the dialog widget), that's a sensible default for the general-purpose single-note suggest entry points, not flag residue.