split out decks, deckconfig, notes, notetypes

This commit is contained in:
Damien Elmes 2021-07-10 20:44:22 +10:00
parent 18851ace47
commit 9e0a295ab9
20 changed files with 733 additions and 683 deletions

View file

@ -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;
}

145
proto/anki/deckconfig.proto Normal file
View file

@ -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;
}

188
proto/anki/decks.proto Normal file
View file

@ -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;
}

106
proto/anki/notes.proto Normal file
View file

@ -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;
}

176
proto/anki/notetypes.proto Normal file
View file

@ -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;
}

View file

@ -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:

View file

@ -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.

View file

@ -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]

View file

@ -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)

View file

@ -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,

View file

@ -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:

View file

@ -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

View file

@ -143,6 +143,13 @@ impl NotesService for Backend {
})
})
}
fn get_single_notetype_of_notes(&self, input: pb::NoteIds) -> Result<pb::NotetypeId> {
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<i64>) -> Vec<NoteId> {

View file

@ -159,13 +159,6 @@ impl NotetypesService for Backend {
})
}
fn get_single_notetype_of_notes(&self, input: pb::NoteIds) -> Result<pb::NotetypeId> {
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,

View file

@ -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);

View file

@ -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)
);
}

View file

@ -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<Backend.NotetypeNames> {
return Backend.NotetypeNames.decode(await postRequest("/_anki/notetypeNames", ""));
export async function getNotetypeNames(): Promise<Notetypes.NotetypeNames> {
return Notetypes.NotetypeNames.decode(
await postRequest("/_anki/notetypeNames", "")
);
}
export async function getChangeNotetypeInfo(
oldNotetypeId: number,
newNotetypeId: number
): Promise<Backend.ChangeNotetypeInfo> {
return Backend.ChangeNotetypeInfo.decode(
): Promise<Notetypes.ChangeNotetypeInfo> {
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<void> {
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();
}

View file

@ -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)
);
}

View file

@ -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<Backend.DeckConfigsForUpdate> {
return Backend.DeckConfigsForUpdate.decode(
): Promise<DeckConfig.DeckConfigsForUpdate> {
return DeckConfig.DeckConfigsForUpdate.decode(
await postRequest("/_anki/deckConfigsForUpdate", JSON.stringify({ deckId }))
);
}
export async function saveDeckOptions(
input: Backend.UpdateDeckConfigsRequest
input: DeckConfig.UpdateDeckConfigsRequest
): Promise<void> {
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<ConfigInner>;
readonly currentAuxData: Writable<Record<string, unknown>>;
readonly configList: Readable<ConfigListEntry[]>;
readonly parentLimits: Readable<ParentLimits>;
readonly cardStateCustomizer: Writable<string>;
readonly currentDeck: Backend.DeckConfigsForUpdate.CurrentDeck;
readonly currentDeck: DeckConfig.DeckConfigsForUpdate.CurrentDeck;
readonly defaults: ConfigInner;
readonly addonComponents: Writable<DynamicSvelteComponent[]>;
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,

View file

@ -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 };