split out sync, search, scheduler & config

This commit is contained in:
Damien Elmes 2021-07-10 21:33:12 +10:00
parent 9e0a295ab9
commit 35b059ecdb
29 changed files with 675 additions and 617 deletions

View file

@ -7,7 +7,6 @@ package anki.backend;
import "anki/generic.proto"; import "anki/generic.proto";
import "anki/cards.proto"; import "anki/cards.proto";
import "anki/decks.proto";
import "anki/collection.proto"; import "anki/collection.proto";
import "anki/notes.proto"; import "anki/notes.proto";
import "anki/notetypes.proto"; import "anki/notetypes.proto";
@ -19,12 +18,12 @@ import "anki/notetypes.proto";
/// that information is not available in prost, so we define an enum to make /// that information is not available in prost, so we define an enum to make
/// sure all clients agree on the service index /// sure all clients agree on the service index
enum ServiceIndex { enum ServiceIndex {
SERVICE_INDEX_SCHEDULING = 0; SERVICE_INDEX_SCHEDULER = 0;
SERVICE_INDEX_DECKS = 1; SERVICE_INDEX_DECKS = 1;
SERVICE_INDEX_NOTES = 2; SERVICE_INDEX_NOTES = 2;
SERVICE_INDEX_SYNC = 3; SERVICE_INDEX_SYNC = 3;
SERVICE_INDEX_NOTETYPES = 4; SERVICE_INDEX_NOTETYPES = 4;
SERVICE_INDEX_CONFIG = 5; SERVICE_INDEX_CONFIGS = 5;
SERVICE_INDEX_CARD_RENDERING = 6; SERVICE_INDEX_CARD_RENDERING = 6;
SERVICE_INDEX_DECK_CONFIG = 7; SERVICE_INDEX_DECK_CONFIG = 7;
SERVICE_INDEX_TAGS = 8; SERVICE_INDEX_TAGS = 8;
@ -36,61 +35,6 @@ enum ServiceIndex {
SERVICE_INDEX_CARDS = 14; SERVICE_INDEX_CARDS = 14;
} }
service SchedulingService {
rpc SchedTimingToday(generic.Empty) returns (SchedTimingTodayResponse);
rpc StudiedToday(generic.Empty) returns (generic.String);
rpc StudiedTodayMessage(StudiedTodayMessageRequest) returns (generic.String);
rpc UpdateStats(UpdateStatsRequest) returns (generic.Empty);
rpc ExtendLimits(ExtendLimitsRequest) returns (generic.Empty);
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(decks.DeckId) returns (collection.OpChanges);
rpc RebuildFilteredDeck(decks.DeckId) returns (collection.OpChangesWithCount);
rpc ScheduleCardsAsNew(ScheduleCardsAsNewRequest)
returns (collection.OpChanges);
rpc SetDueDate(SetDueDateRequest) returns (collection.OpChanges);
rpc SortCards(SortCardsRequest) returns (collection.OpChangesWithCount);
rpc SortDeck(SortDeckRequest) returns (collection.OpChangesWithCount);
rpc GetNextCardStates(cards.CardId) returns (NextCardStates);
rpc DescribeNextStates(NextCardStates) returns (generic.StringList);
rpc StateIsLeech(SchedulingState) returns (generic.Bool);
rpc AnswerCard(CardAnswer) returns (collection.OpChanges);
rpc UpgradeScheduler(generic.Empty) returns (generic.Empty);
rpc GetQueuedCards(GetQueuedCardsRequest) returns (QueuedCards);
}
service SyncService {
rpc SyncMedia(SyncAuth) returns (generic.Empty);
rpc AbortSync(generic.Empty) returns (generic.Empty);
rpc AbortMediaSync(generic.Empty) returns (generic.Empty);
rpc BeforeUpload(generic.Empty) returns (generic.Empty);
rpc SyncLogin(SyncLoginRequest) returns (SyncAuth);
rpc SyncStatus(SyncAuth) returns (SyncStatusResponse);
rpc SyncCollection(SyncAuth) returns (SyncCollectionResponse);
rpc FullUpload(SyncAuth) returns (generic.Empty);
rpc FullDownload(SyncAuth) returns (generic.Empty);
rpc SyncServerMethod(SyncServerMethodRequest) returns (generic.Json);
}
service ConfigService {
rpc GetConfigJson(generic.String) returns (generic.Json);
rpc SetConfigJson(SetConfigJsonRequest) returns (collection.OpChanges);
rpc SetConfigJsonNoUndo(SetConfigJsonRequest) returns (generic.Empty);
rpc RemoveConfig(generic.String) returns (collection.OpChanges);
rpc GetAllConfig(generic.Empty) returns (generic.Json);
rpc GetConfigBool(Config.Bool) returns (generic.Bool);
rpc SetConfigBool(SetConfigBoolRequest) returns (collection.OpChanges);
rpc GetConfigString(Config.String) returns (generic.String);
rpc SetConfigString(SetConfigStringRequest) returns (collection.OpChanges);
rpc GetPreferences(generic.Empty) returns (Preferences);
rpc SetPreferences(Preferences) returns (collection.OpChanges);
}
service CardRenderingService { service CardRenderingService {
rpc ExtractAVTags(ExtractAVTagsRequest) returns (ExtractAVTagsResponse); rpc ExtractAVTags(ExtractAVTagsRequest) returns (ExtractAVTagsResponse);
rpc ExtractLatex(ExtractLatexRequest) returns (ExtractLatexResponse); rpc ExtractLatex(ExtractLatexRequest) returns (ExtractLatexResponse);
@ -121,19 +65,6 @@ service TagsService {
returns (collection.OpChangesWithCount); returns (collection.OpChangesWithCount);
} }
service SearchService {
rpc BuildSearchString(SearchNode) returns (generic.String);
rpc SearchCards(SearchRequest) returns (SearchResponse);
rpc SearchNotes(SearchRequest) returns (SearchResponse);
rpc JoinSearchNodes(JoinSearchNodesRequest) returns (generic.String);
rpc ReplaceSearchNode(ReplaceSearchNodeRequest) returns (generic.String);
rpc FindAndReplace(FindAndReplaceRequest)
returns (collection.OpChangesWithCount);
rpc AllBrowserColumns(generic.Empty) returns (BrowserColumns);
rpc BrowserRowForId(generic.Int64) returns (BrowserRow);
rpc SetActiveBrowserColumns(generic.StringList) returns (generic.Empty);
}
service StatsService { service StatsService {
rpc CardStats(cards.CardId) returns (generic.String); rpc CardStats(cards.CardId) returns (generic.String);
rpc Graphs(GraphsRequest) returns (GraphsResponse); rpc Graphs(GraphsRequest) returns (GraphsResponse);
@ -194,11 +125,6 @@ message BackendError {
// Messages // Messages
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
message SchedTimingTodayResponse {
uint32 days_elapsed = 1;
int64 next_day_at = 2;
}
message RenderExistingCardRequest { message RenderExistingCardRequest {
int64 card_id = 1; int64 card_id = 1;
bool browser = 2; bool browser = 2;
@ -295,118 +221,11 @@ message TrashMediaFilesRequest {
repeated string fnames = 1; repeated string fnames = 1;
} }
message StudiedTodayMessageRequest {
uint32 cards = 1;
double seconds = 2;
}
message CongratsLearnMessageRequest { message CongratsLearnMessageRequest {
float next_due = 1; float next_due = 1;
uint32 remaining = 2; uint32 remaining = 2;
} }
message SearchRequest {
string search = 1;
SortOrder order = 2;
}
message SearchResponse {
repeated int64 ids = 1;
}
message SortOrder {
message Builtin {
string column = 1;
bool reverse = 2;
}
oneof value {
generic.Empty none = 1;
string custom = 2;
Builtin builtin = 3;
}
}
message SearchNode {
message Dupe {
int64 notetype_id = 1;
string first_field = 2;
}
enum Flag {
FLAG_NONE = 0;
FLAG_ANY = 1;
FLAG_RED = 2;
FLAG_ORANGE = 3;
FLAG_GREEN = 4;
FLAG_BLUE = 5;
FLAG_PINK = 6;
FLAG_TURQUOISE = 7;
FLAG_PURPLE = 8;
}
enum Rating {
RATING_ANY = 0;
RATING_AGAIN = 1;
RATING_HARD = 2;
RATING_GOOD = 3;
RATING_EASY = 4;
RATING_BY_RESCHEDULE = 5;
}
message Rated {
uint32 days = 1;
Rating rating = 2;
}
enum CardState {
CARD_STATE_NEW = 0;
CARD_STATE_LEARN = 1;
CARD_STATE_REVIEW = 2;
CARD_STATE_DUE = 3;
CARD_STATE_SUSPENDED = 4;
CARD_STATE_BURIED = 5;
}
message IdList {
repeated int64 ids = 1;
}
message Group {
enum Joiner {
AND = 0;
OR = 1;
}
repeated SearchNode nodes = 1;
Joiner joiner = 2;
}
oneof filter {
Group group = 1;
SearchNode negated = 2;
string parsable_text = 3;
uint32 template = 4;
int64 nid = 5;
Dupe dupe = 6;
string field_name = 7;
Rated rated = 8;
uint32 added_in_days = 9;
int32 due_in_days = 10;
Flag flag = 11;
CardState card_state = 12;
IdList nids = 13;
uint32 edited_in_days = 14;
string deck = 15;
int32 due_on_day = 16;
string tag = 17;
string note = 18;
uint32 introduced_in_days = 19;
}
}
message JoinSearchNodesRequest {
SearchNode.Group.Joiner joiner = 1;
SearchNode existing_node = 2;
SearchNode additional_node = 3;
}
message ReplaceSearchNodeRequest {
SearchNode existing_node = 1;
SearchNode replacement_node = 2;
}
message SetTagCollapsedRequest { message SetTagCollapsedRequest {
string name = 1; string name = 1;
bool collapsed = 2; bool collapsed = 2;
@ -433,12 +252,6 @@ message RenameTagsRequest {
string new_prefix = 2; string new_prefix = 2;
} }
message SetConfigJsonRequest {
string key = 1;
bytes value_json = 2;
bool undoable = 3;
}
message EmptyCardsReport { message EmptyCardsReport {
message NoteWithEmptyCards { message NoteWithEmptyCards {
int64 note_id = 1; int64 note_id = 1;
@ -449,59 +262,6 @@ message EmptyCardsReport {
repeated NoteWithEmptyCards notes = 2; repeated NoteWithEmptyCards notes = 2;
} }
message FindAndReplaceRequest {
repeated int64 nids = 1;
string search = 2;
string replacement = 3;
bool regex = 4;
bool match_case = 5;
string field_name = 6;
}
message BrowserColumns {
enum Sorting {
SORTING_NONE = 0;
SORTING_NORMAL = 1;
SORTING_REVERSED = 2;
}
enum Alignment {
ALIGNMENT_START = 0;
ALIGNMENT_CENTER = 1;
}
message Column {
string key = 1;
string cards_mode_label = 2;
string notes_mode_label = 3;
Sorting sorting = 4;
bool uses_cell_font = 5;
Alignment alignment = 6;
}
repeated Column columns = 1;
}
message BrowserRow {
message Cell {
string text = 1;
bool is_rtl = 2;
}
enum Color {
COLOR_DEFAULT = 0;
COLOR_MARKED = 1;
COLOR_SUSPENDED = 2;
COLOR_FLAG_RED = 3;
COLOR_FLAG_ORANGE = 4;
COLOR_FLAG_GREEN = 5;
COLOR_FLAG_BLUE = 6;
COLOR_FLAG_PINK = 7;
COLOR_FLAG_TURQUOISE = 8;
COLOR_FLAG_PURPLE = 9;
}
repeated Cell cells = 1;
Color color = 2;
string font_name = 3;
uint32 font_size = 4;
}
message NoteIdsAndTagsRequest { message NoteIdsAndTagsRequest {
repeated int64 note_ids = 1; repeated int64 note_ids = 1;
string tags = 2; string tags = 2;
@ -515,117 +275,6 @@ message FindAndReplaceTagRequest {
bool match_case = 5; bool match_case = 5;
} }
message Preferences {
message Scheduling {
enum NewReviewMix {
DISTRIBUTE = 0;
REVIEWS_FIRST = 1;
NEW_FIRST = 2;
}
// read only; 1-3
uint32 scheduler_version = 1;
uint32 rollover = 2;
uint32 learn_ahead_secs = 3;
NewReviewMix new_review_mix = 4;
// v2 only
bool new_timezone = 5;
bool day_learn_first = 6;
}
message Reviewing {
bool hide_audio_play_buttons = 1;
bool interrupt_audio_when_answering = 2;
bool show_remaining_due_counts = 3;
bool show_intervals_on_buttons = 4;
uint32 time_limit_secs = 5;
}
message Editing {
bool adding_defaults_to_current_deck = 1;
bool paste_images_as_png = 2;
bool paste_strips_formatting = 3;
string default_search_text = 4;
}
Scheduling scheduling = 1;
Reviewing reviewing = 2;
Editing editing = 3;
}
message SyncLoginRequest {
string username = 1;
string password = 2;
}
message SyncStatusResponse {
enum Required {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
}
Required required = 1;
}
message SyncCollectionResponse {
enum ChangesRequired {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
// local collection has no cards; upload not an option
FULL_DOWNLOAD = 3;
// remote collection has no cards; download not an option
FULL_UPLOAD = 4;
}
uint32 host_number = 1;
string server_message = 2;
ChangesRequired required = 3;
}
message SyncAuth {
string hkey = 1;
uint32 host_number = 2;
}
message SyncServerMethodRequest {
enum Method {
HOST_KEY = 0;
META = 1;
START = 2;
APPLY_GRAVES = 3;
APPLY_CHANGES = 4;
CHUNK = 5;
APPLY_CHUNK = 6;
SANITY_CHECK = 7;
FINISH = 8;
ABORT = 9;
// caller must reopen after these two are called
FULL_UPLOAD = 10;
FULL_DOWNLOAD = 11;
}
Method method = 1;
bytes data = 2;
}
message UpdateStatsRequest {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 4;
int32 millisecond_delta = 5;
}
message ExtendLimitsRequest {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 3;
}
message CountsForDeckTodayResponse {
int32 new = 1;
int32 review = 2;
}
message GraphsRequest { message GraphsRequest {
string search = 1; string search = 1;
uint32 days = 2; uint32 days = 2;
@ -673,205 +322,7 @@ message RevlogEntry {
uint32 taken_millis = 8; uint32 taken_millis = 8;
ReviewKind review_kind = 9; ReviewKind review_kind = 9;
} }
message CongratsInfoResponse {
uint32 learn_remaining = 1;
uint32 secs_until_next_learn = 2;
bool review_remaining = 3;
bool new_remaining = 4;
bool have_sched_buried = 5;
bool have_user_buried = 6;
bool is_filtered_deck = 7;
bool bridge_commands_supported = 8;
string deck_description = 9;
}
message UnburyDeckRequest {
enum Mode {
ALL = 0;
SCHED_ONLY = 1;
USER_ONLY = 2;
}
int64 deck_id = 1;
Mode mode = 2;
}
message BuryOrSuspendCardsRequest {
enum Mode {
SUSPEND = 0;
BURY_SCHED = 1;
BURY_USER = 2;
}
repeated int64 card_ids = 1;
repeated int64 note_ids = 2;
Mode mode = 3;
}
message ScheduleCardsAsNewRequest {
repeated int64 card_ids = 1;
bool log = 2;
}
message SetDueDateRequest {
repeated int64 card_ids = 1;
string days = 2;
Config.String config_key = 3;
}
message SortCardsRequest {
repeated int64 card_ids = 1;
uint32 starting_from = 2;
uint32 step_size = 3;
bool randomize = 4;
bool shift_existing = 5;
}
message SortDeckRequest {
int64 deck_id = 1;
bool randomize = 2;
}
message Config {
message Bool {
enum Key {
BROWSER_TABLE_SHOW_NOTES_MODE = 0;
PREVIEW_BOTH_SIDES = 3;
COLLAPSE_TAGS = 4;
COLLAPSE_NOTETYPES = 5;
COLLAPSE_DECKS = 6;
COLLAPSE_SAVED_SEARCHES = 7;
COLLAPSE_TODAY = 8;
COLLAPSE_CARD_STATE = 9;
COLLAPSE_FLAGS = 10;
SCHED_2021 = 11;
ADDING_DEFAULTS_TO_CURRENT_DECK = 12;
HIDE_AUDIO_PLAY_BUTTONS = 13;
INTERRUPT_AUDIO_WHEN_ANSWERING = 14;
PASTE_IMAGES_AS_PNG = 15;
PASTE_STRIPS_FORMATTING = 16;
NORMALIZE_NOTE_TEXT = 17;
}
Key key = 1;
}
message String {
enum Key {
SET_DUE_BROWSER = 0;
SET_DUE_REVIEWER = 1;
DEFAULT_SEARCH_TEXT = 2;
CARD_STATE_CUSTOMIZER = 3;
}
Key key = 1;
}
}
message SetConfigBoolRequest {
Config.Bool.Key key = 1;
bool value = 2;
bool undoable = 3;
}
message SetConfigStringRequest {
Config.String.Key key = 1;
string value = 2;
bool undoable = 3;
}
message RenderMarkdownRequest { message RenderMarkdownRequest {
string markdown = 1; string markdown = 1;
bool sanitize = 2; bool sanitize = 2;
} }
message SchedulingState {
message New {
uint32 position = 1;
}
message Learning {
uint32 remaining_steps = 1;
uint32 scheduled_secs = 2;
}
message Review {
uint32 scheduled_days = 1;
uint32 elapsed_days = 2;
float ease_factor = 3;
uint32 lapses = 4;
bool leeched = 5;
}
message Relearning {
Review review = 1;
Learning learning = 2;
}
message Normal {
oneof value {
New new = 1;
Learning learning = 2;
Review review = 3;
Relearning relearning = 4;
}
}
message Preview {
uint32 scheduled_secs = 1;
bool finished = 2;
}
message ReschedulingFilter {
Normal original_state = 1;
}
message Filtered {
oneof value {
Preview preview = 1;
ReschedulingFilter rescheduling = 2;
}
}
oneof value {
Normal normal = 1;
Filtered filtered = 2;
}
}
message NextCardStates {
SchedulingState current = 1;
SchedulingState again = 2;
SchedulingState hard = 3;
SchedulingState good = 4;
SchedulingState easy = 5;
}
message CardAnswer {
enum Rating {
AGAIN = 0;
HARD = 1;
GOOD = 2;
EASY = 3;
}
int64 card_id = 1;
SchedulingState current_state = 2;
SchedulingState new_state = 3;
Rating rating = 4;
int64 answered_at_millis = 5;
uint32 milliseconds_taken = 6;
}
message GetQueuedCardsRequest {
uint32 fetch_limit = 1;
bool intraday_learning_only = 2;
}
message QueuedCards {
enum Queue {
NEW = 0;
LEARNING = 1;
REVIEW = 2;
}
message QueuedCard {
cards.Card card = 1;
Queue queue = 2;
NextCardStates next_states = 3;
}
repeated QueuedCard cards = 1;
uint32 new_count = 2;
uint32 learning_count = 3;
uint32 review_count = 4;
}

113
proto/anki/configs.proto Normal file
View file

@ -0,0 +1,113 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";
package anki.configs;
import "anki/generic.proto";
import "anki/collection.proto";
service ConfigsService {
rpc GetConfigJson(generic.String) returns (generic.Json);
rpc SetConfigJson(SetConfigJsonRequest) returns (collection.OpChanges);
rpc SetConfigJsonNoUndo(SetConfigJsonRequest) returns (generic.Empty);
rpc RemoveConfig(generic.String) returns (collection.OpChanges);
rpc GetAllConfig(generic.Empty) returns (generic.Json);
rpc GetConfigBool(Config.Bool) returns (generic.Bool);
rpc SetConfigBool(SetConfigBoolRequest) returns (collection.OpChanges);
rpc GetConfigString(Config.String) returns (generic.String);
rpc SetConfigString(SetConfigStringRequest) returns (collection.OpChanges);
rpc GetPreferences(generic.Empty) returns (Preferences);
rpc SetPreferences(Preferences) returns (collection.OpChanges);
}
message Config {
message Bool {
enum Key {
BROWSER_TABLE_SHOW_NOTES_MODE = 0;
PREVIEW_BOTH_SIDES = 3;
COLLAPSE_TAGS = 4;
COLLAPSE_NOTETYPES = 5;
COLLAPSE_DECKS = 6;
COLLAPSE_SAVED_SEARCHES = 7;
COLLAPSE_TODAY = 8;
COLLAPSE_CARD_STATE = 9;
COLLAPSE_FLAGS = 10;
SCHED_2021 = 11;
ADDING_DEFAULTS_TO_CURRENT_DECK = 12;
HIDE_AUDIO_PLAY_BUTTONS = 13;
INTERRUPT_AUDIO_WHEN_ANSWERING = 14;
PASTE_IMAGES_AS_PNG = 15;
PASTE_STRIPS_FORMATTING = 16;
NORMALIZE_NOTE_TEXT = 17;
}
Key key = 1;
}
message String {
enum Key {
SET_DUE_BROWSER = 0;
SET_DUE_REVIEWER = 1;
DEFAULT_SEARCH_TEXT = 2;
CARD_STATE_CUSTOMIZER = 3;
}
Key key = 1;
}
}
message SetConfigBoolRequest {
Config.Bool.Key key = 1;
bool value = 2;
bool undoable = 3;
}
message SetConfigStringRequest {
Config.String.Key key = 1;
string value = 2;
bool undoable = 3;
}
message SetConfigJsonRequest {
string key = 1;
bytes value_json = 2;
bool undoable = 3;
}
message Preferences {
message Scheduling {
enum NewReviewMix {
DISTRIBUTE = 0;
REVIEWS_FIRST = 1;
NEW_FIRST = 2;
}
// read only; 1-3
uint32 scheduler_version = 1;
uint32 rollover = 2;
uint32 learn_ahead_secs = 3;
NewReviewMix new_review_mix = 4;
// v2 only
bool new_timezone = 5;
bool day_learn_first = 6;
}
message Reviewing {
bool hide_audio_play_buttons = 1;
bool interrupt_audio_when_answering = 2;
bool show_remaining_due_counts = 3;
bool show_intervals_on_buttons = 4;
uint32 time_limit_secs = 5;
}
message Editing {
bool adding_defaults_to_current_deck = 1;
bool paste_images_as_png = 2;
bool paste_strips_formatting = 3;
string default_search_text = 4;
}
Scheduling scheduling = 1;
Reviewing reviewing = 2;
Editing editing = 3;
}

219
proto/anki/scheduler.proto Normal file
View file

@ -0,0 +1,219 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";
package anki.scheduler;
import "anki/generic.proto";
import "anki/cards.proto";
import "anki/decks.proto";
import "anki/collection.proto";
import "anki/configs.proto";
service SchedulerService {
rpc GetQueuedCards(GetQueuedCardsRequest) returns (QueuedCards);
rpc AnswerCard(CardAnswer) returns (collection.OpChanges);
rpc SchedTimingToday(generic.Empty) returns (SchedTimingTodayResponse);
rpc StudiedToday(generic.Empty) returns (generic.String);
rpc StudiedTodayMessage(StudiedTodayMessageRequest) returns (generic.String);
rpc UpdateStats(UpdateStatsRequest) returns (generic.Empty);
rpc ExtendLimits(ExtendLimitsRequest) returns (generic.Empty);
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(decks.DeckId) returns (collection.OpChanges);
rpc RebuildFilteredDeck(decks.DeckId) returns (collection.OpChangesWithCount);
rpc ScheduleCardsAsNew(ScheduleCardsAsNewRequest)
returns (collection.OpChanges);
rpc SetDueDate(SetDueDateRequest) returns (collection.OpChanges);
rpc SortCards(SortCardsRequest) returns (collection.OpChangesWithCount);
rpc SortDeck(SortDeckRequest) returns (collection.OpChangesWithCount);
rpc GetNextCardStates(cards.CardId) returns (NextCardStates);
rpc DescribeNextStates(NextCardStates) returns (generic.StringList);
rpc StateIsLeech(SchedulingState) returns (generic.Bool);
rpc UpgradeScheduler(generic.Empty) returns (generic.Empty);
}
message SchedulingState {
message New {
uint32 position = 1;
}
message Learning {
uint32 remaining_steps = 1;
uint32 scheduled_secs = 2;
}
message Review {
uint32 scheduled_days = 1;
uint32 elapsed_days = 2;
float ease_factor = 3;
uint32 lapses = 4;
bool leeched = 5;
}
message Relearning {
Review review = 1;
Learning learning = 2;
}
message Normal {
oneof value {
New new = 1;
Learning learning = 2;
Review review = 3;
Relearning relearning = 4;
}
}
message Preview {
uint32 scheduled_secs = 1;
bool finished = 2;
}
message ReschedulingFilter {
Normal original_state = 1;
}
message Filtered {
oneof value {
Preview preview = 1;
ReschedulingFilter rescheduling = 2;
}
}
oneof value {
Normal normal = 1;
Filtered filtered = 2;
}
}
message QueuedCards {
enum Queue {
NEW = 0;
LEARNING = 1;
REVIEW = 2;
}
message QueuedCard {
cards.Card card = 1;
Queue queue = 2;
NextCardStates next_states = 3;
}
repeated QueuedCard cards = 1;
uint32 new_count = 2;
uint32 learning_count = 3;
uint32 review_count = 4;
}
message GetQueuedCardsRequest {
uint32 fetch_limit = 1;
bool intraday_learning_only = 2;
}
message SchedTimingTodayResponse {
uint32 days_elapsed = 1;
int64 next_day_at = 2;
}
message StudiedTodayMessageRequest {
uint32 cards = 1;
double seconds = 2;
}
message UpdateStatsRequest {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 4;
int32 millisecond_delta = 5;
}
message ExtendLimitsRequest {
int64 deck_id = 1;
int32 new_delta = 2;
int32 review_delta = 3;
}
message CountsForDeckTodayResponse {
int32 new = 1;
int32 review = 2;
}
message CongratsInfoResponse {
uint32 learn_remaining = 1;
uint32 secs_until_next_learn = 2;
bool review_remaining = 3;
bool new_remaining = 4;
bool have_sched_buried = 5;
bool have_user_buried = 6;
bool is_filtered_deck = 7;
bool bridge_commands_supported = 8;
string deck_description = 9;
}
message UnburyDeckRequest {
enum Mode {
ALL = 0;
SCHED_ONLY = 1;
USER_ONLY = 2;
}
int64 deck_id = 1;
Mode mode = 2;
}
message BuryOrSuspendCardsRequest {
enum Mode {
SUSPEND = 0;
BURY_SCHED = 1;
BURY_USER = 2;
}
repeated int64 card_ids = 1;
repeated int64 note_ids = 2;
Mode mode = 3;
}
message ScheduleCardsAsNewRequest {
repeated int64 card_ids = 1;
bool log = 2;
}
message SetDueDateRequest {
repeated int64 card_ids = 1;
string days = 2;
configs.Config.String config_key = 3;
}
message SortCardsRequest {
repeated int64 card_ids = 1;
uint32 starting_from = 2;
uint32 step_size = 3;
bool randomize = 4;
bool shift_existing = 5;
}
message SortDeckRequest {
int64 deck_id = 1;
bool randomize = 2;
}
message NextCardStates {
SchedulingState current = 1;
SchedulingState again = 2;
SchedulingState hard = 3;
SchedulingState good = 4;
SchedulingState easy = 5;
}
message CardAnswer {
enum Rating {
AGAIN = 0;
HARD = 1;
GOOD = 2;
EASY = 3;
}
int64 card_id = 1;
SchedulingState current_state = 2;
SchedulingState new_state = 3;
Rating rating = 4;
int64 answered_at_millis = 5;
uint32 milliseconds_taken = 6;
}

177
proto/anki/search.proto Normal file
View file

@ -0,0 +1,177 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";
package anki.search;
import "anki/generic.proto";
import "anki/collection.proto";
service SearchService {
rpc BuildSearchString(SearchNode) returns (generic.String);
rpc SearchCards(SearchRequest) returns (SearchResponse);
rpc SearchNotes(SearchRequest) returns (SearchResponse);
rpc JoinSearchNodes(JoinSearchNodesRequest) returns (generic.String);
rpc ReplaceSearchNode(ReplaceSearchNodeRequest) returns (generic.String);
rpc FindAndReplace(FindAndReplaceRequest)
returns (collection.OpChangesWithCount);
rpc AllBrowserColumns(generic.Empty) returns (BrowserColumns);
rpc BrowserRowForId(generic.Int64) returns (BrowserRow);
rpc SetActiveBrowserColumns(generic.StringList) returns (generic.Empty);
}
message SearchNode {
message Dupe {
int64 notetype_id = 1;
string first_field = 2;
}
enum Flag {
FLAG_NONE = 0;
FLAG_ANY = 1;
FLAG_RED = 2;
FLAG_ORANGE = 3;
FLAG_GREEN = 4;
FLAG_BLUE = 5;
FLAG_PINK = 6;
FLAG_TURQUOISE = 7;
FLAG_PURPLE = 8;
}
enum Rating {
RATING_ANY = 0;
RATING_AGAIN = 1;
RATING_HARD = 2;
RATING_GOOD = 3;
RATING_EASY = 4;
RATING_BY_RESCHEDULE = 5;
}
message Rated {
uint32 days = 1;
Rating rating = 2;
}
enum CardState {
CARD_STATE_NEW = 0;
CARD_STATE_LEARN = 1;
CARD_STATE_REVIEW = 2;
CARD_STATE_DUE = 3;
CARD_STATE_SUSPENDED = 4;
CARD_STATE_BURIED = 5;
}
message IdList {
repeated int64 ids = 1;
}
message Group {
enum Joiner {
AND = 0;
OR = 1;
}
repeated SearchNode nodes = 1;
Joiner joiner = 2;
}
oneof filter {
Group group = 1;
SearchNode negated = 2;
string parsable_text = 3;
uint32 template = 4;
int64 nid = 5;
Dupe dupe = 6;
string field_name = 7;
Rated rated = 8;
uint32 added_in_days = 9;
int32 due_in_days = 10;
Flag flag = 11;
CardState card_state = 12;
IdList nids = 13;
uint32 edited_in_days = 14;
string deck = 15;
int32 due_on_day = 16;
string tag = 17;
string note = 18;
uint32 introduced_in_days = 19;
}
}
message SearchRequest {
string search = 1;
SortOrder order = 2;
}
message SearchResponse {
repeated int64 ids = 1;
}
message SortOrder {
message Builtin {
string column = 1;
bool reverse = 2;
}
oneof value {
generic.Empty none = 1;
string custom = 2;
Builtin builtin = 3;
}
}
message JoinSearchNodesRequest {
SearchNode.Group.Joiner joiner = 1;
SearchNode existing_node = 2;
SearchNode additional_node = 3;
}
message ReplaceSearchNodeRequest {
SearchNode existing_node = 1;
SearchNode replacement_node = 2;
}
message FindAndReplaceRequest {
repeated int64 nids = 1;
string search = 2;
string replacement = 3;
bool regex = 4;
bool match_case = 5;
string field_name = 6;
}
message BrowserColumns {
enum Sorting {
SORTING_NONE = 0;
SORTING_NORMAL = 1;
SORTING_REVERSED = 2;
}
enum Alignment {
ALIGNMENT_START = 0;
ALIGNMENT_CENTER = 1;
}
message Column {
string key = 1;
string cards_mode_label = 2;
string notes_mode_label = 3;
Sorting sorting = 4;
bool uses_cell_font = 5;
Alignment alignment = 6;
}
repeated Column columns = 1;
}
message BrowserRow {
message Cell {
string text = 1;
bool is_rtl = 2;
}
enum Color {
COLOR_DEFAULT = 0;
COLOR_MARKED = 1;
COLOR_SUSPENDED = 2;
COLOR_FLAG_RED = 3;
COLOR_FLAG_ORANGE = 4;
COLOR_FLAG_GREEN = 5;
COLOR_FLAG_BLUE = 6;
COLOR_FLAG_PINK = 7;
COLOR_FLAG_TURQUOISE = 8;
COLOR_FLAG_PURPLE = 9;
}
repeated Cell cells = 1;
Color color = 2;
string font_name = 3;
uint32 font_size = 4;
}

76
proto/anki/sync.proto Normal file
View file

@ -0,0 +1,76 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";
package anki.sync;
import "anki/generic.proto";
service SyncService {
rpc SyncMedia(SyncAuth) returns (generic.Empty);
rpc AbortSync(generic.Empty) returns (generic.Empty);
rpc AbortMediaSync(generic.Empty) returns (generic.Empty);
rpc BeforeUpload(generic.Empty) returns (generic.Empty);
rpc SyncLogin(SyncLoginRequest) returns (SyncAuth);
rpc SyncStatus(SyncAuth) returns (SyncStatusResponse);
rpc SyncCollection(SyncAuth) returns (SyncCollectionResponse);
rpc FullUpload(SyncAuth) returns (generic.Empty);
rpc FullDownload(SyncAuth) returns (generic.Empty);
rpc SyncServerMethod(SyncServerMethodRequest) returns (generic.Json);
}
message SyncAuth {
string hkey = 1;
uint32 host_number = 2;
}
message SyncLoginRequest {
string username = 1;
string password = 2;
}
message SyncStatusResponse {
enum Required {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
}
Required required = 1;
}
message SyncCollectionResponse {
enum ChangesRequired {
NO_CHANGES = 0;
NORMAL_SYNC = 1;
FULL_SYNC = 2;
// local collection has no cards; upload not an option
FULL_DOWNLOAD = 3;
// remote collection has no cards; download not an option
FULL_UPLOAD = 4;
}
uint32 host_number = 1;
string server_message = 2;
ChangesRequired required = 3;
}
message SyncServerMethodRequest {
enum Method {
HOST_KEY = 0;
META = 1;
START = 2;
APPLY_GRAVES = 3;
APPLY_CHANGES = 4;
CHUNK = 5;
APPLY_CHUNK = 6;
SANITY_CHECK = 7;
FINISH = 8;
ABORT = 9;
// caller must reopen after these two are called
FULL_UPLOAD = 10;
FULL_DOWNLOAD = 11;
}
Method method = 1;
bytes data = 2;
}

View file

@ -16,6 +16,10 @@ import anki.decks_pb2
import anki.deckconfig_pb2 import anki.deckconfig_pb2
import anki.notes_pb2 import anki.notes_pb2
import anki.notetypes_pb2 import anki.notetypes_pb2
import anki.scheduler_pb2
import anki.sync_pb2
import anki.configs_pb2
import anki.search_pb2
import stringcase import stringcase
@ -179,6 +183,10 @@ service_modules = dict(
DECKS=anki.decks_pb2, DECKS=anki.decks_pb2,
DECK_CONFIG=anki.deckconfig_pb2, DECK_CONFIG=anki.deckconfig_pb2,
NOTETYPES=anki.notetypes_pb2, NOTETYPES=anki.notetypes_pb2,
SCHEDULER=anki.scheduler_pb2,
SYNC=anki.sync_pb2,
CONFIGS=anki.configs_pb2,
SEARCH=anki.search_pb2,
) )
for service in anki.backend_pb2.ServiceIndex.DESCRIPTOR.values: for service in anki.backend_pb2.ServiceIndex.DESCRIPTOR.values:

View file

@ -8,22 +8,22 @@ from __future__ import annotations
from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union, cast from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union, cast
import anki.backend_pb2 as _pb import anki.backend_pb2 as _pb
from anki import collection_pb2 from anki import collection_pb2, configs_pb2, generic_pb2, search_pb2
from anki._legacy import DeprecatedNamesMixin, deprecated from anki._legacy import DeprecatedNamesMixin, deprecated
# protobuf we publicly export - listed first to avoid circular imports # protobuf we publicly export - listed first to avoid circular imports
SearchNode = _pb.SearchNode SearchNode = search_pb2.SearchNode
Progress = collection_pb2.Progress Progress = collection_pb2.Progress
EmptyCardsReport = _pb.EmptyCardsReport EmptyCardsReport = _pb.EmptyCardsReport
GraphPreferences = _pb.GraphPreferences GraphPreferences = _pb.GraphPreferences
Preferences = _pb.Preferences Preferences = configs_pb2.Preferences
UndoStatus = collection_pb2.UndoStatus UndoStatus = collection_pb2.UndoStatus
OpChanges = collection_pb2.OpChanges OpChanges = collection_pb2.OpChanges
OpChangesWithCount = collection_pb2.OpChangesWithCount OpChangesWithCount = collection_pb2.OpChangesWithCount
OpChangesWithId = collection_pb2.OpChangesWithId OpChangesWithId = collection_pb2.OpChangesWithId
OpChangesAfterUndo = collection_pb2.OpChangesAfterUndo OpChangesAfterUndo = collection_pb2.OpChangesAfterUndo
BrowserRow = _pb.BrowserRow BrowserRow = search_pb2.BrowserRow
BrowserColumns = _pb.BrowserColumns BrowserColumns = search_pb2.BrowserColumns
import copy import copy
import os import os
@ -34,7 +34,7 @@ import weakref
from dataclasses import dataclass, field from dataclasses import dataclass, field
import anki.latex import anki.latex
from anki import generic_pb2, hooks from anki import hooks
from anki._backend import RustBackend, Translations from anki._backend import RustBackend, Translations
from anki.browser import BrowserConfig, BrowserDefaults from anki.browser import BrowserConfig, BrowserDefaults
from anki.cards import Card, CardId from anki.cards import Card, CardId
@ -486,12 +486,12 @@ class Collection(DeprecatedNamesMixin):
order: Union[bool, str, BrowserColumns.Column], order: Union[bool, str, BrowserColumns.Column],
reverse: bool, reverse: bool,
finding_notes: bool, finding_notes: bool,
) -> _pb.SortOrder: ) -> search_pb2.SortOrder:
if isinstance(order, str): if isinstance(order, str):
return _pb.SortOrder(custom=order) return search_pb2.SortOrder(custom=order)
if isinstance(order, bool): if isinstance(order, bool):
if order is False: if order is False:
return _pb.SortOrder(none=generic_pb2.Empty()) return search_pb2.SortOrder(none=generic_pb2.Empty())
# order=True: set args to sort column and reverse from config # order=True: set args to sort column and reverse from config
sort_key = BrowserConfig.sort_column_key(finding_notes) sort_key = BrowserConfig.sort_column_key(finding_notes)
order = self.get_browser_column(self.get_config(sort_key)) order = self.get_browser_column(self.get_config(sort_key))
@ -499,13 +499,15 @@ class Collection(DeprecatedNamesMixin):
reverse = self.get_config(reverse_key) reverse = self.get_config(reverse_key)
if isinstance(order, BrowserColumns.Column): if isinstance(order, BrowserColumns.Column):
if order.sorting != BrowserColumns.SORTING_NONE: if order.sorting != BrowserColumns.SORTING_NONE:
return _pb.SortOrder( return search_pb2.SortOrder(
builtin=_pb.SortOrder.Builtin(column=order.key, reverse=reverse) builtin=search_pb2.SortOrder.Builtin(
column=order.key, reverse=reverse
)
) )
# eg, user is ordering on an add-on field with the add-on not installed # eg, user is ordering on an add-on field with the add-on not installed
print(f"{order} is not a valid sort order.") print(f"{order} is not a valid sort order.")
return _pb.SortOrder(none=generic_pb2.Empty()) return search_pb2.SortOrder(none=generic_pb2.Empty())
def find_and_replace( def find_and_replace(
self, self,

View file

@ -25,12 +25,12 @@ from typing import Any
from weakref import ref from weakref import ref
import anki import anki
from anki import backend_pb2 as _pb from anki import configs_pb2
from anki.collection import OpChanges from anki.collection import OpChanges
from anki.errors import NotFoundError from anki.errors import NotFoundError
from anki.utils import from_json_bytes, to_json_bytes from anki.utils import from_json_bytes, to_json_bytes
Config = _pb.Config Config = configs_pb2.Config
class ConfigManager: class ConfigManager:

1
pylib/anki/configs_pb2.pyi Symbolic link
View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/configs_pb2.pyi

View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/deckconfig_pb2.pyi

1
pylib/anki/decks_pb2.pyi Symbolic link
View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/decks_pb2.pyi

1
pylib/anki/notes_pb2.pyi Symbolic link
View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/notes_pb2.pyi

View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/notetypes_pb2.pyi

View file

@ -4,12 +4,15 @@
from __future__ import annotations from __future__ import annotations
import anki import anki
import anki.backend_pb2 as _pb from anki import decks_pb2, scheduler_pb2
from anki import decks_pb2
from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId
from anki.config import Config from anki.config import Config
SchedTimingToday = _pb.SchedTimingTodayResponse SchedTimingToday = scheduler_pb2.SchedTimingTodayResponse
CongratsInfo = scheduler_pb2.CongratsInfoResponse
UnburyDeck = scheduler_pb2.UnburyDeckRequest
BuryOrSuspend = scheduler_pb2.BuryOrSuspendCardsRequest
FilteredDeckForUpdate = decks_pb2.FilteredDeckForUpdate
from typing import List, Optional, Sequence from typing import List, Optional, Sequence
@ -20,11 +23,6 @@ from anki.decks import DeckConfigDict, DeckId, DeckTreeNode
from anki.notes import NoteId from anki.notes import NoteId
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
CongratsInfo = _pb.CongratsInfoResponse
UnburyDeck = _pb.UnburyDeckRequest
BuryOrSuspend = _pb.BuryOrSuspendCardsRequest
FilteredDeckForUpdate = decks_pb2.FilteredDeckForUpdate
class SchedulerBase: class SchedulerBase:
"Actions shared between schedulers." "Actions shared between schedulers."

View file

@ -11,8 +11,7 @@ from heapq import *
from typing import Any, Callable, Dict, List, Optional, Tuple, Union from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
import anki.backend_pb2 as _pb from anki import hooks, scheduler_pb2
from anki import hooks
from anki.cards import Card, CardId from anki.cards import Card, CardId
from anki.consts import * from anki.consts import *
from anki.decks import DeckConfigDict, DeckDict, DeckId from anki.decks import DeckConfigDict, DeckDict, DeckId
@ -20,8 +19,8 @@ from anki.lang import FormatTimeSpan
from anki.scheduler.legacy import SchedulerBaseWithLegacy from anki.scheduler.legacy import SchedulerBaseWithLegacy
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
CountsForDeckToday = _pb.CountsForDeckTodayResponse CountsForDeckToday = scheduler_pb2.CountsForDeckTodayResponse
SchedTimingToday = _pb.SchedTimingTodayResponse SchedTimingToday = scheduler_pb2.SchedTimingTodayResponse
# legacy type alias # legacy type alias
QueueConfig = Dict[str, Any] QueueConfig = Dict[str, Any]

View file

@ -14,7 +14,7 @@ from __future__ import annotations
from typing import List, Literal, Sequence, Tuple from typing import List, Literal, Sequence, Tuple
import anki.backend_pb2 as _pb from anki import scheduler_pb2
from anki.cards import Card from anki.cards import Card
from anki.collection import OpChanges from anki.collection import OpChanges
from anki.consts import * from anki.consts import *
@ -24,10 +24,10 @@ from anki.scheduler.legacy import SchedulerBaseWithLegacy
from anki.types import assert_exhaustive from anki.types import assert_exhaustive
from anki.utils import intTime from anki.utils import intTime
QueuedCards = _pb.QueuedCards QueuedCards = scheduler_pb2.QueuedCards
SchedulingState = _pb.SchedulingState SchedulingState = scheduler_pb2.SchedulingState
NextStates = _pb.NextCardStates NextStates = scheduler_pb2.NextCardStates
CardAnswer = _pb.CardAnswer CardAnswer = scheduler_pb2.CardAnswer
class Scheduler(SchedulerBaseWithLegacy): class Scheduler(SchedulerBaseWithLegacy):
@ -171,7 +171,7 @@ class Scheduler(SchedulerBaseWithLegacy):
########################################################################## ##########################################################################
# fixme: move these into tests_schedv2 in the future # fixme: move these into tests_schedv2 in the future
def _interval_for_state(self, state: _pb.SchedulingState) -> int: def _interval_for_state(self, state: scheduler_pb2.SchedulingState) -> int:
kind = state.WhichOneof("value") kind = state.WhichOneof("value")
if kind == "normal": if kind == "normal":
return self._interval_for_normal_state(state.normal) return self._interval_for_normal_state(state.normal)
@ -181,7 +181,9 @@ class Scheduler(SchedulerBaseWithLegacy):
assert_exhaustive(kind) assert_exhaustive(kind)
return 0 # unreachable return 0 # unreachable
def _interval_for_normal_state(self, normal: _pb.SchedulingState.Normal) -> int: def _interval_for_normal_state(
self, normal: scheduler_pb2.SchedulingState.Normal
) -> int:
kind = normal.WhichOneof("value") kind = normal.WhichOneof("value")
if kind == "new": if kind == "new":
return 0 return 0
@ -196,7 +198,7 @@ class Scheduler(SchedulerBaseWithLegacy):
return 0 # unreachable return 0 # unreachable
def _interval_for_filtered_state( def _interval_for_filtered_state(
self, filtered: _pb.SchedulingState.Filtered self, filtered: scheduler_pb2.SchedulingState.Filtered
) -> int: ) -> int:
kind = filtered.WhichOneof("value") kind = filtered.WhichOneof("value")
if kind == "preview": if kind == "preview":

View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/scheduler_pb2.pyi

1
pylib/anki/search_pb2.pyi Symbolic link
View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/search_pb2.pyi

View file

@ -1,12 +1,12 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import anki.backend_pb2 as _pb from anki import sync_pb2
# public exports # public exports
SyncAuth = _pb.SyncAuth SyncAuth = sync_pb2.SyncAuth
SyncOutput = _pb.SyncCollectionResponse SyncOutput = sync_pb2.SyncCollectionResponse
SyncStatus = _pb.SyncStatusResponse SyncStatus = sync_pb2.SyncStatusResponse
# Legacy attributes some add-ons may be using # Legacy attributes some add-ons may be using

1
pylib/anki/sync_pb2.pyi Symbolic link
View file

@ -0,0 +1 @@
../../bazel-bin/pylib/anki/sync_pb2.pyi

View file

@ -27,7 +27,7 @@ except ImportError as e:
from flask import Response from flask import Response
from anki import Collection from anki import Collection
from anki.backend_pb2 import SyncServerMethodRequest from anki.sync_pb2 import SyncServerMethodRequest
Method = SyncServerMethodRequest.Method # pylint: disable=no-member Method = SyncServerMethodRequest.Method # pylint: disable=no-member

View file

@ -4,7 +4,7 @@
use serde_json::Value; use serde_json::Value;
use super::Backend; use super::Backend;
pub(super) use crate::backend_proto::config_service::Service as ConfigService; pub(super) use crate::backend_proto::configs_service::Service as ConfigsService;
use crate::{ use crate::{
backend_proto as pb, backend_proto as pb,
backend_proto::config::{bool::Key as BoolKeyProto, string::Key as StringKeyProto}, backend_proto::config::{bool::Key as BoolKeyProto, string::Key as StringKeyProto},
@ -52,7 +52,7 @@ impl From<pb::config::String> for StringKey {
} }
} }
impl ConfigService for Backend { impl ConfigsService for Backend {
fn get_config_json(&self, input: pb::String) -> Result<pb::Json> { fn get_config_json(&self, input: pb::String) -> Result<pb::Json> {
self.with_col(|col| { self.with_col(|col| {
let val: Option<Value> = col.get_config_optional(input.val.as_str()); let val: Option<Value> = col.get_config_optional(input.val.as_str());

View file

@ -40,7 +40,7 @@ use self::{
card::CardsService, card::CardsService,
cardrendering::CardRenderingService, cardrendering::CardRenderingService,
collection::CollectionService, collection::CollectionService,
config::ConfigService, config::ConfigsService,
deckconfig::DeckConfigService, deckconfig::DeckConfigService,
decks::DecksService, decks::DecksService,
i18n::I18nService, i18n::I18nService,
@ -48,7 +48,7 @@ use self::{
notes::NotesService, notes::NotesService,
notetypes::NotetypesService, notetypes::NotetypesService,
progress::ProgressState, progress::ProgressState,
scheduler::SchedulingService, scheduler::SchedulerService,
search::SearchService, search::SearchService,
stats::StatsService, stats::StatsService,
sync::{SyncService, SyncState}, sync::{SyncService, SyncState},
@ -117,11 +117,11 @@ impl Backend {
pb::ServiceIndex::from_i32(service as i32) pb::ServiceIndex::from_i32(service as i32)
.ok_or_else(|| AnkiError::invalid_input("invalid service")) .ok_or_else(|| AnkiError::invalid_input("invalid service"))
.and_then(|service| match service { .and_then(|service| match service {
pb::ServiceIndex::Scheduling => SchedulingService::run_method(self, method, input), pb::ServiceIndex::Scheduler => SchedulerService::run_method(self, method, input),
pb::ServiceIndex::Decks => DecksService::run_method(self, method, input), pb::ServiceIndex::Decks => DecksService::run_method(self, method, input),
pb::ServiceIndex::Notes => NotesService::run_method(self, method, input), pb::ServiceIndex::Notes => NotesService::run_method(self, method, input),
pb::ServiceIndex::Notetypes => NotetypesService::run_method(self, method, input), pb::ServiceIndex::Notetypes => NotetypesService::run_method(self, method, input),
pb::ServiceIndex::Config => ConfigService::run_method(self, method, input), pb::ServiceIndex::Configs => ConfigsService::run_method(self, method, input),
pb::ServiceIndex::Sync => SyncService::run_method(self, method, input), pb::ServiceIndex::Sync => SyncService::run_method(self, method, input),
pb::ServiceIndex::Tags => TagsService::run_method(self, method, input), pb::ServiceIndex::Tags => TagsService::run_method(self, method, input),
pb::ServiceIndex::DeckConfig => DeckConfigService::run_method(self, method, input), pb::ServiceIndex::DeckConfig => DeckConfigService::run_method(self, method, input),

View file

@ -5,7 +5,7 @@ mod answering;
mod states; mod states;
use super::Backend; use super::Backend;
pub(super) use crate::backend_proto::scheduling_service::Service as SchedulingService; pub(super) use crate::backend_proto::scheduler_service::Service as SchedulerService;
use crate::{ use crate::{
backend_proto::{self as pb}, backend_proto::{self as pb},
prelude::*, prelude::*,
@ -16,7 +16,7 @@ use crate::{
stats::studied_today, stats::studied_today,
}; };
impl SchedulingService for Backend { impl SchedulerService for Backend {
/// This behaves like _updateCutoff() in older code - it also unburies at the start of /// This behaves like _updateCutoff() in older code - it also unburies at the start of
/// a new day. /// a new day.
fn sched_timing_today(&self, _input: pb::Empty) -> Result<pb::SchedTimingTodayResponse> { fn sched_timing_today(&self, _input: pb::Empty) -> Result<pb::SchedTimingTodayResponse> {

View file

@ -14,11 +14,15 @@ macro_rules! protobuf {
} }
protobuf!(backend); protobuf!(backend);
protobuf!(cards);
protobuf!(collection);
protobuf!(configs);
protobuf!(deckconfig);
protobuf!(decks);
protobuf!(generic);
protobuf!(i18n);
protobuf!(notes); protobuf!(notes);
protobuf!(notetypes); protobuf!(notetypes);
protobuf!(decks); protobuf!(scheduler);
protobuf!(deckconfig); protobuf!(search);
protobuf!(i18n); protobuf!(sync);
protobuf!(cards);
protobuf!(generic);
protobuf!(collection);

View file

@ -3,11 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import type { Backend } from "lib/proto"; import type { Scheduler } from "lib/proto";
import { buildNextLearnMsg } from "./lib"; import { buildNextLearnMsg } from "./lib";
import { bridgeLink } from "lib/bridgecommand"; import { bridgeLink } from "lib/bridgecommand";
export let info: Backend.CongratsInfoResponse; export let info: Scheduler.CongratsInfoResponse;
import * as tr from "lib/i18n"; import * as tr from "lib/i18n";
const congrats = tr.schedulingCongratulationsFinished(); const congrats = tr.schedulingCongratulationsFinished();

View file

@ -1,19 +1,19 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { Backend } from "lib/proto"; import { Scheduler } from "lib/proto";
import { postRequest } from "lib/postrequest"; import { postRequest } from "lib/postrequest";
import { naturalUnit, unitAmount, unitName } from "lib/time"; import { naturalUnit, unitAmount, unitName } from "lib/time";
import * as tr from "lib/i18n"; import * as tr from "lib/i18n";
export async function getCongratsInfo(): Promise<Backend.CongratsInfoResponse> { export async function getCongratsInfo(): Promise<Scheduler.CongratsInfoResponse> {
return Backend.CongratsInfoResponse.decode( return Scheduler.CongratsInfoResponse.decode(
await postRequest("/_anki/congratsInfo", "") await postRequest("/_anki/congratsInfo", "")
); );
} }
export function buildNextLearnMsg(info: Backend.CongratsInfoResponse): string { export function buildNextLearnMsg(info: Scheduler.CongratsInfoResponse): string {
const secsUntil = info.secsUntilNextLearn; const secsUntil = info.secsUntilNextLearn;
// next learning card not due (/ until tomorrow)? // next learning card not due (/ until tomorrow)?
if (secsUntil == 0 || secsUntil > 86_400) { if (secsUntil == 0 || secsUntil > 86_400) {

View file

@ -6,4 +6,5 @@ import Backend = anki.backend;
import Cards = anki.cards; import Cards = anki.cards;
import DeckConfig = anki.deckconfig; import DeckConfig = anki.deckconfig;
import Notetypes = anki.notetypes; import Notetypes = anki.notetypes;
export { Backend, Cards, DeckConfig, Notetypes }; import Scheduler = anki.scheduler;
export { Backend, Cards, DeckConfig, Notetypes, Scheduler };

View file

@ -1,26 +1,26 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { Backend } from "lib/proto"; import { Scheduler } from "lib/proto";
import { postRequest } from "lib/postrequest"; import { postRequest } from "lib/postrequest";
async function getNextStates(): Promise<Backend.NextCardStates> { async function getNextStates(): Promise<Scheduler.NextCardStates> {
return Backend.NextCardStates.decode( return Scheduler.NextCardStates.decode(
await postRequest("/_anki/nextCardStates", "") await postRequest("/_anki/nextCardStates", "")
); );
} }
async function setNextStates( async function setNextStates(
key: string, key: string,
states: Backend.NextCardStates states: Scheduler.NextCardStates
): Promise<void> { ): Promise<void> {
const data: Uint8Array = Backend.NextCardStates.encode(states).finish(); const data: Uint8Array = Scheduler.NextCardStates.encode(states).finish();
await postRequest("/_anki/setNextCardStates", data, { key }); await postRequest("/_anki/setNextCardStates", data, { key });
} }
export async function mutateNextCardStates( export async function mutateNextCardStates(
key: string, key: string,
mutator: (states: Backend.NextCardStates) => void mutator: (states: Scheduler.NextCardStates) => void
): Promise<void> { ): Promise<void> {
const states = await getNextStates(); const states = await getNextStates();
mutator(states); mutator(states);