diff --git a/proto/anki/backend.proto b/proto/anki/backend.proto index 36a0cf79a..4d94e3481 100644 --- a/proto/anki/backend.proto +++ b/proto/anki/backend.proto @@ -7,34 +7,10 @@ package anki.backend; import "anki/generic.proto"; import "anki/cards.proto"; +import "anki/decks.proto"; import "anki/collection.proto"; - -// IDs used in RPC calls -/////////////////////////////////////////////////////////// - -message NotetypeId { - int64 ntid = 1; -} - -message NoteId { - int64 nid = 1; -} - -message NoteIds { - repeated int64 note_ids = 1; -} - -message DeckId { - int64 did = 1; -} - -message DeckIds { - repeated int64 dids = 1; -} - -message DeckConfigId { - int64 dcid = 1; -} +import "anki/notes.proto"; +import "anki/notetypes.proto"; // Backend methods /////////////////////////////////////////////////////////// @@ -66,15 +42,15 @@ service SchedulingService { rpc StudiedTodayMessage(StudiedTodayMessageRequest) returns (generic.String); rpc UpdateStats(UpdateStatsRequest) returns (generic.Empty); rpc ExtendLimits(ExtendLimitsRequest) returns (generic.Empty); - rpc CountsForDeckToday(DeckId) returns (CountsForDeckTodayResponse); + rpc CountsForDeckToday(decks.DeckId) returns (CountsForDeckTodayResponse); rpc CongratsInfo(generic.Empty) returns (CongratsInfoResponse); rpc RestoreBuriedAndSuspendedCards(cards.CardIds) returns (collection.OpChanges); rpc UnburyDeck(UnburyDeckRequest) returns (collection.OpChanges); rpc BuryOrSuspendCards(BuryOrSuspendCardsRequest) returns (collection.OpChangesWithCount); - rpc EmptyFilteredDeck(DeckId) returns (collection.OpChanges); - rpc RebuildFilteredDeck(DeckId) returns (collection.OpChangesWithCount); + rpc EmptyFilteredDeck(decks.DeckId) returns (collection.OpChanges); + rpc RebuildFilteredDeck(decks.DeckId) returns (collection.OpChangesWithCount); rpc ScheduleCardsAsNew(ScheduleCardsAsNewRequest) returns (collection.OpChanges); rpc SetDueDate(SetDueDateRequest) returns (collection.OpChanges); @@ -88,49 +64,6 @@ service SchedulingService { rpc GetQueuedCards(GetQueuedCardsRequest) returns (QueuedCards); } -service DecksService { - rpc AddDeckLegacy(generic.Json) returns (collection.OpChangesWithId); - rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyRequest) returns (DeckId); - rpc DeckTree(DeckTreeRequest) returns (DeckTreeNode); - rpc DeckTreeLegacy(generic.Empty) returns (generic.Json); - rpc GetAllDecksLegacy(generic.Empty) returns (generic.Json); - rpc GetDeckIdByName(generic.String) returns (DeckId); - rpc GetDeck(DeckId) returns (Deck); - rpc UpdateDeck(Deck) returns (collection.OpChanges); - rpc UpdateDeckLegacy(generic.Json) returns (collection.OpChanges); - rpc SetDeckCollapsed(SetDeckCollapsedRequest) returns (collection.OpChanges); - rpc GetDeckLegacy(DeckId) returns (generic.Json); - rpc GetDeckNames(GetDeckNamesRequest) returns (DeckNames); - rpc NewDeckLegacy(generic.Bool) returns (generic.Json); - rpc RemoveDecks(DeckIds) returns (collection.OpChangesWithCount); - rpc ReparentDecks(ReparentDecksRequest) - returns (collection.OpChangesWithCount); - rpc RenameDeck(RenameDeckRequest) returns (collection.OpChanges); - rpc GetOrCreateFilteredDeck(DeckId) returns (FilteredDeckForUpdate); - rpc AddOrUpdateFilteredDeck(FilteredDeckForUpdate) - returns (collection.OpChangesWithId); - rpc FilteredDeckOrderLabels(generic.Empty) returns (generic.StringList); - rpc SetCurrentDeck(DeckId) returns (collection.OpChanges); - rpc GetCurrentDeck(generic.Empty) returns (Deck); -} - -service NotesService { - rpc NewNote(NotetypeId) returns (Note); - rpc AddNote(AddNoteRequest) returns (AddNoteResponse); - rpc DefaultsForAdding(DefaultsForAddingRequest) returns (DeckAndNotetype); - rpc DefaultDeckForNotetype(NotetypeId) returns (DeckId); - rpc UpdateNote(UpdateNoteRequest) returns (collection.OpChanges); - rpc GetNote(NoteId) returns (Note); - rpc RemoveNotes(RemoveNotesRequest) returns (collection.OpChangesWithCount); - rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteResponse); - rpc AfterNoteUpdates(AfterNoteUpdatesRequest) - returns (collection.OpChangesWithCount); - rpc FieldNamesForNotes(FieldNamesForNotesRequest) - returns (FieldNamesForNotesResponse); - rpc NoteFieldsCheck(Note) returns (NoteFieldsCheckResponse); - rpc CardsOfNote(NoteId) returns (cards.CardIds); -} - service SyncService { rpc SyncMedia(SyncAuth) returns (generic.Empty); rpc AbortSync(generic.Empty) returns (generic.Empty); @@ -158,28 +91,6 @@ service ConfigService { rpc SetPreferences(Preferences) returns (collection.OpChanges); } -service NotetypesService { - rpc AddNotetype(Notetype) returns (collection.OpChangesWithId); - rpc UpdateNotetype(Notetype) returns (collection.OpChanges); - rpc AddNotetypeLegacy(generic.Json) returns (collection.OpChangesWithId); - rpc UpdateNotetypeLegacy(generic.Json) returns (collection.OpChanges); - rpc AddOrUpdateNotetype(AddOrUpdateNotetypeRequest) returns (NotetypeId); - rpc GetStockNotetypeLegacy(StockNotetype) returns (generic.Json); - rpc GetNotetype(NotetypeId) returns (Notetype); - rpc GetNotetypeLegacy(NotetypeId) returns (generic.Json); - rpc GetNotetypeNames(generic.Empty) returns (NotetypeNames); - rpc GetNotetypeNamesAndCounts(generic.Empty) returns (NotetypeUseCounts); - rpc GetNotetypeIdByName(generic.String) returns (NotetypeId); - rpc RemoveNotetype(NotetypeId) returns (collection.OpChanges); - rpc GetAuxNotetypeConfigKey(GetAuxConfigKeyRequest) returns (generic.String); - rpc GetAuxTemplateConfigKey(GetAuxTemplateConfigKeyRequest) - returns (generic.String); - rpc GetSingleNotetypeOfNotes(NoteIds) returns (NotetypeId); - rpc GetChangeNotetypeInfo(GetChangeNotetypeInfoRequest) - returns (ChangeNotetypeInfo); - rpc ChangeNotetype(ChangeNotetypeRequest) returns (collection.OpChanges); -} - service CardRenderingService { rpc ExtractAVTags(ExtractAVTagsRequest) returns (ExtractAVTagsResponse); rpc ExtractLatex(ExtractLatexRequest) returns (ExtractLatexResponse); @@ -194,18 +105,6 @@ service CardRenderingService { rpc RenderMarkdown(RenderMarkdownRequest) returns (generic.String); } -service DeckConfigService { - rpc AddOrUpdateDeckConfigLegacy(generic.Json) returns (DeckConfigId); - rpc GetDeckConfig(DeckConfigId) returns (DeckConfig); - rpc AllDeckConfigLegacy(generic.Empty) returns (generic.Json); - rpc GetDeckConfigLegacy(DeckConfigId) returns (generic.Json); - rpc NewDeckConfigLegacy(generic.Empty) returns (generic.Json); - rpc RemoveDeckConfig(DeckConfigId) returns (generic.Empty); - rpc GetDeckConfigsForUpdate(DeckId) returns (DeckConfigsForUpdate); - rpc UpdateDeckConfigs(UpdateDeckConfigsRequest) - returns (collection.OpChanges); -} - service TagsService { rpc ClearUnusedTags(generic.Empty) returns (collection.OpChangesWithCount); rpc AllTags(generic.Empty) returns (generic.StringList); @@ -250,257 +149,6 @@ service MediaService { rpc RestoreTrash(generic.Empty) returns (generic.Empty); } -// Protobuf stored in .anki2 files -// These should be moved to a separate file in the future -/////////////////////////////////////////////////////////// - -message DeckConfig { - message Config { - enum NewCardInsertOrder { - NEW_CARD_INSERT_ORDER_DUE = 0; - NEW_CARD_INSERT_ORDER_RANDOM = 1; - } - enum NewCardGatherPriority { - NEW_CARD_GATHER_PRIORITY_DECK = 0; - NEW_CARD_GATHER_PRIORITY_LOWEST_POSITION = 1; - NEW_CARD_GATHER_PRIORITY_HIGHEST_POSITION = 2; - } - enum NewCardSortOrder { - NEW_CARD_SORT_ORDER_TEMPLATE_THEN_LOWEST_POSITION = 0; - NEW_CARD_SORT_ORDER_TEMPLATE_THEN_HIGHEST_POSITION = 1; - NEW_CARD_SORT_ORDER_TEMPLATE_THEN_RANDOM = 2; - NEW_CARD_SORT_ORDER_LOWEST_POSITION = 3; - NEW_CARD_SORT_ORDER_HIGHEST_POSITION = 4; - NEW_CARD_SORT_ORDER_RANDOM = 5; - } - enum ReviewCardOrder { - REVIEW_CARD_ORDER_DAY = 0; - REVIEW_CARD_ORDER_DAY_THEN_DECK = 1; - REVIEW_CARD_ORDER_DECK_THEN_DAY = 2; - REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 3; - REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 4; - // REVIEW_CARD_ORDER_RELATIVE_OVERDUE = 3; - } - enum ReviewMix { - REVIEW_MIX_MIX_WITH_REVIEWS = 0; - REVIEW_MIX_AFTER_REVIEWS = 1; - REVIEW_MIX_BEFORE_REVIEWS = 2; - } - enum LeechAction { - LEECH_ACTION_SUSPEND = 0; - LEECH_ACTION_TAG_ONLY = 1; - } - - repeated float learn_steps = 1; - repeated float relearn_steps = 2; - - reserved 3 to 8; - - uint32 new_per_day = 9; - uint32 reviews_per_day = 10; - uint32 new_per_day_minimum = 29; - - float initial_ease = 11; - float easy_multiplier = 12; - float hard_multiplier = 13; - float lapse_multiplier = 14; - float interval_multiplier = 15; - - uint32 maximum_review_interval = 16; - uint32 minimum_lapse_interval = 17; - - uint32 graduating_interval_good = 18; - uint32 graduating_interval_easy = 19; - - NewCardInsertOrder new_card_insert_order = 20; - NewCardGatherPriority new_card_gather_priority = 34; - NewCardSortOrder new_card_sort_order = 32; - ReviewMix new_mix = 30; - - ReviewCardOrder review_order = 33; - - ReviewMix interday_learning_mix = 31; - - LeechAction leech_action = 21; - uint32 leech_threshold = 22; - - bool disable_autoplay = 23; - uint32 cap_answer_time_to_secs = 24; - bool show_timer = 25; - bool skip_question_when_replaying_answer = 26; - - bool bury_new = 27; - bool bury_reviews = 28; - - bytes other = 255; - } - - int64 id = 1; - string name = 2; - int64 mtime_secs = 3; - int32 usn = 4; - Config config = 5; -} - -message Deck { - message Common { - bool study_collapsed = 1; - bool browser_collapsed = 2; - - uint32 last_day_studied = 3; - int32 new_studied = 4; - int32 review_studied = 5; - int32 milliseconds_studied = 7; - - // previously set in the v1 scheduler, - // but not currently used for anything - int32 learning_studied = 6; - - reserved 8 to 13; - - bytes other = 255; - } - message Normal { - int64 config_id = 1; - uint32 extend_new = 2; - uint32 extend_review = 3; - string description = 4; - bool markdown_description = 5; - - reserved 6 to 11; - } - message Filtered { - message SearchTerm { - enum Order { - OLDEST_REVIEWED_FIRST = 0; - RANDOM = 1; - INTERVALS_ASCENDING = 2; - INTERVALS_DESCENDING = 3; - LAPSES = 4; - ADDED = 5; - DUE = 6; - REVERSE_ADDED = 7; - DUE_PRIORITY = 8; - } - - string search = 1; - uint32 limit = 2; - Order order = 3; - } - - bool reschedule = 1; - repeated SearchTerm search_terms = 2; - // v1 scheduler only - repeated float delays = 3; - // v2 scheduler only - uint32 preview_delay = 4; - } - // a container to store the deck specifics in the DB - // as a tagged enum - message KindContainer { - oneof kind { - Normal normal = 1; - Filtered filtered = 2; - } - } - - int64 id = 1; - string name = 2; - int64 mtime_secs = 3; - int32 usn = 4; - Common common = 5; - // the specifics are inlined here when sending data to clients, - // as otherwise an extra level of indirection would be required - oneof kind { - Normal normal = 6; - Filtered filtered = 7; - } -} - -message Notetype { - message Config { - enum Kind { - KIND_NORMAL = 0; - KIND_CLOZE = 1; - } - message CardRequirement { - enum Kind { - KIND_NONE = 0; - KIND_ANY = 1; - KIND_ALL = 2; - } - uint32 card_ord = 1; - Kind kind = 2; - repeated uint32 field_ords = 3; - } - - Kind kind = 1; - uint32 sort_field_idx = 2; - string css = 3; - /// This is now stored separately; retrieve with DefaultsForAdding() - int64 target_deck_id_unused = 4; - string latex_pre = 5; - string latex_post = 6; - bool latex_svg = 7; - repeated CardRequirement reqs = 8; - - bytes other = 255; - } - message Field { - message Config { - bool sticky = 1; - bool rtl = 2; - string font_name = 3; - uint32 font_size = 4; - - bytes other = 255; - } - generic.OptionalUInt32 ord = 1; - string name = 2; - Config config = 5; - } - message Template { - message Config { - string q_format = 1; - string a_format = 2; - string q_format_browser = 3; - string a_format_browser = 4; - int64 target_deck_id = 5; - string browser_font_name = 6; - uint32 browser_font_size = 7; - - bytes other = 255; - } - - generic.OptionalUInt32 ord = 1; - string name = 2; - int64 mtime_secs = 3; - sint32 usn = 4; - Config config = 5; - } - - int64 id = 1; - string name = 2; - int64 mtime_secs = 3; - sint32 usn = 4; - Config config = 7; - repeated Field fields = 8; - repeated Template templates = 9; -} - -// Database objects -/////////////////////////////////////////////////////////// - -message Note { - int64 id = 1; - string guid = 2; - int64 notetype_id = 3; - uint32 mtime_secs = 4; - int32 usn = 5; - repeated string tags = 6; - repeated string fields = 7; -} - // Backend /////////////////////////////////////////////////////////// @@ -551,43 +199,20 @@ message SchedTimingTodayResponse { int64 next_day_at = 2; } -message DeckTreeRequest { - // if non-zero, counts for the provided timestamp will be included - int64 now = 1; - int64 top_deck_id = 2; -} - -message DeckTreeNode { - int64 deck_id = 1; - string name = 2; - uint32 level = 4; - bool collapsed = 5; - - uint32 review_count = 6; - uint32 learn_count = 7; - uint32 new_count = 8; - - bool filtered = 16; - - // low index so key can be packed into a byte, but at bottom - // to make debug output easier to read - repeated DeckTreeNode children = 3; -} - message RenderExistingCardRequest { int64 card_id = 1; bool browser = 2; } message RenderUncommittedCardRequest { - Note note = 1; + notes.Note note = 1; uint32 card_ord = 2; - Notetype.Template template = 3; + notetypes.Notetype.Template template = 3; bool fill_empty = 4; } message RenderUncommittedCardLegacyRequest { - Note note = 1; + notes.Note note = 1; uint32 card_ord = 2; bytes template = 3; bool fill_empty = 4; @@ -782,53 +407,11 @@ message ReplaceSearchNodeRequest { SearchNode replacement_node = 2; } -message DeckConfigsForUpdate { - message ConfigWithExtra { - DeckConfig config = 1; - uint32 use_count = 2; - } - message CurrentDeck { - string name = 1; - int64 config_id = 2; - repeated int64 parent_config_ids = 3; - } - - repeated ConfigWithExtra all_config = 1; - CurrentDeck current_deck = 2; - DeckConfig defaults = 3; - bool schema_modified = 4; - bool v3_scheduler = 5; - bool have_addons = 6; - // only applies to v3 scheduler - string card_state_customizer = 7; -} - -message UpdateDeckConfigsRequest { - int64 target_deck_id = 1; - /// Unchanged, non-selected configs can be omitted. Deck will - /// be set to whichever entry comes last. - repeated DeckConfig configs = 2; - repeated int64 removed_config_ids = 3; - bool apply_to_children = 4; - string card_state_customizer = 5; -} - message SetTagCollapsedRequest { string name = 1; bool collapsed = 2; } -message SetDeckCollapsedRequest { - enum Scope { - REVIEWER = 0; - BROWSER = 1; - } - - int64 deck_id = 1; - bool collapsed = 2; - Scope scope = 3; -} - message GetChangedTagsResponse { repeated string tags = 1; } @@ -856,57 +439,6 @@ message SetConfigJsonRequest { bool undoable = 3; } -message StockNotetype { - enum Kind { - BASIC = 0; - BASIC_AND_REVERSED = 1; - BASIC_OPTIONAL_REVERSED = 2; - BASIC_TYPING = 3; - CLOZE = 4; - } - - Kind kind = 1; -} - -message NotetypeNames { - repeated NotetypeNameId entries = 1; -} - -message NotetypeUseCounts { - repeated NotetypeNameIdUseCount entries = 1; -} - -message NotetypeNameId { - int64 id = 1; - string name = 2; -} - -message NotetypeNameIdUseCount { - int64 id = 1; - string name = 2; - uint32 use_count = 3; -} - -message AddOrUpdateNotetypeRequest { - bytes json = 1; - bool preserve_usn_and_mtime = 2; -} - -message AddNoteRequest { - Note note = 1; - int64 deck_id = 2; -} - -message AddNoteResponse { - int64 note_id = 1; - collection.OpChanges changes = 2; -} - -message UpdateNoteRequest { - Note note = 1; - bool skip_undo_entry = 2; -} - message EmptyCardsReport { message NoteWithEmptyCards { int64 note_id = 1; @@ -917,28 +449,6 @@ message EmptyCardsReport { repeated NoteWithEmptyCards notes = 2; } -message DeckNames { - repeated DeckNameId entries = 1; -} - -message DeckNameId { - int64 id = 1; - string name = 2; -} - -message AddOrUpdateDeckLegacyRequest { - bytes deck = 1; - bool preserve_usn_and_mtime = 2; -} - -message FieldNamesForNotesRequest { - repeated int64 nids = 1; -} - -message FieldNamesForNotesResponse { - repeated string fields = 1; -} - message FindAndReplaceRequest { repeated int64 nids = 1; string search = 2; @@ -992,12 +502,6 @@ message BrowserRow { uint32 font_size = 4; } -message AfterNoteUpdatesRequest { - repeated int64 nids = 1; - bool mark_notes_modified = 2; - bool generate_cards = 3; -} - message NoteIdsAndTagsRequest { repeated int64 note_ids = 1; string tags = 2; @@ -1049,33 +553,6 @@ message Preferences { Editing editing = 3; } -message ClozeNumbersInNoteResponse { - repeated uint32 numbers = 1; -} - -message GetDeckNamesRequest { - bool skip_empty_default = 1; - // if unset, implies skip_empty_default - bool include_filtered = 2; -} - -message ReparentDecksRequest { - repeated int64 deck_ids = 1; - int64 new_parent = 2; -} - -message NoteFieldsCheckResponse { - enum State { - NORMAL = 0; - EMPTY = 1; - DUPLICATE = 2; - MISSING_CLOZE = 3; - NOTETYPE_NOT_CLOZE = 4; - FIELD_NOT_CLOZE = 5; - } - State state = 1; -} - message SyncLoginRequest { string username = 1; string password = 2; @@ -1131,11 +608,6 @@ message SyncServerMethodRequest { bytes data = 2; } -message RemoveNotesRequest { - repeated int64 note_ids = 1; - repeated int64 card_ids = 2; -} - message UpdateStatsRequest { int64 deck_id = 1; int32 new_delta = 2; @@ -1403,58 +875,3 @@ message QueuedCards { uint32 learning_count = 3; uint32 review_count = 4; } - -message DefaultsForAddingRequest { - int64 home_deck_of_current_review_card = 1; -} - -message DeckAndNotetype { - int64 deck_id = 1; - int64 notetype_id = 2; -} - -message RenameDeckRequest { - int64 deck_id = 1; - string new_name = 2; -} - -message FilteredDeckForUpdate { - int64 id = 1; - string name = 2; - Deck.Filtered config = 3; -} - -message GetAuxConfigKeyRequest { - int64 id = 1; - string key = 2; -} - -message GetAuxTemplateConfigKeyRequest { - int64 notetype_id = 1; - uint32 card_ordinal = 2; - string key = 3; -} - -message GetChangeNotetypeInfoRequest { - int64 old_notetype_id = 1; - int64 new_notetype_id = 2; -} - -message ChangeNotetypeRequest { - repeated int64 note_ids = 1; - // -1 is used to represent null, as nullable repeated fields - // are unwieldy in protobuf - repeated int32 new_fields = 2; - repeated int32 new_templates = 3; - int64 old_notetype_id = 4; - int64 new_notetype_id = 5; - int64 current_schema = 6; -} - -message ChangeNotetypeInfo { - repeated string old_field_names = 1; - repeated string old_template_names = 2; - repeated string new_field_names = 3; - repeated string new_template_names = 4; - ChangeNotetypeRequest input = 5; -} diff --git a/proto/anki/deckconfig.proto b/proto/anki/deckconfig.proto new file mode 100644 index 000000000..224ddad87 --- /dev/null +++ b/proto/anki/deckconfig.proto @@ -0,0 +1,145 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +syntax = "proto3"; + +package anki.deckconfig; + +import "anki/generic.proto"; +import "anki/collection.proto"; +import "anki/decks.proto"; + +service DeckConfigService { + rpc AddOrUpdateDeckConfigLegacy(generic.Json) returns (DeckConfigId); + rpc GetDeckConfig(DeckConfigId) returns (DeckConfig); + rpc AllDeckConfigLegacy(generic.Empty) returns (generic.Json); + rpc GetDeckConfigLegacy(DeckConfigId) returns (generic.Json); + rpc NewDeckConfigLegacy(generic.Empty) returns (generic.Json); + rpc RemoveDeckConfig(DeckConfigId) returns (generic.Empty); + rpc GetDeckConfigsForUpdate(decks.DeckId) returns (DeckConfigsForUpdate); + rpc UpdateDeckConfigs(UpdateDeckConfigsRequest) + returns (collection.OpChanges); +} + +message DeckConfigId { + int64 dcid = 1; +} + +message DeckConfig { + message Config { + enum NewCardInsertOrder { + NEW_CARD_INSERT_ORDER_DUE = 0; + NEW_CARD_INSERT_ORDER_RANDOM = 1; + } + enum NewCardGatherPriority { + NEW_CARD_GATHER_PRIORITY_DECK = 0; + NEW_CARD_GATHER_PRIORITY_LOWEST_POSITION = 1; + NEW_CARD_GATHER_PRIORITY_HIGHEST_POSITION = 2; + } + enum NewCardSortOrder { + NEW_CARD_SORT_ORDER_TEMPLATE_THEN_LOWEST_POSITION = 0; + NEW_CARD_SORT_ORDER_TEMPLATE_THEN_HIGHEST_POSITION = 1; + NEW_CARD_SORT_ORDER_TEMPLATE_THEN_RANDOM = 2; + NEW_CARD_SORT_ORDER_LOWEST_POSITION = 3; + NEW_CARD_SORT_ORDER_HIGHEST_POSITION = 4; + NEW_CARD_SORT_ORDER_RANDOM = 5; + } + enum ReviewCardOrder { + REVIEW_CARD_ORDER_DAY = 0; + REVIEW_CARD_ORDER_DAY_THEN_DECK = 1; + REVIEW_CARD_ORDER_DECK_THEN_DAY = 2; + REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 3; + REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 4; + // REVIEW_CARD_ORDER_RELATIVE_OVERDUE = 3; + } + enum ReviewMix { + REVIEW_MIX_MIX_WITH_REVIEWS = 0; + REVIEW_MIX_AFTER_REVIEWS = 1; + REVIEW_MIX_BEFORE_REVIEWS = 2; + } + enum LeechAction { + LEECH_ACTION_SUSPEND = 0; + LEECH_ACTION_TAG_ONLY = 1; + } + + repeated float learn_steps = 1; + repeated float relearn_steps = 2; + + reserved 3 to 8; + + uint32 new_per_day = 9; + uint32 reviews_per_day = 10; + uint32 new_per_day_minimum = 29; + + float initial_ease = 11; + float easy_multiplier = 12; + float hard_multiplier = 13; + float lapse_multiplier = 14; + float interval_multiplier = 15; + + uint32 maximum_review_interval = 16; + uint32 minimum_lapse_interval = 17; + + uint32 graduating_interval_good = 18; + uint32 graduating_interval_easy = 19; + + NewCardInsertOrder new_card_insert_order = 20; + NewCardGatherPriority new_card_gather_priority = 34; + NewCardSortOrder new_card_sort_order = 32; + ReviewMix new_mix = 30; + + ReviewCardOrder review_order = 33; + + ReviewMix interday_learning_mix = 31; + + LeechAction leech_action = 21; + uint32 leech_threshold = 22; + + bool disable_autoplay = 23; + uint32 cap_answer_time_to_secs = 24; + bool show_timer = 25; + bool skip_question_when_replaying_answer = 26; + + bool bury_new = 27; + bool bury_reviews = 28; + + bytes other = 255; + } + + int64 id = 1; + string name = 2; + int64 mtime_secs = 3; + int32 usn = 4; + Config config = 5; +} + +message DeckConfigsForUpdate { + message ConfigWithExtra { + DeckConfig config = 1; + uint32 use_count = 2; + } + message CurrentDeck { + string name = 1; + int64 config_id = 2; + repeated int64 parent_config_ids = 3; + } + + repeated ConfigWithExtra all_config = 1; + CurrentDeck current_deck = 2; + DeckConfig defaults = 3; + bool schema_modified = 4; + bool v3_scheduler = 5; + bool have_addons = 6; + // only applies to v3 scheduler + string card_state_customizer = 7; +} + +message UpdateDeckConfigsRequest { + int64 target_deck_id = 1; + /// Unchanged, non-selected configs can be omitted. Deck will + /// be set to whichever entry comes last. + repeated DeckConfig configs = 2; + repeated int64 removed_config_ids = 3; + bool apply_to_children = 4; + string card_state_customizer = 5; +} diff --git a/proto/anki/decks.proto b/proto/anki/decks.proto new file mode 100644 index 000000000..5e24ea1cc --- /dev/null +++ b/proto/anki/decks.proto @@ -0,0 +1,188 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +syntax = "proto3"; + +package anki.decks; + +import "anki/generic.proto"; +import "anki/collection.proto"; + +service DecksService { + rpc AddDeckLegacy(generic.Json) returns (collection.OpChangesWithId); + rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyRequest) returns (DeckId); + rpc DeckTree(DeckTreeRequest) returns (DeckTreeNode); + rpc DeckTreeLegacy(generic.Empty) returns (generic.Json); + rpc GetAllDecksLegacy(generic.Empty) returns (generic.Json); + rpc GetDeckIdByName(generic.String) returns (DeckId); + rpc GetDeck(DeckId) returns (Deck); + rpc UpdateDeck(Deck) returns (collection.OpChanges); + rpc UpdateDeckLegacy(generic.Json) returns (collection.OpChanges); + rpc SetDeckCollapsed(SetDeckCollapsedRequest) returns (collection.OpChanges); + rpc GetDeckLegacy(DeckId) returns (generic.Json); + rpc GetDeckNames(GetDeckNamesRequest) returns (DeckNames); + rpc NewDeckLegacy(generic.Bool) returns (generic.Json); + rpc RemoveDecks(DeckIds) returns (collection.OpChangesWithCount); + rpc ReparentDecks(ReparentDecksRequest) + returns (collection.OpChangesWithCount); + rpc RenameDeck(RenameDeckRequest) returns (collection.OpChanges); + rpc GetOrCreateFilteredDeck(DeckId) returns (FilteredDeckForUpdate); + rpc AddOrUpdateFilteredDeck(FilteredDeckForUpdate) + returns (collection.OpChangesWithId); + rpc FilteredDeckOrderLabels(generic.Empty) returns (generic.StringList); + rpc SetCurrentDeck(DeckId) returns (collection.OpChanges); + rpc GetCurrentDeck(generic.Empty) returns (Deck); +} + +message DeckId { + int64 did = 1; +} + +message DeckIds { + repeated int64 dids = 1; +} + +message Deck { + message Common { + bool study_collapsed = 1; + bool browser_collapsed = 2; + + uint32 last_day_studied = 3; + int32 new_studied = 4; + int32 review_studied = 5; + int32 milliseconds_studied = 7; + + // previously set in the v1 scheduler, + // but not currently used for anything + int32 learning_studied = 6; + + reserved 8 to 13; + + bytes other = 255; + } + message Normal { + int64 config_id = 1; + uint32 extend_new = 2; + uint32 extend_review = 3; + string description = 4; + bool markdown_description = 5; + + reserved 6 to 11; + } + message Filtered { + message SearchTerm { + enum Order { + OLDEST_REVIEWED_FIRST = 0; + RANDOM = 1; + INTERVALS_ASCENDING = 2; + INTERVALS_DESCENDING = 3; + LAPSES = 4; + ADDED = 5; + DUE = 6; + REVERSE_ADDED = 7; + DUE_PRIORITY = 8; + } + + string search = 1; + uint32 limit = 2; + Order order = 3; + } + + bool reschedule = 1; + repeated SearchTerm search_terms = 2; + // v1 scheduler only + repeated float delays = 3; + // v2 scheduler only + uint32 preview_delay = 4; + } + // a container to store the deck specifics in the DB + // as a tagged enum + message KindContainer { + oneof kind { + Normal normal = 1; + Filtered filtered = 2; + } + } + + int64 id = 1; + string name = 2; + int64 mtime_secs = 3; + int32 usn = 4; + Common common = 5; + // the specifics are inlined here when sending data to clients, + // as otherwise an extra level of indirection would be required + oneof kind { + Normal normal = 6; + Filtered filtered = 7; + } +} + +message AddOrUpdateDeckLegacyRequest { + bytes deck = 1; + bool preserve_usn_and_mtime = 2; +} + +message DeckTreeRequest { + // if non-zero, counts for the provided timestamp will be included + int64 now = 1; + int64 top_deck_id = 2; +} + +message DeckTreeNode { + int64 deck_id = 1; + string name = 2; + uint32 level = 4; + bool collapsed = 5; + + uint32 review_count = 6; + uint32 learn_count = 7; + uint32 new_count = 8; + + bool filtered = 16; + + // low index so key can be packed into a byte, but at bottom + // to make debug output easier to read + repeated DeckTreeNode children = 3; +} + +message SetDeckCollapsedRequest { + enum Scope { + REVIEWER = 0; + BROWSER = 1; + } + + int64 deck_id = 1; + bool collapsed = 2; + Scope scope = 3; +} + +message GetDeckNamesRequest { + bool skip_empty_default = 1; + // if unset, implies skip_empty_default + bool include_filtered = 2; +} + +message DeckNames { + repeated DeckNameId entries = 1; +} + +message DeckNameId { + int64 id = 1; + string name = 2; +} + +message ReparentDecksRequest { + repeated int64 deck_ids = 1; + int64 new_parent = 2; +} + +message RenameDeckRequest { + int64 deck_id = 1; + string new_name = 2; +} + +message FilteredDeckForUpdate { + int64 id = 1; + string name = 2; + Deck.Filtered config = 3; +} diff --git a/proto/anki/notes.proto b/proto/anki/notes.proto new file mode 100644 index 000000000..3c5fc7759 --- /dev/null +++ b/proto/anki/notes.proto @@ -0,0 +1,106 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +syntax = "proto3"; + +package anki.notes; + +import "anki/notetypes.proto"; +import "anki/collection.proto"; +import "anki/decks.proto"; +import "anki/cards.proto"; + +service NotesService { + rpc NewNote(notetypes.NotetypeId) returns (Note); + rpc AddNote(AddNoteRequest) returns (AddNoteResponse); + rpc DefaultsForAdding(DefaultsForAddingRequest) returns (DeckAndNotetype); + rpc DefaultDeckForNotetype(notetypes.NotetypeId) returns (decks.DeckId); + rpc UpdateNote(UpdateNoteRequest) returns (collection.OpChanges); + rpc GetNote(NoteId) returns (Note); + rpc RemoveNotes(RemoveNotesRequest) returns (collection.OpChangesWithCount); + rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteResponse); + rpc AfterNoteUpdates(AfterNoteUpdatesRequest) + returns (collection.OpChangesWithCount); + rpc FieldNamesForNotes(FieldNamesForNotesRequest) + returns (FieldNamesForNotesResponse); + rpc NoteFieldsCheck(Note) returns (NoteFieldsCheckResponse); + rpc CardsOfNote(NoteId) returns (cards.CardIds); + rpc GetSingleNotetypeOfNotes(notes.NoteIds) returns (notetypes.NotetypeId); +} + +message NoteId { + int64 nid = 1; +} + +message NoteIds { + repeated int64 note_ids = 1; +} + +message Note { + int64 id = 1; + string guid = 2; + int64 notetype_id = 3; + uint32 mtime_secs = 4; + int32 usn = 5; + repeated string tags = 6; + repeated string fields = 7; +} + +message AddNoteRequest { + Note note = 1; + int64 deck_id = 2; +} + +message AddNoteResponse { + int64 note_id = 1; + collection.OpChanges changes = 2; +} + +message UpdateNoteRequest { + Note note = 1; + bool skip_undo_entry = 2; +} + +message DefaultsForAddingRequest { + int64 home_deck_of_current_review_card = 1; +} + +message DeckAndNotetype { + int64 deck_id = 1; + int64 notetype_id = 2; +} + +message RemoveNotesRequest { + repeated int64 note_ids = 1; + repeated int64 card_ids = 2; +} + +message ClozeNumbersInNoteResponse { + repeated uint32 numbers = 1; +} + +message AfterNoteUpdatesRequest { + repeated int64 nids = 1; + bool mark_notes_modified = 2; + bool generate_cards = 3; +} + +message FieldNamesForNotesRequest { + repeated int64 nids = 1; +} + +message FieldNamesForNotesResponse { + repeated string fields = 1; +} + +message NoteFieldsCheckResponse { + enum State { + NORMAL = 0; + EMPTY = 1; + DUPLICATE = 2; + MISSING_CLOZE = 3; + NOTETYPE_NOT_CLOZE = 4; + FIELD_NOT_CLOZE = 5; + } + State state = 1; +} diff --git a/proto/anki/notetypes.proto b/proto/anki/notetypes.proto new file mode 100644 index 000000000..4065e34ab --- /dev/null +++ b/proto/anki/notetypes.proto @@ -0,0 +1,176 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +syntax = "proto3"; + +package anki.notetypes; + +import "anki/generic.proto"; +import "anki/collection.proto"; + +service NotetypesService { + rpc AddNotetype(Notetype) returns (collection.OpChangesWithId); + rpc UpdateNotetype(Notetype) returns (collection.OpChanges); + rpc AddNotetypeLegacy(generic.Json) returns (collection.OpChangesWithId); + rpc UpdateNotetypeLegacy(generic.Json) returns (collection.OpChanges); + rpc AddOrUpdateNotetype(AddOrUpdateNotetypeRequest) returns (NotetypeId); + rpc GetStockNotetypeLegacy(StockNotetype) returns (generic.Json); + rpc GetNotetype(NotetypeId) returns (Notetype); + rpc GetNotetypeLegacy(NotetypeId) returns (generic.Json); + rpc GetNotetypeNames(generic.Empty) returns (NotetypeNames); + rpc GetNotetypeNamesAndCounts(generic.Empty) returns (NotetypeUseCounts); + rpc GetNotetypeIdByName(generic.String) returns (NotetypeId); + rpc RemoveNotetype(NotetypeId) returns (collection.OpChanges); + rpc GetAuxNotetypeConfigKey(GetAuxConfigKeyRequest) returns (generic.String); + rpc GetAuxTemplateConfigKey(GetAuxTemplateConfigKeyRequest) + returns (generic.String); + rpc GetChangeNotetypeInfo(GetChangeNotetypeInfoRequest) + returns (ChangeNotetypeInfo); + rpc ChangeNotetype(ChangeNotetypeRequest) returns (collection.OpChanges); +} + +message NotetypeId { + int64 ntid = 1; +} + +message Notetype { + message Config { + enum Kind { + KIND_NORMAL = 0; + KIND_CLOZE = 1; + } + message CardRequirement { + enum Kind { + KIND_NONE = 0; + KIND_ANY = 1; + KIND_ALL = 2; + } + uint32 card_ord = 1; + Kind kind = 2; + repeated uint32 field_ords = 3; + } + + Kind kind = 1; + uint32 sort_field_idx = 2; + string css = 3; + /// This is now stored separately; retrieve with DefaultsForAdding() + int64 target_deck_id_unused = 4; + string latex_pre = 5; + string latex_post = 6; + bool latex_svg = 7; + repeated CardRequirement reqs = 8; + + bytes other = 255; + } + message Field { + message Config { + bool sticky = 1; + bool rtl = 2; + string font_name = 3; + uint32 font_size = 4; + + bytes other = 255; + } + generic.OptionalUInt32 ord = 1; + string name = 2; + Config config = 5; + } + message Template { + message Config { + string q_format = 1; + string a_format = 2; + string q_format_browser = 3; + string a_format_browser = 4; + int64 target_deck_id = 5; + string browser_font_name = 6; + uint32 browser_font_size = 7; + + bytes other = 255; + } + + generic.OptionalUInt32 ord = 1; + string name = 2; + int64 mtime_secs = 3; + sint32 usn = 4; + Config config = 5; + } + + int64 id = 1; + string name = 2; + int64 mtime_secs = 3; + sint32 usn = 4; + Config config = 7; + repeated Field fields = 8; + repeated Template templates = 9; +} + +message AddOrUpdateNotetypeRequest { + bytes json = 1; + bool preserve_usn_and_mtime = 2; +} + +message StockNotetype { + enum Kind { + BASIC = 0; + BASIC_AND_REVERSED = 1; + BASIC_OPTIONAL_REVERSED = 2; + BASIC_TYPING = 3; + CLOZE = 4; + } + + Kind kind = 1; +} + +message NotetypeNames { + repeated NotetypeNameId entries = 1; +} + +message NotetypeUseCounts { + repeated NotetypeNameIdUseCount entries = 1; +} + +message NotetypeNameId { + int64 id = 1; + string name = 2; +} + +message NotetypeNameIdUseCount { + int64 id = 1; + string name = 2; + uint32 use_count = 3; +} + +message GetAuxConfigKeyRequest { + int64 id = 1; + string key = 2; +} + +message GetAuxTemplateConfigKeyRequest { + int64 notetype_id = 1; + uint32 card_ordinal = 2; + string key = 3; +} + +message GetChangeNotetypeInfoRequest { + int64 old_notetype_id = 1; + int64 new_notetype_id = 2; +} + +message ChangeNotetypeRequest { + repeated int64 note_ids = 1; + // -1 is used to represent null, as nullable repeated fields + // are unwieldy in protobuf + repeated int32 new_fields = 2; + repeated int32 new_templates = 3; + int64 old_notetype_id = 4; + int64 new_notetype_id = 5; + int64 current_schema = 6; +} + +message ChangeNotetypeInfo { + repeated string old_field_names = 1; + repeated string old_template_names = 2; + repeated string new_field_names = 3; + repeated string new_template_names = 4; + ChangeNotetypeRequest input = 5; +} diff --git a/pylib/anki/_backend/genbackend.py b/pylib/anki/_backend/genbackend.py index 4d8530cbb..cbe84735c 100755 --- a/pylib/anki/_backend/genbackend.py +++ b/pylib/anki/_backend/genbackend.py @@ -12,6 +12,10 @@ import anki.backend_pb2 import anki.i18n_pb2 import anki.cards_pb2 import anki.collection_pb2 +import anki.decks_pb2 +import anki.deckconfig_pb2 +import anki.notes_pb2 +import anki.notetypes_pb2 import stringcase @@ -171,6 +175,10 @@ service_modules = dict( I18N=anki.i18n_pb2, COLLECTION=anki.collection_pb2, CARDS=anki.cards_pb2, + NOTES=anki.notes_pb2, + DECKS=anki.decks_pb2, + DECK_CONFIG=anki.deckconfig_pb2, + NOTETYPES=anki.notetypes_pb2, ) for service in anki.backend_pb2.ServiceIndex.DESCRIPTOR.values: diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index c331edd09..7821a180d 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -22,7 +22,6 @@ OpChanges = collection_pb2.OpChanges OpChangesWithCount = collection_pb2.OpChangesWithCount OpChangesWithId = collection_pb2.OpChangesWithId OpChangesAfterUndo = collection_pb2.OpChangesAfterUndo -DefaultsForAdding = _pb.DeckAndNotetype BrowserRow = _pb.BrowserRow BrowserColumns = _pb.BrowserColumns @@ -370,7 +369,7 @@ class Collection(DeprecatedNamesMixin): def defaults_for_adding( self, *, current_review_card: Optional[Card] - ) -> DefaultsForAdding: + ) -> anki.notes.DefaultsForAdding: """Get starting deck and notetype for add screen. An option in the preferences controls whether this will be based on the current deck or current notetype. diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index d2291caf8..bb32c074f 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -23,7 +23,7 @@ from typing import ( if TYPE_CHECKING: import anki -import anki.backend_pb2 as _pb +from anki import deckconfig_pb2, decks_pb2 from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning from anki.cards import CardId from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId @@ -32,12 +32,12 @@ from anki.errors import NotFoundError from anki.utils import from_json_bytes, ids2str, intTime, to_json_bytes # public exports -DeckTreeNode = _pb.DeckTreeNode -DeckNameId = _pb.DeckNameId -FilteredDeckConfig = _pb.Deck.Filtered -DeckCollapseScope = _pb.SetDeckCollapsedRequest.Scope -DeckConfigsForUpdate = _pb.DeckConfigsForUpdate -UpdateDeckConfigs = _pb.UpdateDeckConfigsRequest +DeckTreeNode = decks_pb2.DeckTreeNode +DeckNameId = decks_pb2.DeckNameId +FilteredDeckConfig = decks_pb2.Deck.Filtered +DeckCollapseScope = decks_pb2.SetDeckCollapsedRequest.Scope +DeckConfigsForUpdate = deckconfig_pb2.DeckConfigsForUpdate +UpdateDeckConfigs = deckconfig_pb2.UpdateDeckConfigsRequest # type aliases until we can move away from dicts DeckDict = Dict[str, Any] diff --git a/pylib/anki/models.py b/pylib/anki/models.py index 50066e711..889d749bd 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -12,7 +12,7 @@ import time from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union import anki # pylint: disable=unused-import -import anki.backend_pb2 as _pb +from anki import notetypes_pb2 from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning from anki.collection import OpChanges, OpChangesWithId from anki.consts import * @@ -22,11 +22,11 @@ from anki.stdmodels import StockNotetypeKind from anki.utils import checksum, from_json_bytes, to_json_bytes # public exports -NotetypeNameId = _pb.NotetypeNameId -NotetypeNameIdUseCount = _pb.NotetypeNameIdUseCount -NotetypeNames = _pb.NotetypeNames -ChangeNotetypeInfo = _pb.ChangeNotetypeInfo -ChangeNotetypeRequest = _pb.ChangeNotetypeRequest +NotetypeNameId = notetypes_pb2.NotetypeNameId +NotetypeNameIdUseCount = notetypes_pb2.NotetypeNameIdUseCount +NotetypeNames = notetypes_pb2.NotetypeNames +ChangeNotetypeInfo = notetypes_pb2.ChangeNotetypeInfo +ChangeNotetypeRequest = notetypes_pb2.ChangeNotetypeRequest # legacy types NotetypeDict = Dict[str, Any] @@ -459,7 +459,9 @@ and notes.mid = ? and cards.ord = ?""", def _availClozeOrds( self, notetype: NotetypeDict, flds: str, allow_empty: bool = True ) -> List[int]: - note = _pb.Note(fields=[flds]) + import anki.notes_pb2 + + note = anki.notes_pb2.Note(fields=[flds]) return list(self.col._backend.cloze_numbers_in_note(note)) # @deprecated(replaced_by=add_template) diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index d6ad1f5de..f63da1083 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -9,15 +9,15 @@ import copy from typing import Any, List, NewType, Optional, Sequence, Tuple, Union import anki # pylint: disable=unused-import -import anki.backend_pb2 as _pb -from anki import hooks +from anki import hooks, notes_pb2 from anki._legacy import DeprecatedNamesMixin from anki.consts import MODEL_STD from anki.models import NotetypeDict, NotetypeId, TemplateDict from anki.utils import joinFields -DuplicateOrEmptyResult = _pb.NoteFieldsCheckResponse.State -NoteFieldsCheckResult = _pb.NoteFieldsCheckResponse.State +DuplicateOrEmptyResult = notes_pb2.NoteFieldsCheckResponse.State +NoteFieldsCheckResult = notes_pb2.NoteFieldsCheckResponse.State +DefaultsForAdding = notes_pb2.DeckAndNotetype # types NoteId = NewType("NoteId", int) @@ -53,7 +53,7 @@ class Note(DeprecatedNamesMixin): assert note self._load_from_backend_note(note) - def _load_from_backend_note(self, note: _pb.Note) -> None: + def _load_from_backend_note(self, note: notes_pb2.Note) -> None: self.id = NoteId(note.id) self.guid = note.guid self.mid = NotetypeId(note.notetype_id) @@ -63,9 +63,9 @@ class Note(DeprecatedNamesMixin): self.fields = list(note.fields) self._fmap = self.col.models.field_map(self.note_type()) - def _to_backend_note(self) -> _pb.Note: + def _to_backend_note(self) -> notes_pb2.Note: hooks.note_will_flush(self) - return _pb.Note( + return notes_pb2.Note( id=self.id, guid=self.guid, notetype_id=self.mid, diff --git a/pylib/anki/scheduler/base.py b/pylib/anki/scheduler/base.py index c3f3534d8..0f602b831 100644 --- a/pylib/anki/scheduler/base.py +++ b/pylib/anki/scheduler/base.py @@ -5,6 +5,7 @@ from __future__ import annotations import anki import anki.backend_pb2 as _pb +from anki import decks_pb2 from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId from anki.config import Config @@ -22,7 +23,7 @@ from anki.utils import ids2str, intTime CongratsInfo = _pb.CongratsInfoResponse UnburyDeck = _pb.UnburyDeckRequest BuryOrSuspend = _pb.BuryOrSuspendCardsRequest -FilteredDeckForUpdate = _pb.FilteredDeckForUpdate +FilteredDeckForUpdate = decks_pb2.FilteredDeckForUpdate class SchedulerBase: diff --git a/pylib/anki/stdmodels.py b/pylib/anki/stdmodels.py index 01ac09b45..720007408 100644 --- a/pylib/anki/stdmodels.py +++ b/pylib/anki/stdmodels.py @@ -6,11 +6,11 @@ from __future__ import annotations from typing import Any, Callable, List, Tuple import anki -import anki.backend_pb2 as _pb +from anki import notetypes_pb2 from anki.utils import from_json_bytes # pylint: disable=no-member -StockNotetypeKind = _pb.StockNotetype.Kind +StockNotetypeKind = notetypes_pb2.StockNotetype.Kind # add-on authors can add ("note type name", function) # to this list to have it shown in the add/clone note type screen diff --git a/rslib/src/backend/notes.rs b/rslib/src/backend/notes.rs index d3b8c93a2..e54123910 100644 --- a/rslib/src/backend/notes.rs +++ b/rslib/src/backend/notes.rs @@ -143,6 +143,13 @@ impl NotesService for Backend { }) }) } + + fn get_single_notetype_of_notes(&self, input: pb::NoteIds) -> Result { + self.with_col(|col| { + col.get_single_notetype_of_notes(&input.note_ids.into_newtype(NoteId)) + .map(Into::into) + }) + } } pub(super) fn to_note_ids(ids: Vec) -> Vec { diff --git a/rslib/src/backend/notetypes.rs b/rslib/src/backend/notetypes.rs index e47f1d484..9dfe9ab44 100644 --- a/rslib/src/backend/notetypes.rs +++ b/rslib/src/backend/notetypes.rs @@ -159,13 +159,6 @@ impl NotetypesService for Backend { }) } - fn get_single_notetype_of_notes(&self, input: pb::NoteIds) -> Result { - self.with_col(|col| { - col.get_single_notetype_of_notes(&input.note_ids.into_newtype(NoteId)) - .map(Into::into) - }) - } - fn get_change_notetype_info( &self, input: pb::GetChangeNotetypeInfoRequest, diff --git a/rslib/src/backend_proto.rs b/rslib/src/backend_proto.rs index f0cbcd718..43ce3c497 100644 --- a/rslib/src/backend_proto.rs +++ b/rslib/src/backend_proto.rs @@ -1,24 +1,24 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -pub mod backend { - include!(concat!(env!("OUT_DIR"), "/anki.backend.rs")); -} -pub mod i18n { - include!(concat!(env!("OUT_DIR"), "/anki.i18n.rs")); -} -pub mod generic { - include!(concat!(env!("OUT_DIR"), "/anki.generic.rs")); -} -pub mod cards { - include!(concat!(env!("OUT_DIR"), "/anki.cards.rs")); -} -pub mod collection { - include!(concat!(env!("OUT_DIR"), "/anki.collection.rs")); +macro_rules! protobuf { + ($ident:ident) => { + pub mod $ident { + include!(concat!( + env!("OUT_DIR"), + concat!("/anki.", stringify!($ident), ".rs") + )); + } + pub use $ident::*; + }; } -pub use backend::*; -pub use cards::*; -pub use collection::*; -pub use generic::*; -pub use i18n::*; +protobuf!(backend); +protobuf!(notes); +protobuf!(notetypes); +protobuf!(decks); +protobuf!(deckconfig); +protobuf!(i18n); +protobuf!(cards); +protobuf!(generic); +protobuf!(collection); diff --git a/ts/change-notetype/lib.test.ts b/ts/change-notetype/lib.test.ts index 7f392a455..bcda9781c 100644 --- a/ts/change-notetype/lib.test.ts +++ b/ts/change-notetype/lib.test.ts @@ -5,7 +5,7 @@ @typescript-eslint/no-explicit-any: "off", */ -import { Backend } from "lib/proto"; +import { Notetypes } from "lib/proto"; import { ChangeNotetypeState, negativeOneToNull, MapContext } from "./lib"; import { get } from "svelte/store"; @@ -64,15 +64,15 @@ const exampleInfoSame = { function differentState(): ChangeNotetypeState { return new ChangeNotetypeState( - Backend.NotetypeNames.fromObject(exampleNames), - Backend.ChangeNotetypeInfo.fromObject(exampleInfoDifferent) + Notetypes.NotetypeNames.fromObject(exampleNames), + Notetypes.ChangeNotetypeInfo.fromObject(exampleInfoDifferent) ); } function sameState(): ChangeNotetypeState { return new ChangeNotetypeState( - Backend.NotetypeNames.fromObject(exampleNames), - Backend.ChangeNotetypeInfo.fromObject(exampleInfoSame) + Notetypes.NotetypeNames.fromObject(exampleNames), + Notetypes.ChangeNotetypeInfo.fromObject(exampleInfoSame) ); } diff --git a/ts/change-notetype/lib.ts b/ts/change-notetype/lib.ts index df982fa9a..84661db38 100644 --- a/ts/change-notetype/lib.ts +++ b/ts/change-notetype/lib.ts @@ -5,20 +5,22 @@ @typescript-eslint/no-non-null-assertion: "off", */ -import { Backend } from "lib/proto"; +import { Notetypes } from "lib/proto"; import { postRequest } from "lib/postrequest"; import { readable, Readable } from "svelte/store"; import { isEqual } from "lodash-es"; -export async function getNotetypeNames(): Promise { - return Backend.NotetypeNames.decode(await postRequest("/_anki/notetypeNames", "")); +export async function getNotetypeNames(): Promise { + return Notetypes.NotetypeNames.decode( + await postRequest("/_anki/notetypeNames", "") + ); } export async function getChangeNotetypeInfo( oldNotetypeId: number, newNotetypeId: number -): Promise { - return Backend.ChangeNotetypeInfo.decode( +): Promise { + return Notetypes.ChangeNotetypeInfo.decode( await postRequest( "/_anki/changeNotetypeInfo", JSON.stringify({ oldNotetypeId, newNotetypeId }) @@ -27,9 +29,9 @@ export async function getChangeNotetypeInfo( } export async function changeNotetype( - input: Backend.ChangeNotetypeRequest + input: Notetypes.ChangeNotetypeRequest ): Promise { - const data: Uint8Array = Backend.ChangeNotetypeRequest.encode(input).finish(); + const data: Uint8Array = Notetypes.ChangeNotetypeRequest.encode(input).finish(); await postRequest("/_anki/changeNotetype", data); return; } @@ -47,9 +49,9 @@ export function negativeOneToNull(list: number[]): (number | null)[] { export class ChangeNotetypeInfoWrapper { fields: (number | null)[]; templates?: (number | null)[]; - readonly info: Backend.ChangeNotetypeInfo; + readonly info: Notetypes.ChangeNotetypeInfo; - constructor(info: Backend.ChangeNotetypeInfo) { + constructor(info: Notetypes.ChangeNotetypeInfo) { this.info = info; const templates = info.input!.newTemplates!; if (templates.length > 0) { @@ -111,13 +113,13 @@ export class ChangeNotetypeInfoWrapper { ); } - input(): Backend.ChangeNotetypeRequest { - return this.info.input as Backend.ChangeNotetypeRequest; + input(): Notetypes.ChangeNotetypeRequest { + return this.info.input as Notetypes.ChangeNotetypeRequest; } /// Pack changes back into input message for saving. - intoInput(): Backend.ChangeNotetypeRequest { - const input = this.info.input as Backend.ChangeNotetypeRequest; + intoInput(): Notetypes.ChangeNotetypeRequest { + const input = this.info.input as Notetypes.ChangeNotetypeRequest; input.newFields = nullToNegativeOne(this.fields); if (this.templates) { input.newTemplates = nullToNegativeOne(this.templates); @@ -143,10 +145,13 @@ export class ChangeNotetypeState { private info_: ChangeNotetypeInfoWrapper; private infoSetter!: (val: ChangeNotetypeInfoWrapper) => void; - private notetypeNames: Backend.NotetypeNames; + private notetypeNames: Notetypes.NotetypeNames; private notetypesSetter!: (val: NotetypeListEntry[]) => void; - constructor(notetypes: Backend.NotetypeNames, info: Backend.ChangeNotetypeInfo) { + constructor( + notetypes: Notetypes.NotetypeNames, + info: Notetypes.ChangeNotetypeInfo + ) { this.info_ = new ChangeNotetypeInfoWrapper(info); this.info = readable(this.info_, (set) => { this.infoSetter = set; @@ -197,7 +202,7 @@ export class ChangeNotetypeState { await changeNotetype(this.dataForSaving()); } - dataForSaving(): Backend.ChangeNotetypeRequest { + dataForSaving(): Notetypes.ChangeNotetypeRequest { return this.info_.intoInput(); } diff --git a/ts/deck-options/lib.test.ts b/ts/deck-options/lib.test.ts index 1b2ba26ee..eb9168896 100644 --- a/ts/deck-options/lib.test.ts +++ b/ts/deck-options/lib.test.ts @@ -5,7 +5,7 @@ @typescript-eslint/no-explicit-any: "off", */ -import { Backend } from "lib/proto"; +import { DeckConfig } from "lib/proto"; import { DeckOptionsState } from "./lib"; import { get } from "svelte/store"; @@ -94,7 +94,7 @@ const exampleData = { function startingState(): DeckOptionsState { return new DeckOptionsState( 123, - Backend.DeckConfigsForUpdate.fromObject(exampleData) + DeckConfig.DeckConfigsForUpdate.fromObject(exampleData) ); } diff --git a/ts/deck-options/lib.ts b/ts/deck-options/lib.ts index 506225374..1775d4815 100644 --- a/ts/deck-options/lib.ts +++ b/ts/deck-options/lib.ts @@ -5,7 +5,7 @@ @typescript-eslint/no-non-null-assertion: "off", */ -import { Backend } from "lib/proto"; +import { DeckConfig } from "lib/proto"; import { postRequest } from "lib/postrequest"; import { Writable, writable, get, Readable, readable } from "svelte/store"; import { isEqual, cloneDeep } from "lodash-es"; @@ -14,16 +14,16 @@ import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent"; export async function getDeckOptionsInfo( deckId: number -): Promise { - return Backend.DeckConfigsForUpdate.decode( +): Promise { + return DeckConfig.DeckConfigsForUpdate.decode( await postRequest("/_anki/deckConfigsForUpdate", JSON.stringify({ deckId })) ); } export async function saveDeckOptions( - input: Backend.UpdateDeckConfigsRequest + input: DeckConfig.UpdateDeckConfigsRequest ): Promise { - const data: Uint8Array = Backend.UpdateDeckConfigsRequest.encode(input).finish(); + const data: Uint8Array = DeckConfig.UpdateDeckConfigsRequest.encode(input).finish(); await postRequest("/_anki/updateDeckConfigs", data); return; } @@ -31,7 +31,7 @@ export async function saveDeckOptions( export type DeckOptionsId = number; export interface ConfigWithCount { - config: Backend.DeckConfig; + config: DeckConfig.DeckConfig; useCount: number; } @@ -48,14 +48,14 @@ export interface ConfigListEntry { current: boolean; } -type ConfigInner = Backend.DeckConfig.Config; +type ConfigInner = DeckConfig.DeckConfig.Config; export class DeckOptionsState { readonly currentConfig: Writable; readonly currentAuxData: Writable>; readonly configList: Readable; readonly parentLimits: Readable; readonly cardStateCustomizer: Writable; - readonly currentDeck: Backend.DeckConfigsForUpdate.CurrentDeck; + readonly currentDeck: DeckConfig.DeckConfigsForUpdate.CurrentDeck; readonly defaults: ConfigInner; readonly addonComponents: Writable; readonly v3Scheduler: boolean; @@ -70,12 +70,13 @@ export class DeckOptionsState { private removedConfigs: DeckOptionsId[] = []; private schemaModified: boolean; - constructor(targetDeckId: number, data: Backend.DeckConfigsForUpdate) { + constructor(targetDeckId: number, data: DeckConfig.DeckConfigsForUpdate) { this.targetDeckId = targetDeckId; - this.currentDeck = data.currentDeck as Backend.DeckConfigsForUpdate.CurrentDeck; + this.currentDeck = + data.currentDeck as DeckConfig.DeckConfigsForUpdate.CurrentDeck; this.defaults = data.defaults!.config! as ConfigInner; this.configs = data.allConfig.map((config) => { - const configInner = config.config as Backend.DeckConfig; + const configInner = config.config as DeckConfig.DeckConfig; return { config: configInner, useCount: config.useCount!, @@ -150,9 +151,9 @@ export class DeckOptionsState { } /// Clone the current config, making it current. - private addConfigFrom(name: string, source: Backend.DeckConfig.IConfig): void { + private addConfigFrom(name: string, source: DeckConfig.DeckConfig.IConfig): void { const uniqueName = this.ensureNewNameUnique(name); - const config = Backend.DeckConfig.create({ + const config = DeckConfig.DeckConfig.create({ id: 0, name: uniqueName, config: cloneDeep(source), @@ -188,7 +189,7 @@ export class DeckOptionsState { this.updateConfigList(); } - dataForSaving(applyToChildren: boolean): Backend.UpdateDeckConfigsRequest { + dataForSaving(applyToChildren: boolean): DeckConfig.UpdateDeckConfigsRequest { const modifiedConfigsExcludingCurrent = this.configs .map((c) => c.config) .filter((c, idx) => { @@ -202,7 +203,7 @@ export class DeckOptionsState { // current must come last, even if unmodified this.configs[this.selectedIdx].config, ]; - return Backend.UpdateDeckConfigsRequest.create({ + return DeckConfig.UpdateDeckConfigsRequest.create({ targetDeckId: this.targetDeckId, removedConfigIds: this.removedConfigs, configs, diff --git a/ts/lib/proto.ts b/ts/lib/proto.ts index c1a245585..507f637d6 100644 --- a/ts/lib/proto.ts +++ b/ts/lib/proto.ts @@ -4,4 +4,6 @@ import { anki } from "./backend_proto"; import Backend = anki.backend; import Cards = anki.cards; -export { Backend, Cards }; +import DeckConfig = anki.deckconfig; +import Notetypes = anki.notetypes; +export { Backend, Cards, DeckConfig, Notetypes };