mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 06:52:21 -04:00
Merge pull request #1286 from ankitects/proto
Split backend.proto into multiple files
This commit is contained in:
commit
0b9fbd9050
151 changed files with 2634 additions and 2208 deletions
|
@ -1,5 +1,5 @@
|
||||||
workspace(
|
workspace(
|
||||||
name = "net_ankiweb_anki",
|
name = "ankidesktop",
|
||||||
managed_directories = {"@npm": [
|
managed_directories = {"@npm": [
|
||||||
"ts/node_modules",
|
"ts/node_modules",
|
||||||
]},
|
]},
|
||||||
|
|
14
defs.bzl
14
defs.bzl
|
@ -1,10 +1,10 @@
|
||||||
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
|
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
|
||||||
load("@bazel_skylib//lib:versions.bzl", "versions")
|
load("@bazel_skylib//lib:versions.bzl", "versions")
|
||||||
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
|
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
|
||||||
load("@net_ankiweb_anki//cargo:crates.bzl", "raze_fetch_remote_crates")
|
load("@ankidesktop//cargo:crates.bzl", "raze_fetch_remote_crates")
|
||||||
load(":python.bzl", "setup_local_python")
|
load(":python.bzl", "setup_local_python")
|
||||||
load(":protobuf.bzl", "setup_protobuf_binary")
|
load("//proto:protobuf.bzl", "setup_protobuf_binary")
|
||||||
load("//rslib:clang_format.bzl", "setup_clang_format")
|
load("//proto:format.bzl", "setup_clang_format")
|
||||||
load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install")
|
load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install")
|
||||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
|
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
|
||||||
load("@com_github_ali5h_rules_pip//:defs.bzl", "pip_import")
|
load("@com_github_ali5h_rules_pip//:defs.bzl", "pip_import")
|
||||||
|
@ -35,7 +35,7 @@ def setup_deps():
|
||||||
|
|
||||||
pip_import(
|
pip_import(
|
||||||
name = "py_deps",
|
name = "py_deps",
|
||||||
requirements = "@net_ankiweb_anki//pip:requirements.txt",
|
requirements = "@ankidesktop//pip:requirements.txt",
|
||||||
python_runtime = "@python//:python",
|
python_runtime = "@python//:python",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,12 +44,12 @@ def setup_deps():
|
||||||
python_runtime = "@python//:python",
|
python_runtime = "@python//:python",
|
||||||
)
|
)
|
||||||
|
|
||||||
node_repositories(package_json = ["@net_ankiweb_anki//ts:package.json"])
|
node_repositories(package_json = ["@ankidesktop//ts:package.json"])
|
||||||
|
|
||||||
yarn_install(
|
yarn_install(
|
||||||
name = "npm",
|
name = "npm",
|
||||||
package_json = "@net_ankiweb_anki//ts:package.json",
|
package_json = "@ankidesktop//ts:package.json",
|
||||||
yarn_lock = "@net_ankiweb_anki//ts:yarn.lock",
|
yarn_lock = "@ankidesktop//ts:yarn.lock",
|
||||||
)
|
)
|
||||||
|
|
||||||
sass_repositories()
|
sass_repositories()
|
||||||
|
|
|
@ -74,14 +74,14 @@ index eff3d9df2..fb2e9f7fe 100644
|
||||||
python_runtime = "@python//:python",
|
python_runtime = "@python//:python",
|
||||||
)
|
)
|
||||||
|
|
||||||
- node_repositories(package_json = ["@net_ankiweb_anki//ts:package.json"])
|
- node_repositories(package_json = ["@ankidesktop//ts:package.json"])
|
||||||
+ native.local_repository(
|
+ native.local_repository(
|
||||||
+ name = "local_node",
|
+ name = "local_node",
|
||||||
+ path = "local_node",
|
+ path = "local_node",
|
||||||
+ )
|
+ )
|
||||||
+
|
+
|
||||||
+ node_repositories(
|
+ node_repositories(
|
||||||
+ package_json = ["@net_ankiweb_anki//ts:package.json"],
|
+ package_json = ["@ankidesktop//ts:package.json"],
|
||||||
+ vendored_node = "@local_node//:node",
|
+ vendored_node = "@local_node//:node",
|
||||||
+ )
|
+ )
|
||||||
|
|
||||||
|
|
0
proto/.top_level
Normal file
0
proto/.top_level
Normal file
32
proto/BUILD.bazel
Normal file
32
proto/BUILD.bazel
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
load("@rules_proto//proto:defs.bzl", "proto_library")
|
||||||
|
load("//proto:clang_format.bzl", "proto_format")
|
||||||
|
|
||||||
|
proto_format(
|
||||||
|
name = "format",
|
||||||
|
srcs = glob(["**/*.proto"]),
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
proto_library(
|
||||||
|
name = "backend_proto_lib",
|
||||||
|
srcs = glob(["**/*.proto"]),
|
||||||
|
# "" removes the "proto/" prefix
|
||||||
|
strip_import_prefix = "",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "proto",
|
||||||
|
srcs = glob(["**/*.proto"]),
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
exports_files([
|
||||||
|
# for external workspace use
|
||||||
|
"format.py",
|
||||||
|
# an empty file we use to get the root proto/ path in the Rust build
|
||||||
|
".top_level",
|
||||||
|
])
|
63
proto/anki/backend.proto
Normal file
63
proto/anki/backend.proto
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.backend;
|
||||||
|
|
||||||
|
/// while the protobuf descriptors expose the order services are defined in,
|
||||||
|
/// that information is not available in prost, so we define an enum to make
|
||||||
|
/// sure all clients agree on the service index
|
||||||
|
enum ServiceIndex {
|
||||||
|
SERVICE_INDEX_SCHEDULER = 0;
|
||||||
|
SERVICE_INDEX_DECKS = 1;
|
||||||
|
SERVICE_INDEX_NOTES = 2;
|
||||||
|
SERVICE_INDEX_SYNC = 3;
|
||||||
|
SERVICE_INDEX_NOTETYPES = 4;
|
||||||
|
SERVICE_INDEX_CONFIG = 5;
|
||||||
|
SERVICE_INDEX_CARD_RENDERING = 6;
|
||||||
|
SERVICE_INDEX_DECK_CONFIG = 7;
|
||||||
|
SERVICE_INDEX_TAGS = 8;
|
||||||
|
SERVICE_INDEX_SEARCH = 9;
|
||||||
|
SERVICE_INDEX_STATS = 10;
|
||||||
|
SERVICE_INDEX_MEDIA = 11;
|
||||||
|
SERVICE_INDEX_I18N = 12;
|
||||||
|
SERVICE_INDEX_COLLECTION = 13;
|
||||||
|
SERVICE_INDEX_CARDS = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BackendInit {
|
||||||
|
repeated string preferred_langs = 1;
|
||||||
|
string locale_folder_path = 2;
|
||||||
|
bool server = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message I18nBackendInit {
|
||||||
|
repeated string preferred_langs = 4;
|
||||||
|
string locale_folder_path = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BackendError {
|
||||||
|
enum Kind {
|
||||||
|
INVALID_INPUT = 0;
|
||||||
|
UNDO_EMPTY = 1;
|
||||||
|
INTERRUPTED = 2;
|
||||||
|
TEMPLATE_PARSE = 3;
|
||||||
|
IO_ERROR = 4;
|
||||||
|
DB_ERROR = 5;
|
||||||
|
NETWORK_ERROR = 6;
|
||||||
|
SYNC_AUTH_ERROR = 7;
|
||||||
|
SYNC_OTHER_ERROR = 8;
|
||||||
|
JSON_ERROR = 9;
|
||||||
|
PROTO_ERROR = 10;
|
||||||
|
NOT_FOUND_ERROR = 11;
|
||||||
|
EXISTS = 12;
|
||||||
|
FILTERED_DECK_ERROR = 13;
|
||||||
|
SEARCH_ERROR = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// localized error description suitable for displaying to the user
|
||||||
|
string localized = 1;
|
||||||
|
// the error subtype
|
||||||
|
Kind kind = 2;
|
||||||
|
}
|
119
proto/anki/card_rendering.proto
Normal file
119
proto/anki/card_rendering.proto
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.card_rendering;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
import "anki/notes.proto";
|
||||||
|
import "anki/notetypes.proto";
|
||||||
|
|
||||||
|
service CardRenderingService {
|
||||||
|
rpc ExtractAVTags(ExtractAVTagsRequest) returns (ExtractAVTagsResponse);
|
||||||
|
rpc ExtractLatex(ExtractLatexRequest) returns (ExtractLatexResponse);
|
||||||
|
rpc GetEmptyCards(generic.Empty) returns (EmptyCardsReport);
|
||||||
|
rpc RenderExistingCard(RenderExistingCardRequest)
|
||||||
|
returns (RenderCardResponse);
|
||||||
|
rpc RenderUncommittedCard(RenderUncommittedCardRequest)
|
||||||
|
returns (RenderCardResponse);
|
||||||
|
rpc RenderUncommittedCardLegacy(RenderUncommittedCardLegacyRequest)
|
||||||
|
returns (RenderCardResponse);
|
||||||
|
rpc StripAVTags(generic.String) returns (generic.String);
|
||||||
|
rpc RenderMarkdown(RenderMarkdownRequest) returns (generic.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtractAVTagsRequest {
|
||||||
|
string text = 1;
|
||||||
|
bool question_side = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtractAVTagsResponse {
|
||||||
|
string text = 1;
|
||||||
|
repeated AVTag av_tags = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AVTag {
|
||||||
|
oneof value {
|
||||||
|
string sound_or_video = 1;
|
||||||
|
TTSTag tts = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message TTSTag {
|
||||||
|
string field_text = 1;
|
||||||
|
string lang = 2;
|
||||||
|
repeated string voices = 3;
|
||||||
|
float speed = 4;
|
||||||
|
repeated string other_args = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtractLatexRequest {
|
||||||
|
string text = 1;
|
||||||
|
bool svg = 2;
|
||||||
|
bool expand_clozes = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtractLatexResponse {
|
||||||
|
string text = 1;
|
||||||
|
repeated ExtractedLatex latex = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtractedLatex {
|
||||||
|
string filename = 1;
|
||||||
|
string latex_body = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EmptyCardsReport {
|
||||||
|
message NoteWithEmptyCards {
|
||||||
|
int64 note_id = 1;
|
||||||
|
repeated int64 card_ids = 2;
|
||||||
|
bool will_delete_note = 3;
|
||||||
|
}
|
||||||
|
string report = 1;
|
||||||
|
repeated NoteWithEmptyCards notes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderExistingCardRequest {
|
||||||
|
int64 card_id = 1;
|
||||||
|
bool browser = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderUncommittedCardRequest {
|
||||||
|
notes.Note note = 1;
|
||||||
|
uint32 card_ord = 2;
|
||||||
|
notetypes.Notetype.Template template = 3;
|
||||||
|
bool fill_empty = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderUncommittedCardLegacyRequest {
|
||||||
|
notes.Note note = 1;
|
||||||
|
uint32 card_ord = 2;
|
||||||
|
bytes template = 3;
|
||||||
|
bool fill_empty = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderCardResponse {
|
||||||
|
repeated RenderedTemplateNode question_nodes = 1;
|
||||||
|
repeated RenderedTemplateNode answer_nodes = 2;
|
||||||
|
string css = 3;
|
||||||
|
bool latex_svg = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderedTemplateNode {
|
||||||
|
oneof value {
|
||||||
|
string text = 1;
|
||||||
|
RenderedTemplateReplacement replacement = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderedTemplateReplacement {
|
||||||
|
string field_name = 1;
|
||||||
|
string current_text = 2;
|
||||||
|
repeated string filters = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderMarkdownRequest {
|
||||||
|
string markdown = 1;
|
||||||
|
bool sanitize = 2;
|
||||||
|
}
|
64
proto/anki/cards.proto
Normal file
64
proto/anki/cards.proto
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.cards;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
import "anki/collection.proto";
|
||||||
|
|
||||||
|
service CardsService {
|
||||||
|
rpc GetCard(CardId) returns (Card);
|
||||||
|
rpc UpdateCard(UpdateCardRequest) returns (collection.OpChanges);
|
||||||
|
rpc RemoveCards(RemoveCardsRequest) returns (generic.Empty);
|
||||||
|
rpc SetDeck(SetDeckRequest) returns (collection.OpChangesWithCount);
|
||||||
|
rpc SetFlag(SetFlagRequest) returns (collection.OpChangesWithCount);
|
||||||
|
}
|
||||||
|
message CardId {
|
||||||
|
int64 cid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CardIds {
|
||||||
|
repeated int64 cids = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Card {
|
||||||
|
int64 id = 1;
|
||||||
|
int64 note_id = 2;
|
||||||
|
int64 deck_id = 3;
|
||||||
|
uint32 template_idx = 4;
|
||||||
|
int64 mtime_secs = 5;
|
||||||
|
sint32 usn = 6;
|
||||||
|
uint32 ctype = 7;
|
||||||
|
sint32 queue = 8;
|
||||||
|
sint32 due = 9;
|
||||||
|
uint32 interval = 10;
|
||||||
|
uint32 ease_factor = 11;
|
||||||
|
uint32 reps = 12;
|
||||||
|
uint32 lapses = 13;
|
||||||
|
uint32 remaining_steps = 14;
|
||||||
|
sint32 original_due = 15;
|
||||||
|
int64 original_deck_id = 16;
|
||||||
|
uint32 flags = 17;
|
||||||
|
string data = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateCardRequest {
|
||||||
|
Card card = 1;
|
||||||
|
bool skip_undo_entry = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveCardsRequest {
|
||||||
|
repeated int64 card_ids = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDeckRequest {
|
||||||
|
repeated int64 card_ids = 1;
|
||||||
|
int64 deck_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetFlagRequest {
|
||||||
|
repeated int64 card_ids = 1;
|
||||||
|
uint32 flag = 2;
|
||||||
|
}
|
111
proto/anki/collection.proto
Normal file
111
proto/anki/collection.proto
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.collection;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
|
||||||
|
service CollectionService {
|
||||||
|
rpc OpenCollection(OpenCollectionRequest) returns (generic.Empty);
|
||||||
|
rpc CloseCollection(CloseCollectionRequest) returns (generic.Empty);
|
||||||
|
rpc CheckDatabase(generic.Empty) returns (CheckDatabaseResponse);
|
||||||
|
rpc GetUndoStatus(generic.Empty) returns (UndoStatus);
|
||||||
|
rpc Undo(generic.Empty) returns (OpChangesAfterUndo);
|
||||||
|
rpc Redo(generic.Empty) returns (OpChangesAfterUndo);
|
||||||
|
rpc AddCustomUndoEntry(generic.String) returns (generic.UInt32);
|
||||||
|
rpc MergeUndoEntries(generic.UInt32) returns (OpChanges);
|
||||||
|
rpc LatestProgress(generic.Empty) returns (Progress);
|
||||||
|
rpc SetWantsAbort(generic.Empty) returns (generic.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message OpenCollectionRequest {
|
||||||
|
string collection_path = 1;
|
||||||
|
string media_folder_path = 2;
|
||||||
|
string media_db_path = 3;
|
||||||
|
string log_path = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CloseCollectionRequest {
|
||||||
|
bool downgrade_to_schema11 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CheckDatabaseResponse {
|
||||||
|
repeated string problems = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OpChanges {
|
||||||
|
bool card = 1;
|
||||||
|
bool note = 2;
|
||||||
|
bool deck = 3;
|
||||||
|
bool tag = 4;
|
||||||
|
bool notetype = 5;
|
||||||
|
bool config = 6;
|
||||||
|
bool deck_config = 11;
|
||||||
|
bool mtime = 12;
|
||||||
|
|
||||||
|
bool browser_table = 7;
|
||||||
|
bool browser_sidebar = 8;
|
||||||
|
// editor and displayed card in review screen
|
||||||
|
bool note_text = 9;
|
||||||
|
// whether to call .reset() and getCard()
|
||||||
|
bool study_queues = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OpChangesWithCount {
|
||||||
|
uint32 count = 1;
|
||||||
|
OpChanges changes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OpChangesWithId {
|
||||||
|
int64 id = 1;
|
||||||
|
OpChanges changes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UndoStatus {
|
||||||
|
string undo = 1;
|
||||||
|
string redo = 2;
|
||||||
|
uint32 last_step = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OpChangesAfterUndo {
|
||||||
|
OpChanges changes = 1;
|
||||||
|
string operation = 2;
|
||||||
|
int64 reverted_to_timestamp = 3;
|
||||||
|
UndoStatus new_status = 4;
|
||||||
|
uint32 counter = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Progress {
|
||||||
|
message MediaSync {
|
||||||
|
string checked = 1;
|
||||||
|
string added = 2;
|
||||||
|
string removed = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FullSync {
|
||||||
|
uint32 transferred = 1;
|
||||||
|
uint32 total = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NormalSync {
|
||||||
|
string stage = 1;
|
||||||
|
string added = 2;
|
||||||
|
string removed = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DatabaseCheck {
|
||||||
|
string stage = 1;
|
||||||
|
uint32 stage_total = 2;
|
||||||
|
uint32 stage_current = 3;
|
||||||
|
}
|
||||||
|
oneof value {
|
||||||
|
generic.Empty none = 1;
|
||||||
|
MediaSync media_sync = 2;
|
||||||
|
string media_check = 3;
|
||||||
|
FullSync full_sync = 4;
|
||||||
|
NormalSync normal_sync = 5;
|
||||||
|
DatabaseCheck database_check = 6;
|
||||||
|
}
|
||||||
|
}
|
118
proto/anki/config.proto
Normal file
118
proto/anki/config.proto
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.config;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
import "anki/collection.proto";
|
||||||
|
|
||||||
|
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(GetConfigBoolRequest) returns (generic.Bool);
|
||||||
|
rpc SetConfigBool(SetConfigBoolRequest) returns (collection.OpChanges);
|
||||||
|
rpc GetConfigString(GetConfigStringRequest) returns (generic.String);
|
||||||
|
rpc SetConfigString(SetConfigStringRequest) returns (collection.OpChanges);
|
||||||
|
rpc GetPreferences(generic.Empty) returns (Preferences);
|
||||||
|
rpc SetPreferences(Preferences) returns (collection.OpChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfigKey {
|
||||||
|
enum Bool {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
enum String {
|
||||||
|
SET_DUE_BROWSER = 0;
|
||||||
|
SET_DUE_REVIEWER = 1;
|
||||||
|
DEFAULT_SEARCH_TEXT = 2;
|
||||||
|
CARD_STATE_CUSTOMIZER = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetConfigBoolRequest {
|
||||||
|
ConfigKey.Bool key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetConfigBoolRequest {
|
||||||
|
ConfigKey.Bool key = 1;
|
||||||
|
bool value = 2;
|
||||||
|
bool undoable = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetConfigStringRequest {
|
||||||
|
ConfigKey.String key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetConfigStringRequest {
|
||||||
|
ConfigKey.String key = 1;
|
||||||
|
string value = 2;
|
||||||
|
bool undoable = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OptionalStringConfigKey {
|
||||||
|
ConfigKey.String key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
145
proto/anki/deckconfig.proto
Normal file
145
proto/anki/deckconfig.proto
Normal 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
188
proto/anki/decks.proto
Normal 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;
|
||||||
|
}
|
44
proto/anki/generic.proto
Normal file
44
proto/anki/generic.proto
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.generic;
|
||||||
|
|
||||||
|
message Empty {}
|
||||||
|
|
||||||
|
message OptionalInt32 {
|
||||||
|
sint32 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OptionalUInt32 {
|
||||||
|
uint32 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Int32 {
|
||||||
|
sint32 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UInt32 {
|
||||||
|
uint32 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Int64 {
|
||||||
|
int64 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message String {
|
||||||
|
string val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Json {
|
||||||
|
bytes json = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Bool {
|
||||||
|
bool val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StringList {
|
||||||
|
repeated string vals = 1;
|
||||||
|
}
|
42
proto/anki/i18n.proto
Normal file
42
proto/anki/i18n.proto
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.i18n;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
|
||||||
|
service I18nService {
|
||||||
|
rpc TranslateString(TranslateStringRequest) returns (generic.String);
|
||||||
|
rpc FormatTimespan(FormatTimespanRequest) returns (generic.String);
|
||||||
|
rpc I18nResources(I18nResourcesRequest) returns (generic.Json);
|
||||||
|
}
|
||||||
|
|
||||||
|
message TranslateStringRequest {
|
||||||
|
uint32 module_index = 1;
|
||||||
|
uint32 message_index = 2;
|
||||||
|
map<string, TranslateArgValue> args = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TranslateArgValue {
|
||||||
|
oneof value {
|
||||||
|
string str = 1;
|
||||||
|
double number = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message FormatTimespanRequest {
|
||||||
|
enum Context {
|
||||||
|
PRECISE = 0;
|
||||||
|
ANSWER_BUTTONS = 1;
|
||||||
|
INTERVALS = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float seconds = 1;
|
||||||
|
Context context = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message I18nResourcesRequest {
|
||||||
|
repeated string modules = 1;
|
||||||
|
}
|
32
proto/anki/media.proto
Normal file
32
proto/anki/media.proto
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.media;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
|
||||||
|
service MediaService {
|
||||||
|
rpc CheckMedia(generic.Empty) returns (CheckMediaResponse);
|
||||||
|
rpc TrashMediaFiles(TrashMediaFilesRequest) returns (generic.Empty);
|
||||||
|
rpc AddMediaFile(AddMediaFileRequest) returns (generic.String);
|
||||||
|
rpc EmptyTrash(generic.Empty) returns (generic.Empty);
|
||||||
|
rpc RestoreTrash(generic.Empty) returns (generic.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message CheckMediaResponse {
|
||||||
|
repeated string unused = 1;
|
||||||
|
repeated string missing = 2;
|
||||||
|
string report = 3;
|
||||||
|
bool have_trash = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TrashMediaFilesRequest {
|
||||||
|
repeated string fnames = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddMediaFileRequest {
|
||||||
|
string desired_name = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
106
proto/anki/notes.proto
Normal file
106
proto/anki/notes.proto
Normal 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
176
proto/anki/notetypes.proto
Normal 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;
|
||||||
|
}
|
219
proto/anki/scheduler.proto
Normal file
219
proto/anki/scheduler.proto
Normal 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/config.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;
|
||||||
|
config.OptionalStringConfigKey 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
177
proto/anki/search.proto
Normal 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;
|
||||||
|
}
|
64
proto/anki/stats.proto
Normal file
64
proto/anki/stats.proto
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.stats;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
import "anki/cards.proto";
|
||||||
|
|
||||||
|
service StatsService {
|
||||||
|
rpc CardStats(cards.CardId) returns (generic.String);
|
||||||
|
rpc Graphs(GraphsRequest) returns (GraphsResponse);
|
||||||
|
rpc GetGraphPreferences(generic.Empty) returns (GraphPreferences);
|
||||||
|
rpc SetGraphPreferences(GraphPreferences) returns (generic.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message GraphsRequest {
|
||||||
|
string search = 1;
|
||||||
|
uint32 days = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GraphsResponse {
|
||||||
|
repeated cards.Card cards = 1;
|
||||||
|
repeated RevlogEntry revlog = 2;
|
||||||
|
uint32 days_elapsed = 3;
|
||||||
|
// Based on rollover hour
|
||||||
|
uint32 next_day_at_secs = 4;
|
||||||
|
uint32 scheduler_version = 5;
|
||||||
|
/// Seconds to add to UTC timestamps to get local time.
|
||||||
|
int32 local_offset_secs = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GraphPreferences {
|
||||||
|
enum Weekday {
|
||||||
|
SUNDAY = 0;
|
||||||
|
MONDAY = 1;
|
||||||
|
FRIDAY = 5;
|
||||||
|
SATURDAY = 6;
|
||||||
|
}
|
||||||
|
Weekday calendar_first_day_of_week = 1;
|
||||||
|
bool card_counts_separate_inactive = 2;
|
||||||
|
bool browser_links_supported = 3;
|
||||||
|
bool future_due_show_backlog = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RevlogEntry {
|
||||||
|
enum ReviewKind {
|
||||||
|
LEARNING = 0;
|
||||||
|
REVIEW = 1;
|
||||||
|
RELEARNING = 2;
|
||||||
|
EARLY_REVIEW = 3;
|
||||||
|
MANUAL = 4;
|
||||||
|
}
|
||||||
|
int64 id = 1;
|
||||||
|
int64 cid = 2;
|
||||||
|
int32 usn = 3;
|
||||||
|
uint32 button_chosen = 4;
|
||||||
|
int32 interval = 5;
|
||||||
|
int32 last_interval = 6;
|
||||||
|
uint32 ease_factor = 7;
|
||||||
|
uint32 taken_millis = 8;
|
||||||
|
ReviewKind review_kind = 9;
|
||||||
|
}
|
76
proto/anki/sync.proto
Normal file
76
proto/anki/sync.proto
Normal 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;
|
||||||
|
}
|
60
proto/anki/tags.proto
Normal file
60
proto/anki/tags.proto
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package anki.tags;
|
||||||
|
|
||||||
|
import "anki/generic.proto";
|
||||||
|
import "anki/collection.proto";
|
||||||
|
|
||||||
|
service TagsService {
|
||||||
|
rpc ClearUnusedTags(generic.Empty) returns (collection.OpChangesWithCount);
|
||||||
|
rpc AllTags(generic.Empty) returns (generic.StringList);
|
||||||
|
rpc RemoveTags(generic.String) returns (collection.OpChangesWithCount);
|
||||||
|
rpc SetTagCollapsed(SetTagCollapsedRequest) returns (collection.OpChanges);
|
||||||
|
rpc TagTree(generic.Empty) returns (TagTreeNode);
|
||||||
|
rpc ReparentTags(ReparentTagsRequest) returns (collection.OpChangesWithCount);
|
||||||
|
rpc RenameTags(RenameTagsRequest) returns (collection.OpChangesWithCount);
|
||||||
|
rpc AddNoteTags(NoteIdsAndTagsRequest)
|
||||||
|
returns (collection.OpChangesWithCount);
|
||||||
|
rpc RemoveNoteTags(NoteIdsAndTagsRequest)
|
||||||
|
returns (collection.OpChangesWithCount);
|
||||||
|
rpc FindAndReplaceTag(FindAndReplaceTagRequest)
|
||||||
|
returns (collection.OpChangesWithCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetTagCollapsedRequest {
|
||||||
|
string name = 1;
|
||||||
|
bool collapsed = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TagTreeNode {
|
||||||
|
string name = 1;
|
||||||
|
repeated TagTreeNode children = 2;
|
||||||
|
uint32 level = 3;
|
||||||
|
bool collapsed = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReparentTagsRequest {
|
||||||
|
repeated string tags = 1;
|
||||||
|
string new_parent = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenameTagsRequest {
|
||||||
|
string current_prefix = 1;
|
||||||
|
string new_prefix = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NoteIdsAndTagsRequest {
|
||||||
|
repeated int64 note_ids = 1;
|
||||||
|
string tags = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FindAndReplaceTagRequest {
|
||||||
|
repeated int64 note_ids = 1;
|
||||||
|
string search = 2;
|
||||||
|
string replacement = 3;
|
||||||
|
bool regex = 4;
|
||||||
|
bool match_case = 5;
|
||||||
|
}
|
|
@ -14,9 +14,9 @@ def _impl(rctx):
|
||||||
alias(
|
alias(
|
||||||
name = "clang_format",
|
name = "clang_format",
|
||||||
actual = select({
|
actual = select({
|
||||||
"@net_ankiweb_anki//platforms:windows_x86_64": "@clang_format_windows_x86_64//:clang-format.exe",
|
"@ankidesktop//platforms:windows_x86_64": "@clang_format_windows_x86_64//:clang-format.exe",
|
||||||
"@net_ankiweb_anki//platforms:macos_x86_64": "@clang_format_macos_x86_64//:clang-format",
|
"@ankidesktop//platforms:macos_x86_64": "@clang_format_macos_x86_64//:clang-format",
|
||||||
"@net_ankiweb_anki//platforms:linux_x86_64": "@clang_format_linux_x86_64//:clang-format",
|
"@ankidesktop//platforms:linux_x86_64": "@clang_format_linux_x86_64//:clang-format",
|
||||||
}),
|
}),
|
||||||
visibility = ["//visibility:public"]
|
visibility = ["//visibility:public"]
|
||||||
)
|
)
|
||||||
|
@ -68,7 +68,7 @@ def proto_format(name, srcs, **kwargs):
|
||||||
py_test(
|
py_test(
|
||||||
name = name,
|
name = name,
|
||||||
srcs = [
|
srcs = [
|
||||||
"proto_format.py",
|
"@ankidesktop//proto:format.py",
|
||||||
],
|
],
|
||||||
data = ["@clang_format//:clang_format"] + srcs,
|
data = ["@clang_format//:clang_format"] + srcs,
|
||||||
args = ["$(location @clang_format//:clang_format)"] + [native.package_name() + "/" + f for f in srcs],
|
args = ["$(location @clang_format//:clang_format)"] + [native.package_name() + "/" + f for f in srcs],
|
76
proto/format.bzl
Normal file
76
proto/format.bzl
Normal 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
|
||||||
|
|
||||||
|
"""
|
||||||
|
Exposes a clang-format binary for formatting protobuf.
|
||||||
|
"""
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
|
||||||
|
load("@rules_python//python:defs.bzl", "py_test")
|
||||||
|
|
||||||
|
def _impl(rctx):
|
||||||
|
rctx.file("BUILD.bazel", """
|
||||||
|
alias(
|
||||||
|
name = "clang_format",
|
||||||
|
actual = select({
|
||||||
|
"@ankidesktop//platforms:windows_x86_64": "@clang_format_windows_x86_64//:clang-format.exe",
|
||||||
|
"@ankidesktop//platforms:macos_x86_64": "@clang_format_macos_x86_64//:clang-format",
|
||||||
|
"@ankidesktop//platforms:linux_x86_64": "@clang_format_linux_x86_64//:clang-format",
|
||||||
|
}),
|
||||||
|
visibility = ["//visibility:public"]
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
_setup_clang_format = repository_rule(
|
||||||
|
attrs = {},
|
||||||
|
local = True,
|
||||||
|
implementation = _impl,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_clang_format(name):
|
||||||
|
maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "clang_format_macos_x86_64",
|
||||||
|
build_file_content = """exports_files(["clang-format"])""",
|
||||||
|
sha256 = "238be68d9478163a945754f06a213483473044f5a004c4125d3d9d8d3556466e",
|
||||||
|
urls = [
|
||||||
|
"https://github.com/ankitects/clang-format-binaries/releases/download/anki-2021-01-09/clang-format_macos_x86_64.zip",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "clang_format_linux_x86_64",
|
||||||
|
build_file_content = """exports_files(["clang-format"])""",
|
||||||
|
sha256 = "64060bc4dbca30d0d96aab9344e2783008b16e1cae019a2532f1126ca5ec5449",
|
||||||
|
urls = [
|
||||||
|
"https://github.com/ankitects/clang-format-binaries/releases/download/anki-2021-01-09/clang-format_linux_x86_64.zip",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "clang_format_windows_x86_64",
|
||||||
|
build_file_content = """exports_files(["clang-format.exe"])""",
|
||||||
|
sha256 = "7d9f6915e3f0fb72407830f0fc37141308d2e6915daba72987a52f309fbeaccc",
|
||||||
|
urls = [
|
||||||
|
"https://github.com/ankitects/clang-format-binaries/releases/download/anki-2021-01-09/clang-format_windows_x86_64.zip",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not native.existing_rule(name):
|
||||||
|
_setup_clang_format(
|
||||||
|
name = name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def proto_format(name, srcs, **kwargs):
|
||||||
|
py_test(
|
||||||
|
name = name,
|
||||||
|
srcs = [
|
||||||
|
"@ankidesktop//format.py",
|
||||||
|
],
|
||||||
|
data = ["@clang_format//:clang_format"] + srcs,
|
||||||
|
args = ["$(location @clang_format//:clang_format)"] + [native.package_name() + "/" + f for f in srcs],
|
||||||
|
**kwargs
|
||||||
|
)
|
|
@ -12,10 +12,10 @@ def _impl(rctx):
|
||||||
alias(
|
alias(
|
||||||
name = "protoc",
|
name = "protoc",
|
||||||
actual = select({
|
actual = select({
|
||||||
"@net_ankiweb_anki//platforms:windows_x86_64": "@protoc_bin_windows//:bin/protoc.exe",
|
"@ankidesktop//platforms:windows_x86_64": "@protoc_bin_windows//:bin/protoc.exe",
|
||||||
"@net_ankiweb_anki//platforms:macos_x86_64": "@protoc_bin_macos//:bin/protoc",
|
"@ankidesktop//platforms:macos_x86_64": "@protoc_bin_macos//:bin/protoc",
|
||||||
"@net_ankiweb_anki//platforms:linux_x86_64": "@protoc_bin_linux_x86_64//:bin/protoc",
|
"@ankidesktop//platforms:linux_x86_64": "@protoc_bin_linux_x86_64//:bin/protoc",
|
||||||
"@net_ankiweb_anki//platforms:linux_arm64": "@protoc_bin_linux_arm64//:bin/protoc"
|
"@ankidesktop//platforms:linux_arm64": "@protoc_bin_linux_arm64//:bin/protoc"
|
||||||
}),
|
}),
|
||||||
visibility = ["//visibility:public"]
|
visibility = ["//visibility:public"]
|
||||||
)
|
)
|
|
@ -14,6 +14,7 @@ ignored-classes=
|
||||||
NoteFieldsCheckResponse,
|
NoteFieldsCheckResponse,
|
||||||
BackendError,
|
BackendError,
|
||||||
SetDeckCollapsedRequest,
|
SetDeckCollapsedRequest,
|
||||||
|
ConfigKey,
|
||||||
|
|
||||||
[REPORTS]
|
[REPORTS]
|
||||||
output-format=colorized
|
output-format=colorized
|
||||||
|
|
|
@ -31,6 +31,7 @@ py_test(
|
||||||
main = "tests/run_mypy.py",
|
main = "tests/run_mypy.py",
|
||||||
deps = [
|
deps = [
|
||||||
"//pylib/anki",
|
"//pylib/anki",
|
||||||
|
"//pylib/anki:proto",
|
||||||
requirement("mypy"),
|
requirement("mypy"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ load("@py_deps//:requirements.bzl", "requirement")
|
||||||
load("@rules_python//experimental/python:wheel.bzl", "py_package", "py_wheel")
|
load("@rules_python//experimental/python:wheel.bzl", "py_package", "py_wheel")
|
||||||
load("//:defs.bzl", "anki_version")
|
load("//:defs.bzl", "anki_version")
|
||||||
load("//pylib:orjson.bzl", "orjson_if_available")
|
load("//pylib:orjson.bzl", "orjson_if_available")
|
||||||
|
load("//pylib:protobuf.bzl", "py_proto")
|
||||||
|
|
||||||
copy_file(
|
copy_file(
|
||||||
name = "buildinfo",
|
name = "buildinfo",
|
||||||
|
@ -39,6 +40,7 @@ py_library(
|
||||||
],
|
],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":proto",
|
||||||
requirement("beautifulsoup4"),
|
requirement("beautifulsoup4"),
|
||||||
requirement("decorator"),
|
requirement("decorator"),
|
||||||
requirement("distro"),
|
requirement("distro"),
|
||||||
|
@ -105,3 +107,24 @@ filegroup(
|
||||||
"//pylib:__subpackages__",
|
"//pylib:__subpackages__",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
py_proto(
|
||||||
|
name = "proto_py",
|
||||||
|
srcs = ["//proto"],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
py_library(
|
||||||
|
name = "proto",
|
||||||
|
srcs = [
|
||||||
|
"__init__.py",
|
||||||
|
# includes the .py files
|
||||||
|
":proto_py",
|
||||||
|
],
|
||||||
|
# includes the .pyi files
|
||||||
|
data = [":proto_py", "py.typed"],
|
||||||
|
imports = [".."],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
|
@ -1,18 +1,2 @@
|
||||||
# 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 sys
|
|
||||||
|
|
||||||
from anki.buildinfo import version
|
|
||||||
from anki.collection import Collection
|
|
||||||
|
|
||||||
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
|
|
||||||
raise Exception("Anki requires Python 3.7+")
|
|
||||||
|
|
||||||
# ensure unicode filenames are supported
|
|
||||||
try:
|
|
||||||
"テスト".encode(sys.getfilesystemencoding())
|
|
||||||
except UnicodeEncodeError as exc:
|
|
||||||
raise Exception("Anki requires a UTF-8 locale.") from exc
|
|
||||||
|
|
||||||
__all__ = ["Collection"]
|
|
||||||
|
|
|
@ -1,27 +1,18 @@
|
||||||
load("@rules_python//python:defs.bzl", "py_binary")
|
load("@rules_python//python:defs.bzl", "py_binary")
|
||||||
load("@py_deps//:requirements.bzl", "requirement")
|
load("@py_deps//:requirements.bzl", "requirement")
|
||||||
load("//pylib:protobuf.bzl", "py_proto_library_typed")
|
|
||||||
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
|
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
|
||||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||||
|
|
||||||
py_proto_library_typed(
|
|
||||||
name = "backend_pb2",
|
|
||||||
src = "//rslib:backend.proto",
|
|
||||||
visibility = [
|
|
||||||
"//visibility:public",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
py_binary(
|
py_binary(
|
||||||
name = "genbackend",
|
name = "genbackend",
|
||||||
srcs = [
|
srcs = [
|
||||||
"backend_pb2",
|
|
||||||
"genbackend.py",
|
"genbackend.py",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
requirement("black"),
|
requirement("black"),
|
||||||
requirement("stringcase"),
|
requirement("stringcase"),
|
||||||
requirement("protobuf"),
|
requirement("protobuf"),
|
||||||
|
"//pylib/anki:proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,7 +85,6 @@ filegroup(
|
||||||
srcs = [
|
srcs = [
|
||||||
"__init__.py",
|
"__init__.py",
|
||||||
"rsbridge.pyi",
|
"rsbridge.pyi",
|
||||||
":backend_pb2",
|
|
||||||
":fluent_gen",
|
":fluent_gen",
|
||||||
":rsbackend_gen",
|
":rsbackend_gen",
|
||||||
":rsbridge",
|
":rsbridge",
|
||||||
|
|
|
@ -11,6 +11,8 @@ from weakref import ref
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
|
|
||||||
import anki.buildinfo
|
import anki.buildinfo
|
||||||
|
import anki.lang
|
||||||
|
from anki import backend_pb2, i18n_pb2
|
||||||
from anki._backend.generated import RustBackendGenerated
|
from anki._backend.generated import RustBackendGenerated
|
||||||
from anki.dbproxy import Row as DBRow
|
from anki.dbproxy import Row as DBRow
|
||||||
from anki.dbproxy import ValueForDB
|
from anki.dbproxy import ValueForDB
|
||||||
|
@ -32,7 +34,6 @@ from ..errors import (
|
||||||
TemplateError,
|
TemplateError,
|
||||||
UndoEmpty,
|
UndoEmpty,
|
||||||
)
|
)
|
||||||
from . import backend_pb2 as pb
|
|
||||||
from . import rsbridge
|
from . import rsbridge
|
||||||
from .fluent import GeneratedTranslations, LegacyTranslationEnum
|
from .fluent import GeneratedTranslations, LegacyTranslationEnum
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ class RustBackend(RustBackendGenerated):
|
||||||
if langs is None:
|
if langs is None:
|
||||||
langs = [anki.lang.currentLang]
|
langs = [anki.lang.currentLang]
|
||||||
|
|
||||||
init_msg = pb.BackendInit(
|
init_msg = backend_pb2.BackendInit(
|
||||||
preferred_langs=langs,
|
preferred_langs=langs,
|
||||||
server=server,
|
server=server,
|
||||||
)
|
)
|
||||||
|
@ -95,7 +96,7 @@ class RustBackend(RustBackendGenerated):
|
||||||
return from_json_bytes(self._backend.db_command(to_json_bytes(input)))
|
return from_json_bytes(self._backend.db_command(to_json_bytes(input)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err_bytes = bytes(e.args[0])
|
err_bytes = bytes(e.args[0])
|
||||||
err = pb.BackendError()
|
err = backend_pb2.BackendError()
|
||||||
err.ParseFromString(err_bytes)
|
err.ParseFromString(err_bytes)
|
||||||
raise backend_exception_to_pylib(err)
|
raise backend_exception_to_pylib(err)
|
||||||
|
|
||||||
|
@ -125,21 +126,21 @@ class RustBackend(RustBackendGenerated):
|
||||||
return self._backend.command(service, method, input_bytes)
|
return self._backend.command(service, method, input_bytes)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err_bytes = bytes(e.args[0])
|
err_bytes = bytes(e.args[0])
|
||||||
err = pb.BackendError()
|
err = backend_pb2.BackendError()
|
||||||
err.ParseFromString(err_bytes)
|
err.ParseFromString(err_bytes)
|
||||||
raise backend_exception_to_pylib(err)
|
raise backend_exception_to_pylib(err)
|
||||||
|
|
||||||
|
|
||||||
def translate_string_in(
|
def translate_string_in(
|
||||||
module_index: int, message_index: int, **kwargs: Union[str, int, float]
|
module_index: int, message_index: int, **kwargs: Union[str, int, float]
|
||||||
) -> pb.TranslateStringRequest:
|
) -> i18n_pb2.TranslateStringRequest:
|
||||||
args = {}
|
args = {}
|
||||||
for (k, v) in kwargs.items():
|
for (k, v) in kwargs.items():
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
args[k] = pb.TranslateArgValue(str=v)
|
args[k] = i18n_pb2.TranslateArgValue(str=v)
|
||||||
else:
|
else:
|
||||||
args[k] = pb.TranslateArgValue(number=v)
|
args[k] = i18n_pb2.TranslateArgValue(number=v)
|
||||||
return pb.TranslateStringRequest(
|
return i18n_pb2.TranslateStringRequest(
|
||||||
module_index=module_index, message_index=message_index, args=args
|
module_index=module_index, message_index=message_index, args=args
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,8 +168,8 @@ class Translations(GeneratedTranslations):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def backend_exception_to_pylib(err: pb.BackendError) -> Exception:
|
def backend_exception_to_pylib(err: backend_pb2.BackendError) -> Exception:
|
||||||
kind = pb.BackendError
|
kind = backend_pb2.BackendError
|
||||||
val = err.kind
|
val = err.kind
|
||||||
if val == kind.INTERRUPTED:
|
if val == kind.INTERRUPTED:
|
||||||
return Interrupted()
|
return Interrupted()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../../../bazel-bin/pylib/anki/_backend/backend_pb2.pyi
|
|
|
@ -7,7 +7,24 @@ import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
import pylib.anki._backend.backend_pb2 as pb
|
|
||||||
|
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 anki.scheduler_pb2
|
||||||
|
import anki.sync_pb2
|
||||||
|
import anki.config_pb2
|
||||||
|
import anki.search_pb2
|
||||||
|
import anki.stats_pb2
|
||||||
|
import anki.card_rendering_pb2
|
||||||
|
import anki.tags_pb2
|
||||||
|
import anki.media_pb2
|
||||||
|
|
||||||
import stringcase
|
import stringcase
|
||||||
|
|
||||||
TYPE_DOUBLE = 1
|
TYPE_DOUBLE = 1
|
||||||
|
@ -73,11 +90,11 @@ def python_type_inner(field):
|
||||||
raise Exception(f"unknown type: {type}")
|
raise Exception(f"unknown type: {type}")
|
||||||
|
|
||||||
|
|
||||||
def fullname(fullname):
|
def fullname(fullname: str) -> str:
|
||||||
if "FluentString" in fullname:
|
# eg anki.generic.Empty -> anki.generic_pb2.Empty
|
||||||
return fullname.replace("BackendProto", "anki.fluent_pb2")
|
components = fullname.split(".")
|
||||||
else:
|
components[1] += "_pb2"
|
||||||
return fullname.replace("BackendProto", "pb")
|
return ".".join(components)
|
||||||
|
|
||||||
|
|
||||||
# get_deck_i_d -> get_deck_id etc
|
# get_deck_i_d -> get_deck_id etc
|
||||||
|
@ -131,7 +148,7 @@ def render_method(service_idx, method_idx, method):
|
||||||
return_type = python_type(f)
|
return_type = python_type(f)
|
||||||
else:
|
else:
|
||||||
single_field = ""
|
single_field = ""
|
||||||
return_type = f"pb.{method.output_type.name}"
|
return_type = fullname(method.output_type.full_name)
|
||||||
|
|
||||||
if method.name in SKIP_DECODE:
|
if method.name in SKIP_DECODE:
|
||||||
return_type = "bytes"
|
return_type = "bytes"
|
||||||
|
@ -144,7 +161,7 @@ def render_method(service_idx, method_idx, method):
|
||||||
buf += f"""return self._run_command({service_idx}, {method_idx}, input)
|
buf += f"""return self._run_command({service_idx}, {method_idx}, input)
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
buf += f"""output = pb.{method.output_type.name}()
|
buf += f"""output = {fullname(method.output_type.full_name)}()
|
||||||
output.ParseFromString(self._run_command({service_idx}, {method_idx}, input))
|
output.ParseFromString(self._run_command({service_idx}, {method_idx}, input))
|
||||||
return output{single_field}
|
return output{single_field}
|
||||||
"""
|
"""
|
||||||
|
@ -162,12 +179,30 @@ def render_service(
|
||||||
out.append(render_method(service_index, method_index, method))
|
out.append(render_method(service_index, method_index, method))
|
||||||
|
|
||||||
|
|
||||||
for service in pb.ServiceIndex.DESCRIPTOR.values:
|
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,
|
||||||
|
SCHEDULER=anki.scheduler_pb2,
|
||||||
|
SYNC=anki.sync_pb2,
|
||||||
|
CONFIG=anki.config_pb2,
|
||||||
|
SEARCH=anki.search_pb2,
|
||||||
|
STATS=anki.stats_pb2,
|
||||||
|
CARD_RENDERING=anki.card_rendering_pb2,
|
||||||
|
TAGS=anki.tags_pb2,
|
||||||
|
MEDIA=anki.media_pb2,
|
||||||
|
)
|
||||||
|
|
||||||
|
for service in anki.backend_pb2.ServiceIndex.DESCRIPTOR.values:
|
||||||
# SERVICE_INDEX_TEST -> _TESTSERVICE
|
# SERVICE_INDEX_TEST -> _TESTSERVICE
|
||||||
service_var = (
|
base = service.name.replace("SERVICE_INDEX_", "")
|
||||||
"_" + service.name.replace("SERVICE_INDEX", "").replace("_", "") + "SERVICE"
|
service_pkg = service_modules.get(base)
|
||||||
)
|
service_var = "_" + base.replace("_", "") + "SERVICE"
|
||||||
service_obj = getattr(pb, service_var)
|
service_obj = getattr(service_pkg, service_var)
|
||||||
service_index = service.number
|
service_index = service.number
|
||||||
render_service(service_obj, service_index)
|
render_service(service_obj, service_index)
|
||||||
|
|
||||||
|
@ -194,7 +229,23 @@ col.decks.all_config()
|
||||||
|
|
||||||
from typing import *
|
from typing import *
|
||||||
|
|
||||||
import anki._backend.backend_pb2 as pb
|
import anki
|
||||||
|
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 anki.scheduler_pb2
|
||||||
|
import anki.sync_pb2
|
||||||
|
import anki.config_pb2
|
||||||
|
import anki.search_pb2
|
||||||
|
import anki.stats_pb2
|
||||||
|
import anki.card_rendering_pb2
|
||||||
|
import anki.tags_pb2
|
||||||
|
import anki.media_pb2
|
||||||
|
|
||||||
class RustBackendGenerated:
|
class RustBackendGenerated:
|
||||||
def _run_command(self, service: int, method: int, input: Any) -> bytes:
|
def _run_command(self, service: int, method: int, input: Any) -> bytes:
|
||||||
|
|
1
pylib/anki/backend_pb2.pyi
Symbolic link
1
pylib/anki/backend_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/backend_pb2.pyi
|
1
pylib/anki/card_rendering_pb2.pyi
Symbolic link
1
pylib/anki/card_rendering_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/card_rendering_pb2.pyi
|
|
@ -10,8 +10,7 @@ import time
|
||||||
from typing import List, NewType, Optional
|
from typing import List, NewType, Optional
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import cards_pb2, hooks
|
||||||
from anki import hooks
|
|
||||||
from anki._legacy import DeprecatedNamesMixin, deprecated
|
from anki._legacy import DeprecatedNamesMixin, deprecated
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.models import NotetypeDict, TemplateDict
|
from anki.models import NotetypeDict, TemplateDict
|
||||||
|
@ -31,7 +30,7 @@ from anki.sound import AVTag
|
||||||
|
|
||||||
# types
|
# types
|
||||||
CardId = NewType("CardId", int)
|
CardId = NewType("CardId", int)
|
||||||
BackendCard = _pb.Card
|
BackendCard = cards_pb2.Card
|
||||||
|
|
||||||
|
|
||||||
class Card(DeprecatedNamesMixin):
|
class Card(DeprecatedNamesMixin):
|
||||||
|
@ -62,14 +61,14 @@ class Card(DeprecatedNamesMixin):
|
||||||
self._load_from_backend_card(backend_card)
|
self._load_from_backend_card(backend_card)
|
||||||
else:
|
else:
|
||||||
# new card with defaults
|
# new card with defaults
|
||||||
self._load_from_backend_card(_pb.Card())
|
self._load_from_backend_card(cards_pb2.Card())
|
||||||
|
|
||||||
def load(self) -> None:
|
def load(self) -> None:
|
||||||
card = self.col._backend.get_card(self.id)
|
card = self.col._backend.get_card(self.id)
|
||||||
assert card
|
assert card
|
||||||
self._load_from_backend_card(card)
|
self._load_from_backend_card(card)
|
||||||
|
|
||||||
def _load_from_backend_card(self, card: _pb.Card) -> None:
|
def _load_from_backend_card(self, card: cards_pb2.Card) -> None:
|
||||||
self._render_output = None
|
self._render_output = None
|
||||||
self._note = None
|
self._note = None
|
||||||
self.id = CardId(card.id)
|
self.id = CardId(card.id)
|
||||||
|
@ -91,9 +90,9 @@ class Card(DeprecatedNamesMixin):
|
||||||
self.flags = card.flags
|
self.flags = card.flags
|
||||||
self.data = card.data
|
self.data = card.data
|
||||||
|
|
||||||
def _to_backend_card(self) -> _pb.Card:
|
def _to_backend_card(self) -> cards_pb2.Card:
|
||||||
# mtime & usn are set by backend
|
# mtime & usn are set by backend
|
||||||
return _pb.Card(
|
return cards_pb2.Card(
|
||||||
id=self.id,
|
id=self.id,
|
||||||
note_id=self.nid,
|
note_id=self.nid,
|
||||||
deck_id=self.did,
|
deck_id=self.did,
|
||||||
|
|
1
pylib/anki/cards_pb2.pyi
Symbolic link
1
pylib/anki/cards_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/cards_pb2.pyi
|
|
@ -7,24 +7,29 @@ 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.backend_pb2 as _pb
|
from anki import (
|
||||||
|
card_rendering_pb2,
|
||||||
# protobuf we publicly export - listed first to avoid circular imports
|
collection_pb2,
|
||||||
|
config_pb2,
|
||||||
|
generic_pb2,
|
||||||
|
search_pb2,
|
||||||
|
stats_pb2,
|
||||||
|
)
|
||||||
from anki._legacy import DeprecatedNamesMixin, deprecated
|
from anki._legacy import DeprecatedNamesMixin, deprecated
|
||||||
|
|
||||||
SearchNode = _pb.SearchNode
|
# protobuf we publicly export - listed first to avoid circular imports
|
||||||
Progress = _pb.Progress
|
SearchNode = search_pb2.SearchNode
|
||||||
EmptyCardsReport = _pb.EmptyCardsReport
|
Progress = collection_pb2.Progress
|
||||||
GraphPreferences = _pb.GraphPreferences
|
EmptyCardsReport = card_rendering_pb2.EmptyCardsReport
|
||||||
Preferences = _pb.Preferences
|
GraphPreferences = stats_pb2.GraphPreferences
|
||||||
UndoStatus = _pb.UndoStatus
|
Preferences = config_pb2.Preferences
|
||||||
OpChanges = _pb.OpChanges
|
UndoStatus = collection_pb2.UndoStatus
|
||||||
OpChangesWithCount = _pb.OpChangesWithCount
|
OpChanges = collection_pb2.OpChanges
|
||||||
OpChangesWithId = _pb.OpChangesWithId
|
OpChangesWithCount = collection_pb2.OpChangesWithCount
|
||||||
OpChangesAfterUndo = _pb.OpChangesAfterUndo
|
OpChangesWithId = collection_pb2.OpChangesWithId
|
||||||
DefaultsForAdding = _pb.DeckAndNotetype
|
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
|
||||||
|
@ -370,7 +375,7 @@ class Collection(DeprecatedNamesMixin):
|
||||||
|
|
||||||
def defaults_for_adding(
|
def defaults_for_adding(
|
||||||
self, *, current_review_card: Optional[Card]
|
self, *, current_review_card: Optional[Card]
|
||||||
) -> DefaultsForAdding:
|
) -> anki.notes.DefaultsForAdding:
|
||||||
"""Get starting deck and notetype for add screen.
|
"""Get starting deck and notetype for add screen.
|
||||||
An option in the preferences controls whether this will be based on the current deck
|
An option in the preferences controls whether this will be based on the current deck
|
||||||
or current notetype.
|
or current notetype.
|
||||||
|
@ -487,12 +492,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=_pb.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))
|
||||||
|
@ -500,13 +505,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=_pb.Empty())
|
return search_pb2.SortOrder(none=generic_pb2.Empty())
|
||||||
|
|
||||||
def find_and_replace(
|
def find_and_replace(
|
||||||
self,
|
self,
|
||||||
|
@ -725,19 +732,19 @@ class Collection(DeprecatedNamesMixin):
|
||||||
"This is a debugging aid. Prefer .get_config() when you know the key you need."
|
"This is a debugging aid. Prefer .get_config() when you know the key you need."
|
||||||
return from_json_bytes(self._backend.get_all_config())
|
return from_json_bytes(self._backend.get_all_config())
|
||||||
|
|
||||||
def get_config_bool(self, key: Config.Bool.Key.V) -> bool:
|
def get_config_bool(self, key: Config.Bool.V) -> bool:
|
||||||
return self._backend.get_config_bool(key)
|
return self._backend.get_config_bool(key)
|
||||||
|
|
||||||
def set_config_bool(
|
def set_config_bool(
|
||||||
self, key: Config.Bool.Key.V, value: bool, *, undoable: bool = False
|
self, key: Config.Bool.V, value: bool, *, undoable: bool = False
|
||||||
) -> OpChanges:
|
) -> OpChanges:
|
||||||
return self._backend.set_config_bool(key=key, value=value, undoable=undoable)
|
return self._backend.set_config_bool(key=key, value=value, undoable=undoable)
|
||||||
|
|
||||||
def get_config_string(self, key: Config.String.Key.V) -> str:
|
def get_config_string(self, key: Config.String.V) -> str:
|
||||||
return self._backend.get_config_string(key)
|
return self._backend.get_config_string(key)
|
||||||
|
|
||||||
def set_config_string(
|
def set_config_string(
|
||||||
self, key: Config.String.Key.V, value: str, undoable: bool = False
|
self, key: Config.String.V, value: str, undoable: bool = False
|
||||||
) -> OpChanges:
|
) -> OpChanges:
|
||||||
return self._backend.set_config_string(key=key, value=value, undoable=undoable)
|
return self._backend.set_config_string(key=key, value=value, undoable=undoable)
|
||||||
|
|
||||||
|
|
1
pylib/anki/collection_pb2.pyi
Symbolic link
1
pylib/anki/collection_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/collection_pb2.pyi
|
|
@ -25,12 +25,12 @@ from typing import Any
|
||||||
from weakref import ref
|
from weakref import ref
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
from anki._backend import backend_pb2 as _pb
|
from anki import config_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 = config_pb2.ConfigKey
|
||||||
|
|
||||||
|
|
||||||
class ConfigManager:
|
class ConfigManager:
|
||||||
|
|
1
pylib/anki/config_pb2.pyi
Symbolic link
1
pylib/anki/config_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/config_pb2.pyi
|
|
@ -6,8 +6,6 @@ from __future__ import annotations
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict, NewType, Optional
|
from typing import Any, Dict, NewType, Optional
|
||||||
|
|
||||||
import anki
|
|
||||||
|
|
||||||
# whether new cards should be mixed with reviews, or shown first or last
|
# whether new cards should be mixed with reviews, or shown first or last
|
||||||
NEW_CARDS_DISTRIBUTE = 0
|
NEW_CARDS_DISTRIBUTE = 0
|
||||||
NEW_CARDS_LAST = 1
|
NEW_CARDS_LAST = 1
|
||||||
|
@ -93,6 +91,8 @@ REVLOG_RESCHED = 4
|
||||||
# Labels
|
# Labels
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
import anki.collection
|
||||||
|
|
||||||
|
|
||||||
def _tr(col: Optional[anki.collection.Collection]) -> Any:
|
def _tr(col: Optional[anki.collection.Collection]) -> Any:
|
||||||
if col:
|
if col:
|
||||||
|
|
|
@ -7,7 +7,7 @@ import re
|
||||||
from re import Match
|
from re import Match
|
||||||
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
|
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki
|
import anki._backend
|
||||||
|
|
||||||
# DBValue is actually Union[str, int, float, None], but if defined
|
# DBValue is actually Union[str, int, float, None], but if defined
|
||||||
# that way, every call site needs to do a type check prior to using
|
# that way, every call site needs to do a type check prior to using
|
||||||
|
|
1
pylib/anki/deckconfig_pb2.pyi
Symbolic link
1
pylib/anki/deckconfig_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/deckconfig_pb2.pyi
|
|
@ -23,7 +23,7 @@ from typing import (
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import anki
|
import anki
|
||||||
|
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import deckconfig_pb2, decks_pb2
|
||||||
from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning
|
from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning
|
||||||
from anki.cards import CardId
|
from anki.cards import CardId
|
||||||
from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId
|
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
|
from anki.utils import from_json_bytes, ids2str, intTime, to_json_bytes
|
||||||
|
|
||||||
# public exports
|
# public exports
|
||||||
DeckTreeNode = _pb.DeckTreeNode
|
DeckTreeNode = decks_pb2.DeckTreeNode
|
||||||
DeckNameId = _pb.DeckNameId
|
DeckNameId = decks_pb2.DeckNameId
|
||||||
FilteredDeckConfig = _pb.Deck.Filtered
|
FilteredDeckConfig = decks_pb2.Deck.Filtered
|
||||||
DeckCollapseScope = _pb.SetDeckCollapsedRequest.Scope
|
DeckCollapseScope = decks_pb2.SetDeckCollapsedRequest.Scope
|
||||||
DeckConfigsForUpdate = _pb.DeckConfigsForUpdate
|
DeckConfigsForUpdate = deckconfig_pb2.DeckConfigsForUpdate
|
||||||
UpdateDeckConfigs = _pb.UpdateDeckConfigsRequest
|
UpdateDeckConfigs = deckconfig_pb2.UpdateDeckConfigsRequest
|
||||||
|
|
||||||
# type aliases until we can move away from dicts
|
# type aliases until we can move away from dicts
|
||||||
DeckDict = Dict[str, Any]
|
DeckDict = Dict[str, Any]
|
||||||
|
|
1
pylib/anki/decks_pb2.pyi
Symbolic link
1
pylib/anki/decks_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/decks_pb2.pyi
|
1
pylib/anki/generic_pb2.pyi
Symbolic link
1
pylib/anki/generic_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/generic_pb2.pyi
|
|
@ -76,7 +76,7 @@ class HttpClient:
|
||||||
return buf.getvalue()
|
return buf.getvalue()
|
||||||
|
|
||||||
def _agentName(self) -> str:
|
def _agentName(self) -> str:
|
||||||
from anki import version
|
from anki.buildinfo import version
|
||||||
|
|
||||||
return f"Anki {version}"
|
return f"Anki {version}"
|
||||||
|
|
||||||
|
|
1
pylib/anki/i18n_pb2.pyi
Symbolic link
1
pylib/anki/i18n_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/i18n_pb2.pyi
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from typing import Any, Callable, Sequence, Tuple, Type, Union
|
from typing import Any, Callable, Sequence, Tuple, Type, Union
|
||||||
|
|
||||||
from anki import Collection
|
from anki.collection import Collection
|
||||||
from anki.importing.anki2 import Anki2Importer
|
from anki.importing.anki2 import Anki2Importer
|
||||||
from anki.importing.apkg import AnkiPackageImporter
|
from anki.importing.apkg import AnkiPackageImporter
|
||||||
from anki.importing.base import Importer
|
from anki.importing.base import Importer
|
||||||
|
|
|
@ -9,7 +9,8 @@ import weakref
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
import anki._backend.backend_pb2 as _pb
|
import anki._backend
|
||||||
|
import anki.i18n_pb2 as _pb
|
||||||
|
|
||||||
# public exports
|
# public exports
|
||||||
TR = anki._backend.LegacyTranslationEnum
|
TR = anki._backend.LegacyTranslationEnum
|
||||||
|
|
|
@ -10,8 +10,7 @@ from dataclasses import dataclass
|
||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, Tuple
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import card_rendering_pb2, hooks
|
||||||
from anki import hooks
|
|
||||||
from anki.models import NotetypeDict
|
from anki.models import NotetypeDict
|
||||||
from anki.template import TemplateRenderContext, TemplateRenderOutput
|
from anki.template import TemplateRenderContext, TemplateRenderOutput
|
||||||
from anki.utils import call, isMac, namedtmp, tmpdir
|
from anki.utils import call, isMac, namedtmp, tmpdir
|
||||||
|
@ -45,7 +44,9 @@ class ExtractedLatexOutput:
|
||||||
latex: List[ExtractedLatex]
|
latex: List[ExtractedLatex]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_proto(proto: _pb.ExtractLatexResponse) -> ExtractedLatexOutput:
|
def from_proto(
|
||||||
|
proto: card_rendering_pb2.ExtractLatexResponse,
|
||||||
|
) -> ExtractedLatexOutput:
|
||||||
return ExtractedLatexOutput(
|
return ExtractedLatexOutput(
|
||||||
html=proto.text,
|
html=proto.text,
|
||||||
latex=[
|
latex=[
|
||||||
|
|
|
@ -10,8 +10,7 @@ import sys
|
||||||
import time
|
import time
|
||||||
from typing import Any, Callable, List, Optional, Tuple
|
from typing import Any, Callable, List, Optional, Tuple
|
||||||
|
|
||||||
import anki
|
from anki import media_pb2
|
||||||
import anki._backend.backend_pb2 as _pb
|
|
||||||
from anki._legacy import deprecated
|
from anki._legacy import deprecated
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.latex import render_latex, render_latex_returning_errors
|
from anki.latex import render_latex, render_latex_returning_errors
|
||||||
|
@ -27,7 +26,7 @@ def media_paths_from_col_path(col_path: str) -> Tuple[str, str]:
|
||||||
return (media_folder, media_db)
|
return (media_folder, media_db)
|
||||||
|
|
||||||
|
|
||||||
CheckMediaResponse = _pb.CheckMediaResponse
|
CheckMediaResponse = media_pb2.CheckMediaResponse
|
||||||
|
|
||||||
|
|
||||||
# fixme: look into whether we can drop chdir() below
|
# fixme: look into whether we can drop chdir() below
|
||||||
|
|
1
pylib/anki/media_pb2.pyi
Symbolic link
1
pylib/anki/media_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/media_pb2.pyi
|
|
@ -12,7 +12,7 @@ import time
|
||||||
from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union
|
from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import notetypes_pb2
|
||||||
from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning
|
from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning
|
||||||
from anki.collection import OpChanges, OpChangesWithId
|
from anki.collection import OpChanges, OpChangesWithId
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
|
@ -22,11 +22,11 @@ from anki.stdmodels import StockNotetypeKind
|
||||||
from anki.utils import checksum, from_json_bytes, to_json_bytes
|
from anki.utils import checksum, from_json_bytes, to_json_bytes
|
||||||
|
|
||||||
# public exports
|
# public exports
|
||||||
NotetypeNameId = _pb.NotetypeNameId
|
NotetypeNameId = notetypes_pb2.NotetypeNameId
|
||||||
NotetypeNameIdUseCount = _pb.NotetypeNameIdUseCount
|
NotetypeNameIdUseCount = notetypes_pb2.NotetypeNameIdUseCount
|
||||||
NotetypeNames = _pb.NotetypeNames
|
NotetypeNames = notetypes_pb2.NotetypeNames
|
||||||
ChangeNotetypeInfo = _pb.ChangeNotetypeInfo
|
ChangeNotetypeInfo = notetypes_pb2.ChangeNotetypeInfo
|
||||||
ChangeNotetypeRequest = _pb.ChangeNotetypeRequest
|
ChangeNotetypeRequest = notetypes_pb2.ChangeNotetypeRequest
|
||||||
|
|
||||||
# legacy types
|
# legacy types
|
||||||
NotetypeDict = Dict[str, Any]
|
NotetypeDict = Dict[str, Any]
|
||||||
|
@ -459,7 +459,9 @@ and notes.mid = ? and cards.ord = ?""",
|
||||||
def _availClozeOrds(
|
def _availClozeOrds(
|
||||||
self, notetype: NotetypeDict, flds: str, allow_empty: bool = True
|
self, notetype: NotetypeDict, flds: str, allow_empty: bool = True
|
||||||
) -> List[int]:
|
) -> 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))
|
return list(self.col._backend.cloze_numbers_in_note(note))
|
||||||
|
|
||||||
# @deprecated(replaced_by=add_template)
|
# @deprecated(replaced_by=add_template)
|
||||||
|
|
|
@ -9,15 +9,15 @@ import copy
|
||||||
from typing import Any, List, NewType, Optional, Sequence, Tuple, Union
|
from typing import Any, List, NewType, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import hooks, notes_pb2
|
||||||
from anki import hooks
|
|
||||||
from anki._legacy import DeprecatedNamesMixin
|
from anki._legacy import DeprecatedNamesMixin
|
||||||
from anki.consts import MODEL_STD
|
from anki.consts import MODEL_STD
|
||||||
from anki.models import NotetypeDict, NotetypeId, TemplateDict
|
from anki.models import NotetypeDict, NotetypeId, TemplateDict
|
||||||
from anki.utils import joinFields
|
from anki.utils import joinFields
|
||||||
|
|
||||||
DuplicateOrEmptyResult = _pb.NoteFieldsCheckResponse.State
|
DuplicateOrEmptyResult = notes_pb2.NoteFieldsCheckResponse.State
|
||||||
NoteFieldsCheckResult = _pb.NoteFieldsCheckResponse.State
|
NoteFieldsCheckResult = notes_pb2.NoteFieldsCheckResponse.State
|
||||||
|
DefaultsForAdding = notes_pb2.DeckAndNotetype
|
||||||
|
|
||||||
# types
|
# types
|
||||||
NoteId = NewType("NoteId", int)
|
NoteId = NewType("NoteId", int)
|
||||||
|
@ -53,7 +53,7 @@ class Note(DeprecatedNamesMixin):
|
||||||
assert note
|
assert note
|
||||||
self._load_from_backend_note(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.id = NoteId(note.id)
|
||||||
self.guid = note.guid
|
self.guid = note.guid
|
||||||
self.mid = NotetypeId(note.notetype_id)
|
self.mid = NotetypeId(note.notetype_id)
|
||||||
|
@ -63,9 +63,9 @@ class Note(DeprecatedNamesMixin):
|
||||||
self.fields = list(note.fields)
|
self.fields = list(note.fields)
|
||||||
self._fmap = self.col.models.field_map(self.note_type())
|
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)
|
hooks.note_will_flush(self)
|
||||||
return _pb.Note(
|
return notes_pb2.Note(
|
||||||
id=self.id,
|
id=self.id,
|
||||||
guid=self.guid,
|
guid=self.guid,
|
||||||
notetype_id=self.mid,
|
notetype_id=self.mid,
|
||||||
|
|
1
pylib/anki/notes_pb2.pyi
Symbolic link
1
pylib/anki/notes_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/notes_pb2.pyi
|
1
pylib/anki/notetypes_pb2.pyi
Symbolic link
1
pylib/anki/notetypes_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/notetypes_pb2.pyi
|
|
@ -4,26 +4,26 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import decks_pb2, scheduler_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
|
||||||
|
|
||||||
|
from anki import config_pb2
|
||||||
from anki.cards import CardId
|
from anki.cards import CardId
|
||||||
from anki.consts import CARD_TYPE_NEW, NEW_CARDS_RANDOM, QUEUE_TYPE_NEW, QUEUE_TYPE_REV
|
from anki.consts import CARD_TYPE_NEW, NEW_CARDS_RANDOM, QUEUE_TYPE_NEW, QUEUE_TYPE_REV
|
||||||
from anki.decks import DeckConfigDict, DeckId, DeckTreeNode
|
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 = _pb.FilteredDeckForUpdate
|
|
||||||
|
|
||||||
|
|
||||||
class SchedulerBase:
|
class SchedulerBase:
|
||||||
"Actions shared between schedulers."
|
"Actions shared between schedulers."
|
||||||
|
@ -162,14 +162,14 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
|
||||||
self,
|
self,
|
||||||
card_ids: Sequence[CardId],
|
card_ids: Sequence[CardId],
|
||||||
days: str,
|
days: str,
|
||||||
config_key: Optional[Config.String.Key.V] = None,
|
config_key: Optional[Config.String.V] = None,
|
||||||
) -> OpChanges:
|
) -> OpChanges:
|
||||||
"""Set cards to be due in `days`, turning them into review cards if necessary.
|
"""Set cards to be due in `days`, turning them into review cards if necessary.
|
||||||
`days` can be of the form '5' or '5..7'
|
`days` can be of the form '5' or '5..7'
|
||||||
If `config_key` is provided, provided days will be remembered in config."""
|
If `config_key` is provided, provided days will be remembered in config."""
|
||||||
key: Optional[Config.String]
|
key: Optional[config_pb2.OptionalStringConfigKey]
|
||||||
if config_key is not None:
|
if config_key is not None:
|
||||||
key = Config.String(key=config_key)
|
key = config_pb2.OptionalStringConfigKey(key=config_key)
|
||||||
else:
|
else:
|
||||||
key = None
|
key = None
|
||||||
return self.col._backend.set_due_date(
|
return self.col._backend.set_due_date(
|
||||||
|
|
|
@ -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.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]
|
||||||
|
|
|
@ -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.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":
|
||||||
|
|
1
pylib/anki/scheduler_pb2.pyi
Symbolic link
1
pylib/anki/scheduler_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/scheduler_pb2.pyi
|
1
pylib/anki/search_pb2.pyi
Symbolic link
1
pylib/anki/search_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/search_pb2.pyi
|
|
@ -10,7 +10,8 @@ import json
|
||||||
import time
|
import time
|
||||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki
|
import anki.cards
|
||||||
|
import anki.collection
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.lang import FormatTimeSpan
|
from anki.lang import FormatTimeSpan
|
||||||
from anki.utils import ids2str
|
from anki.utils import ids2str
|
||||||
|
|
1
pylib/anki/stats_pb2.pyi
Symbolic link
1
pylib/anki/stats_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/stats_pb2.pyi
|
|
@ -5,12 +5,13 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Callable, List, Tuple
|
from typing import Any, Callable, List, Tuple
|
||||||
|
|
||||||
import anki
|
import anki.collection
|
||||||
import anki._backend.backend_pb2 as _pb
|
import anki.models
|
||||||
|
from anki import notetypes_pb2
|
||||||
from anki.utils import from_json_bytes
|
from anki.utils import from_json_bytes
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
StockNotetypeKind = _pb.StockNotetype.Kind
|
StockNotetypeKind = notetypes_pb2.StockNotetype.Kind
|
||||||
|
|
||||||
# add-on authors can add ("note type name", function)
|
# add-on authors can add ("note type name", function)
|
||||||
# to this list to have it shown in the add/clone note type screen
|
# to this list to have it shown in the add/clone note type screen
|
||||||
|
|
|
@ -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.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
1
pylib/anki/sync_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/sync_pb2.pyi
|
|
@ -26,8 +26,8 @@ except ImportError as e:
|
||||||
|
|
||||||
from flask import Response
|
from flask import Response
|
||||||
|
|
||||||
from anki import Collection
|
from anki.collection import Collection
|
||||||
from anki._backend.backend_pb2 import SyncServerMethodRequest
|
from anki.sync_pb2 import SyncServerMethodRequest
|
||||||
|
|
||||||
Method = SyncServerMethodRequest.Method # pylint: disable=no-member
|
Method = SyncServerMethodRequest.Method # pylint: disable=no-member
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,15 @@ import re
|
||||||
from typing import Collection, List, Match, Optional, Sequence
|
from typing import Collection, List, Match, Optional, Sequence
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
import anki._backend.backend_pb2 as _pb
|
|
||||||
import anki.collection
|
import anki.collection
|
||||||
|
from anki import tags_pb2
|
||||||
from anki.collection import OpChanges, OpChangesWithCount
|
from anki.collection import OpChanges, OpChangesWithCount
|
||||||
from anki.decks import DeckId
|
from anki.decks import DeckId
|
||||||
from anki.notes import NoteId
|
from anki.notes import NoteId
|
||||||
from anki.utils import ids2str
|
from anki.utils import ids2str
|
||||||
|
|
||||||
# public exports
|
# public exports
|
||||||
TagTreeNode = _pb.TagTreeNode
|
TagTreeNode = tags_pb2.TagTreeNode
|
||||||
MARKED_TAG = "marked"
|
MARKED_TAG = "marked"
|
||||||
|
|
||||||
|
|
||||||
|
|
1
pylib/anki/tags_pb2.pyi
Symbolic link
1
pylib/anki/tags_pb2.pyi
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../bazel-bin/pylib/anki/tags_pb2.pyi
|
|
@ -32,8 +32,7 @@ from dataclasses import dataclass
|
||||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
import anki._backend.backend_pb2 as _pb
|
from anki import card_rendering_pb2, hooks
|
||||||
from anki import hooks
|
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from anki.decks import DeckManager
|
from anki.decks import DeckManager
|
||||||
from anki.errors import TemplateError
|
from anki.errors import TemplateError
|
||||||
|
@ -65,7 +64,9 @@ class PartiallyRenderedCard:
|
||||||
latex_svg: bool
|
latex_svg: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_proto(cls, out: _pb.RenderCardResponse) -> PartiallyRenderedCard:
|
def from_proto(
|
||||||
|
cls, out: card_rendering_pb2.RenderCardResponse
|
||||||
|
) -> PartiallyRenderedCard:
|
||||||
qnodes = cls.nodes_from_proto(out.question_nodes)
|
qnodes = cls.nodes_from_proto(out.question_nodes)
|
||||||
anodes = cls.nodes_from_proto(out.answer_nodes)
|
anodes = cls.nodes_from_proto(out.answer_nodes)
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ class PartiallyRenderedCard:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def nodes_from_proto(
|
def nodes_from_proto(
|
||||||
nodes: Sequence[_pb.RenderedTemplateNode],
|
nodes: Sequence[card_rendering_pb2.RenderedTemplateNode],
|
||||||
) -> TemplateReplacementList:
|
) -> TemplateReplacementList:
|
||||||
results: TemplateReplacementList = []
|
results: TemplateReplacementList = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
@ -90,7 +91,7 @@ class PartiallyRenderedCard:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def av_tag_to_native(tag: _pb.AVTag) -> AVTag:
|
def av_tag_to_native(tag: card_rendering_pb2.AVTag) -> AVTag:
|
||||||
val = tag.WhichOneof("value")
|
val = tag.WhichOneof("value")
|
||||||
if val == "sound_or_video":
|
if val == "sound_or_video":
|
||||||
return SoundOrVideoTag(filename=tag.sound_or_video)
|
return SoundOrVideoTag(filename=tag.sound_or_video)
|
||||||
|
@ -104,7 +105,7 @@ def av_tag_to_native(tag: _pb.AVTag) -> AVTag:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def av_tags_to_native(tags: Sequence[_pb.AVTag]) -> List[AVTag]:
|
def av_tags_to_native(tags: Sequence[card_rendering_pb2.AVTag]) -> List[AVTag]:
|
||||||
return list(map(av_tag_to_native, tags))
|
return list(map(av_tag_to_native, tags))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
load("@bazel_skylib//lib:paths.bzl", "paths")
|
load("@bazel_skylib//lib:paths.bzl", "paths")
|
||||||
|
|
||||||
def _py_proto_library_impl(ctx):
|
def _py_proto_impl(ctx):
|
||||||
basename = ctx.file.src.basename
|
outs = []
|
||||||
outs = [
|
for src in ctx.files.srcs:
|
||||||
ctx.actions.declare_file(paths.replace_extension(basename, "_pb2.py")),
|
base = paths.basename(src.path)
|
||||||
ctx.actions.declare_file(paths.replace_extension(basename, "_pb2.pyi")),
|
outs.append(ctx.actions.declare_file(paths.replace_extension(base, "_pb2.py")))
|
||||||
]
|
outs.append(ctx.actions.declare_file(paths.replace_extension(base, "_pb2.pyi")))
|
||||||
|
|
||||||
ctx.actions.run(
|
ctx.actions.run(
|
||||||
outputs = outs,
|
outputs = outs,
|
||||||
inputs = [ctx.file.src],
|
inputs = ctx.files.srcs,
|
||||||
executable = ctx.executable.protoc_wrapper,
|
executable = ctx.executable.protoc_wrapper,
|
||||||
arguments = [
|
arguments = [
|
||||||
ctx.executable.protoc.path,
|
ctx.executable.protoc.path,
|
||||||
ctx.executable.mypy_protobuf.path,
|
ctx.executable.mypy_protobuf.path,
|
||||||
ctx.file.src.path,
|
|
||||||
paths.dirname(outs[0].path),
|
paths.dirname(outs[0].path),
|
||||||
],
|
] + [file.path for file in ctx.files.srcs],
|
||||||
tools = [
|
tools = [
|
||||||
ctx.executable.protoc,
|
ctx.executable.protoc,
|
||||||
ctx.executable.mypy_protobuf,
|
ctx.executable.mypy_protobuf,
|
||||||
|
@ -26,10 +26,10 @@ def _py_proto_library_impl(ctx):
|
||||||
DefaultInfo(files = depset(direct = outs), data_runfiles = ctx.runfiles(files = outs)),
|
DefaultInfo(files = depset(direct = outs), data_runfiles = ctx.runfiles(files = outs)),
|
||||||
]
|
]
|
||||||
|
|
||||||
py_proto_library_typed = rule(
|
py_proto = rule(
|
||||||
implementation = _py_proto_library_impl,
|
implementation = _py_proto_impl,
|
||||||
attrs = {
|
attrs = {
|
||||||
"src": attr.label(allow_single_file = [".proto"]),
|
"srcs": attr.label_list(allow_files = [".proto"]),
|
||||||
"protoc_wrapper": attr.label(
|
"protoc_wrapper": attr.label(
|
||||||
executable = True,
|
executable = True,
|
||||||
cfg = "exec",
|
cfg = "exec",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from anki import Collection as aopen
|
from anki.collection import Collection as aopen
|
||||||
|
|
||||||
# Between 2-4AM, shift the time back so test assumptions hold.
|
# Between 2-4AM, shift the time back so test assumptions hold.
|
||||||
lt = time.localtime()
|
lt = time.localtime()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from anki import Collection as aopen
|
from anki.collection import Collection as aopen
|
||||||
from anki.dbproxy import emulate_named_args
|
from anki.dbproxy import emulate_named_args
|
||||||
from anki.lang import TR, without_unicode_isolation
|
from anki.lang import TR, without_unicode_isolation
|
||||||
from anki.stdmodels import addBasicModel, get_stock_notetypes
|
from anki.stdmodels import addBasicModel, get_stock_notetypes
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from anki import Collection as aopen
|
from anki.collection import Collection as aopen
|
||||||
from anki.exporting import *
|
from anki.exporting import *
|
||||||
from anki.importing import Anki2Importer
|
from anki.importing import Anki2Importer
|
||||||
from tests.shared import errorsAfterMidnight
|
from tests.shared import errorsAfterMidnight
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from anki import Collection
|
from anki.collection import Collection
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.lang import without_unicode_isolation
|
from anki.lang import without_unicode_isolation
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
|
|
|
@ -10,16 +10,12 @@ import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
(protoc, mypy_protobuf, proto, outdir) = sys.argv[1:]
|
(protoc, mypy_protobuf, outdir, *protos) = sys.argv[1:]
|
||||||
|
|
||||||
# copy to current dir
|
if protos[0].startswith("external"):
|
||||||
basename = os.path.basename(proto)
|
prefix = "external/ankidesktop/proto/"
|
||||||
shutil.copyfile(proto, basename)
|
else:
|
||||||
|
prefix = "proto/"
|
||||||
# output filenames
|
|
||||||
without_ext = os.path.splitext(basename)[0]
|
|
||||||
pb2_py = without_ext + "_pb2.py"
|
|
||||||
pb2_pyi = without_ext + "_pb2.pyi"
|
|
||||||
|
|
||||||
# invoke protoc
|
# invoke protoc
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
|
@ -28,13 +24,17 @@ subprocess.run(
|
||||||
"--plugin=protoc-gen-mypy=" + mypy_protobuf,
|
"--plugin=protoc-gen-mypy=" + mypy_protobuf,
|
||||||
"--python_out=.",
|
"--python_out=.",
|
||||||
"--mypy_out=.",
|
"--mypy_out=.",
|
||||||
basename,
|
"-I" + prefix,
|
||||||
|
"-Iexternal/ankidesktop/" + prefix,
|
||||||
|
*protos,
|
||||||
],
|
],
|
||||||
# mypy prints to stderr on success :-(
|
# mypy prints to stderr on success :-(
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# move files into output
|
for proto in protos:
|
||||||
shutil.move(pb2_py, outdir + "/" + pb2_py)
|
without_prefix_and_ext, _ = os.path.splitext(proto[len(prefix) :])
|
||||||
shutil.move(pb2_pyi, outdir + "/" + pb2_pyi)
|
for ext in "_pb2.py", "_pb2.pyi":
|
||||||
|
path = without_prefix_and_ext + ext
|
||||||
|
shutil.move(path, os.path.join(outdir, os.path.basename(path)))
|
||||||
|
|
|
@ -9,7 +9,7 @@ ignored-classes=
|
||||||
BrowserColumns,
|
BrowserColumns,
|
||||||
BrowserRow,
|
BrowserRow,
|
||||||
SearchNode,
|
SearchNode,
|
||||||
Config,
|
ConfigKey,
|
||||||
OpChanges,
|
OpChanges,
|
||||||
UnburyDeckRequest,
|
UnburyDeckRequest,
|
||||||
CardAnswer,
|
CardAnswer,
|
||||||
|
|
|
@ -13,13 +13,27 @@ import traceback
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
||||||
|
|
||||||
import anki.lang
|
import anki.lang
|
||||||
from anki import version as _version
|
|
||||||
from anki._backend import RustBackend
|
from anki._backend import RustBackend
|
||||||
|
from anki.buildinfo import version as _version
|
||||||
|
from anki.collection import Collection
|
||||||
from anki.consts import HELP_SITE
|
from anki.consts import HELP_SITE
|
||||||
from anki.utils import checksum, isLin, isMac
|
from anki.utils import checksum, isLin, isMac
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import TR, locale_dir, tr
|
from aqt.utils import TR, locale_dir, tr
|
||||||
|
|
||||||
|
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
|
||||||
|
raise Exception("Anki requires Python 3.7+")
|
||||||
|
|
||||||
|
# ensure unicode filenames are supported
|
||||||
|
try:
|
||||||
|
"テスト".encode(sys.getfilesystemencoding())
|
||||||
|
except UnicodeEncodeError as exc:
|
||||||
|
raise Exception("Anki requires a UTF-8 locale.") from exc
|
||||||
|
|
||||||
|
# compat aliases
|
||||||
|
anki.version = _version # type: ignore
|
||||||
|
anki.Collection = Collection # type: ignore
|
||||||
|
|
||||||
# we want to be able to print unicode debug info to console without
|
# we want to be able to print unicode debug info to console without
|
||||||
# fear of a traceback on systems with the console set to ASCII
|
# fear of a traceback on systems with the console set to ASCII
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -502,7 +502,7 @@ class SidebarTreeView(QTreeView):
|
||||||
root: SidebarItem,
|
root: SidebarItem,
|
||||||
name: str,
|
name: str,
|
||||||
icon: Union[str, ColoredIcon],
|
icon: Union[str, ColoredIcon],
|
||||||
collapse_key: Config.Bool.Key.V,
|
collapse_key: Config.Bool.V,
|
||||||
type: Optional[SidebarItemType] = None,
|
type: Optional[SidebarItemType] = None,
|
||||||
) -> SidebarItem:
|
) -> SidebarItem:
|
||||||
def update(expanded: bool) -> None:
|
def update(expanded: bool) -> None:
|
||||||
|
|
6
qt/aqt/data/web/js/vendor/BUILD.bazel
vendored
6
qt/aqt/data/web/js/vendor/BUILD.bazel
vendored
|
@ -1,18 +1,15 @@
|
||||||
load(
|
load(
|
||||||
"//ts:vendor.bzl",
|
"//ts:vendor.bzl",
|
||||||
|
"copy_bootstrap_js",
|
||||||
"copy_css_browser_selector",
|
"copy_css_browser_selector",
|
||||||
"copy_jquery",
|
"copy_jquery",
|
||||||
"copy_jquery_ui",
|
"copy_jquery_ui",
|
||||||
"copy_protobufjs",
|
|
||||||
"copy_bootstrap_js",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
copy_jquery(name = "jquery")
|
copy_jquery(name = "jquery")
|
||||||
|
|
||||||
copy_jquery_ui(name = "jquery-ui")
|
copy_jquery_ui(name = "jquery-ui")
|
||||||
|
|
||||||
copy_protobufjs(name = "protobufjs")
|
|
||||||
|
|
||||||
copy_css_browser_selector(name = "css-browser-selector")
|
copy_css_browser_selector(name = "css-browser-selector")
|
||||||
|
|
||||||
copy_bootstrap_js(name = "bootstrap")
|
copy_bootstrap_js(name = "bootstrap")
|
||||||
|
@ -20,7 +17,6 @@ copy_bootstrap_js(name = "bootstrap")
|
||||||
files = [
|
files = [
|
||||||
"jquery",
|
"jquery",
|
||||||
"jquery-ui",
|
"jquery-ui",
|
||||||
"protobufjs",
|
|
||||||
"css-browser-selector",
|
"css-browser-selector",
|
||||||
"bootstrap",
|
"bootstrap",
|
||||||
]
|
]
|
||||||
|
|
|
@ -137,7 +137,6 @@ class Editor:
|
||||||
],
|
],
|
||||||
js=[
|
js=[
|
||||||
"js/vendor/jquery.min.js",
|
"js/vendor/jquery.min.js",
|
||||||
"js/vendor/protobuf.min.js",
|
|
||||||
"js/editor.js",
|
"js/editor.js",
|
||||||
],
|
],
|
||||||
context=self,
|
context=self,
|
||||||
|
|
|
@ -6,7 +6,8 @@ from operator import itemgetter
|
||||||
from typing import Any, List, Optional, Sequence
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
import aqt.clayout
|
import aqt.clayout
|
||||||
from anki import Collection, stdmodels
|
from anki import stdmodels
|
||||||
|
from anki.collection import Collection
|
||||||
from anki.lang import without_unicode_isolation
|
from anki.lang import without_unicode_isolation
|
||||||
from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount
|
from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount
|
||||||
from anki.notes import Note
|
from anki.notes import Note
|
||||||
|
|
|
@ -29,7 +29,7 @@ def set_due_date_dialog(
|
||||||
*,
|
*,
|
||||||
parent: QWidget,
|
parent: QWidget,
|
||||||
card_ids: Sequence[CardId],
|
card_ids: Sequence[CardId],
|
||||||
config_key: Optional[Config.String.Key.V],
|
config_key: Optional[Config.String.V],
|
||||||
) -> Optional[CollectionOp[OpChanges]]:
|
) -> Optional[CollectionOp[OpChanges]]:
|
||||||
assert aqt.mw
|
assert aqt.mw
|
||||||
if not card_ids:
|
if not card_ids:
|
||||||
|
|
|
@ -17,7 +17,7 @@ from send2trash import send2trash
|
||||||
import anki.lang
|
import anki.lang
|
||||||
import aqt.forms
|
import aqt.forms
|
||||||
import aqt.sound
|
import aqt.sound
|
||||||
from anki import Collection
|
from anki.collection import Collection
|
||||||
from anki.db import DB
|
from anki.db import DB
|
||||||
from anki.lang import without_unicode_isolation
|
from anki.lang import without_unicode_isolation
|
||||||
from anki.sync import SyncAuth
|
from anki.sync import SyncAuth
|
||||||
|
|
|
@ -293,7 +293,6 @@ class Reviewer:
|
||||||
"js/mathjax.js",
|
"js/mathjax.js",
|
||||||
"js/vendor/mathjax/tex-chtml.js",
|
"js/vendor/mathjax/tex-chtml.js",
|
||||||
"js/reviewer.js",
|
"js/reviewer.js",
|
||||||
"js/vendor/protobuf.min.js",
|
|
||||||
"js/reviewer_extras.js",
|
"js/reviewer_extras.js",
|
||||||
],
|
],
|
||||||
context=self,
|
context=self,
|
||||||
|
|
|
@ -34,8 +34,8 @@ from PyQt5.QtWidgets import (
|
||||||
)
|
)
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki import Collection
|
|
||||||
from anki._legacy import deprecated
|
from anki._legacy import deprecated
|
||||||
|
from anki.collection import Collection
|
||||||
from anki.lang import TR, tr_legacyglobal # pylint: disable=unused-import
|
from anki.lang import TR, tr_legacyglobal # pylint: disable=unused-import
|
||||||
from anki.utils import invalidFilename, isMac, isWin, noBundledLibs, versionWithBuild
|
from anki.utils import invalidFilename, isMac, isWin, noBundledLibs, versionWithBuild
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
|
|
@ -30,8 +30,8 @@ if subprocess.run(
|
||||||
"--",
|
"--",
|
||||||
"--config-file",
|
"--config-file",
|
||||||
"qt/mypy.ini",
|
"qt/mypy.ini",
|
||||||
"bazel-bin/qt/dmypy.runfiles/net_ankiweb_anki/pylib/anki",
|
"bazel-bin/qt/dmypy.runfiles/ankidesktop/pylib/anki",
|
||||||
"bazel-bin/qt/dmypy.runfiles/net_ankiweb_anki/qt/aqt",
|
"bazel-bin/qt/dmypy.runfiles/ankidesktop/qt/aqt",
|
||||||
"--python-executable",
|
"--python-executable",
|
||||||
os.path.abspath("pip/stubs/extendsitepkgs"),
|
os.path.abspath("pip/stubs/extendsitepkgs"),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# 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
|
||||||
|
|
||||||
load("@rules_proto//proto:defs.bzl", "proto_library")
|
|
||||||
load("@rules_rust//rust:rust.bzl", "rust_binary", "rust_library", "rust_test")
|
load("@rules_rust//rust:rust.bzl", "rust_binary", "rust_library", "rust_test")
|
||||||
load("@rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script")
|
load("@rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script")
|
||||||
load(":rustfmt.bzl", "rustfmt_fix", "rustfmt_test")
|
load(":rustfmt.bzl", "rustfmt_fix", "rustfmt_test")
|
||||||
load(":clang_format.bzl", "proto_format")
|
|
||||||
load("//ts:sql_format.bzl", "sql_format")
|
load("//ts:sql_format.bzl", "sql_format")
|
||||||
|
|
||||||
# Build script
|
# Build script
|
||||||
|
@ -15,7 +13,7 @@ cargo_build_script(
|
||||||
name = "build_script",
|
name = "build_script",
|
||||||
srcs = glob(["build/*.rs"]),
|
srcs = glob(["build/*.rs"]),
|
||||||
build_script_env = {
|
build_script_env = {
|
||||||
"BACKEND_PROTO": "$(location backend.proto)",
|
"PROTO_TOP": "$(location //proto:.top_level)",
|
||||||
"PROTOC": "$(location @com_google_protobuf//:protoc)",
|
"PROTOC": "$(location @com_google_protobuf//:protoc)",
|
||||||
"RSLIB_FTL_ROOT": "$(location @rslib_ftl//:l10n.toml)",
|
"RSLIB_FTL_ROOT": "$(location @rslib_ftl//:l10n.toml)",
|
||||||
"EXTRA_FTL_ROOT": "$(location @extra_ftl//:l10n.toml)",
|
"EXTRA_FTL_ROOT": "$(location @extra_ftl//:l10n.toml)",
|
||||||
|
@ -24,9 +22,10 @@ cargo_build_script(
|
||||||
crate_root = "build/main.rs",
|
crate_root = "build/main.rs",
|
||||||
data = [
|
data = [
|
||||||
"//ftl",
|
"//ftl",
|
||||||
"backend.proto",
|
"//proto",
|
||||||
"@com_google_protobuf//:protoc",
|
"@com_google_protobuf//:protoc",
|
||||||
# bazel requires us to list these out separately
|
# bazel requires us to list these out separately
|
||||||
|
"//proto:.top_level",
|
||||||
"@rslib_ftl//:l10n.toml",
|
"@rslib_ftl//:l10n.toml",
|
||||||
"@extra_ftl//:l10n.toml",
|
"@extra_ftl//:l10n.toml",
|
||||||
],
|
],
|
||||||
|
@ -163,19 +162,3 @@ sql_format(
|
||||||
name = "sql_format",
|
name = "sql_format",
|
||||||
srcs = glob(["**/*.sql"]),
|
srcs = glob(["**/*.sql"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_format(
|
|
||||||
name = "proto_format",
|
|
||||||
srcs = ["backend.proto"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# backend.proto
|
|
||||||
#######################
|
|
||||||
|
|
||||||
proto_library(
|
|
||||||
name = "backend_proto_lib",
|
|
||||||
srcs = ["backend.proto"],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
|
|
||||||
exports_files(["backend.proto"])
|
|
||||||
|
|
1681
rslib/backend.proto
1681
rslib/backend.proto
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ pub trait Service {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
concat!(" ",
|
concat!(" ",
|
||||||
"{idx} => {{ let input = {input_type}::decode(input)?;\n",
|
"{idx} => {{ let input = super::{input_type}::decode(input)?;\n",
|
||||||
"let output = self.{rust_method}(input)?;\n",
|
"let output = self.{rust_method}(input)?;\n",
|
||||||
"let mut out_bytes = Vec::new(); output.encode(&mut out_bytes)?; Ok(out_bytes) }}, "),
|
"let mut out_bytes = Vec::new(); output.encode(&mut out_bytes)?; Ok(out_bytes) }}, "),
|
||||||
idx = idx,
|
idx = idx,
|
||||||
|
@ -38,8 +38,8 @@ pub trait Service {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
concat!(
|
concat!(
|
||||||
" fn {method_name}(&self, input: {input_type}) -> ",
|
" fn {method_name}(&self, input: super::{input_type}) -> ",
|
||||||
"Result<{output_type}>;\n"
|
"Result<super::{output_type}>;\n"
|
||||||
),
|
),
|
||||||
method_name = method.name,
|
method_name = method.name,
|
||||||
input_type = method.input_type,
|
input_type = method.input_type,
|
||||||
|
@ -55,7 +55,6 @@ impl prost_build::ServiceGenerator for CustomGenerator {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"pub mod {name}_service {{
|
"pub mod {name}_service {{
|
||||||
use super::*;
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
",
|
",
|
||||||
|
@ -73,16 +72,32 @@ fn service_generator() -> Box<dyn prost_build::ServiceGenerator> {
|
||||||
|
|
||||||
pub fn write_backend_proto_rs() {
|
pub fn write_backend_proto_rs() {
|
||||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
let backend_proto;
|
let proto_dir = if let Ok(proto) = env::var("PROTO_TOP") {
|
||||||
let proto_dir;
|
let backend_proto = PathBuf::from(proto);
|
||||||
if let Ok(proto) = env::var("BACKEND_PROTO") {
|
backend_proto.parent().unwrap().to_owned()
|
||||||
backend_proto = PathBuf::from(proto);
|
|
||||||
proto_dir = backend_proto.parent().unwrap().to_owned();
|
|
||||||
} else {
|
} else {
|
||||||
backend_proto = PathBuf::from("backend.proto");
|
PathBuf::from("../proto")
|
||||||
proto_dir = PathBuf::from(".");
|
};
|
||||||
|
|
||||||
|
let subfolders = &["anki"];
|
||||||
|
let mut paths = vec![];
|
||||||
|
|
||||||
|
for subfolder in subfolders {
|
||||||
|
for entry in proto_dir.join(subfolder).read_dir().unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
if path
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.ends_with(".proto")
|
||||||
|
{
|
||||||
|
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println!("cargo:rerun-if-changed={}", backend_proto.to_str().unwrap());
|
|
||||||
|
|
||||||
let mut config = prost_build::Config::new();
|
let mut config = prost_build::Config::new();
|
||||||
config
|
config
|
||||||
|
@ -92,6 +107,6 @@ pub fn write_backend_proto_rs() {
|
||||||
"Deck.Filtered.SearchTerm.Order",
|
"Deck.Filtered.SearchTerm.Order",
|
||||||
"#[derive(strum::EnumIter)]",
|
"#[derive(strum::EnumIter)]",
|
||||||
)
|
)
|
||||||
.compile_protos(&[&backend_proto], &[&proto_dir, &out_dir])
|
.compile_protos(paths.as_slice(), &[proto_dir, out_dir])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use super::Backend;
|
||||||
pub(super) use crate::backend_proto::config_service::Service as ConfigService;
|
pub(super) use crate::backend_proto::config_service::Service as ConfigService;
|
||||||
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_key::{Bool as BoolKeyProto, String as StringKeyProto},
|
||||||
config::{BoolKey, StringKey},
|
config::{BoolKey, StringKey},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
@ -46,12 +46,6 @@ impl From<StringKeyProto> for StringKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<pb::config::String> for StringKey {
|
|
||||||
fn from(key: pb::config::String) -> Self {
|
|
||||||
key.key().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigService for Backend {
|
impl ConfigService 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| {
|
||||||
|
@ -91,7 +85,7 @@ impl ConfigService for Backend {
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_bool(&self, input: pb::config::Bool) -> Result<pb::Bool> {
|
fn get_config_bool(&self, input: pb::GetConfigBoolRequest) -> Result<pb::Bool> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
Ok(pb::Bool {
|
Ok(pb::Bool {
|
||||||
val: col.get_config_bool(input.key().into()),
|
val: col.get_config_bool(input.key().into()),
|
||||||
|
@ -104,7 +98,7 @@ impl ConfigService for Backend {
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_string(&self, input: pb::config::String) -> Result<pb::String> {
|
fn get_config_string(&self, input: pb::GetConfigStringRequest) -> Result<pb::String> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
Ok(pb::String {
|
Ok(pb::String {
|
||||||
val: col.get_config_string(input.key().into()),
|
val: col.get_config_string(input.key().into()),
|
||||||
|
|
|
@ -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,7 +117,7 @@ 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),
|
||||||
|
|
|
@ -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> {
|
pub(super) fn to_note_ids(ids: Vec<i64>) -> Vec<NoteId> {
|
||||||
|
|
|
@ -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(
|
fn get_change_notetype_info(
|
||||||
&self,
|
&self,
|
||||||
input: pb::GetChangeNotetypeInfoRequest,
|
input: pb::GetChangeNotetypeInfoRequest,
|
||||||
|
|
|
@ -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> {
|
||||||
|
@ -117,7 +117,7 @@ impl SchedulingService for Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_due_date(&self, input: pb::SetDueDateRequest) -> Result<pb::OpChanges> {
|
fn set_due_date(&self, input: pb::SetDueDateRequest) -> Result<pb::OpChanges> {
|
||||||
let config = input.config_key.map(Into::into);
|
let config = input.config_key.map(|v| v.key().into());
|
||||||
let days = input.days;
|
let days = input.days;
|
||||||
let cids = input.card_ids.into_newtype(CardId);
|
let cids = input.card_ids.into_newtype(CardId);
|
||||||
self.with_col(|col| col.set_due_date(&cids, &days, config).map(Into::into))
|
self.with_col(|col| col.set_due_date(&cids, &days, config).map(Into::into))
|
||||||
|
|
|
@ -1,4 +1,32 @@
|
||||||
// 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
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/backend_proto.rs"));
|
macro_rules! protobuf {
|
||||||
|
($ident:ident) => {
|
||||||
|
pub mod $ident {
|
||||||
|
include!(concat!(
|
||||||
|
env!("OUT_DIR"),
|
||||||
|
concat!("/anki.", stringify!($ident), ".rs")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
pub use $ident::*;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protobuf!(backend);
|
||||||
|
protobuf!(card_rendering);
|
||||||
|
protobuf!(cards);
|
||||||
|
protobuf!(collection);
|
||||||
|
protobuf!(config);
|
||||||
|
protobuf!(deckconfig);
|
||||||
|
protobuf!(decks);
|
||||||
|
protobuf!(generic);
|
||||||
|
protobuf!(i18n);
|
||||||
|
protobuf!(media);
|
||||||
|
protobuf!(notes);
|
||||||
|
protobuf!(notetypes);
|
||||||
|
protobuf!(scheduler);
|
||||||
|
protobuf!(search);
|
||||||
|
protobuf!(stats);
|
||||||
|
protobuf!(sync);
|
||||||
|
protobuf!(tags);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue