mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
split out remaining tags, stats, media and rendering
This commit is contained in:
parent
35b059ecdb
commit
185e9acd22
44 changed files with 413 additions and 382 deletions
2
defs.bzl
2
defs.bzl
|
@ -3,7 +3,7 @@ load("@bazel_skylib//lib:versions.bzl", "versions")
|
||||||
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
|
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
|
||||||
load("@anki//cargo:crates.bzl", "raze_fetch_remote_crates")
|
load("@anki//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("//proto: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")
|
||||||
|
|
|
@ -5,15 +5,6 @@ syntax = "proto3";
|
||||||
|
|
||||||
package anki.backend;
|
package anki.backend;
|
||||||
|
|
||||||
import "anki/generic.proto";
|
|
||||||
import "anki/cards.proto";
|
|
||||||
import "anki/collection.proto";
|
|
||||||
import "anki/notes.proto";
|
|
||||||
import "anki/notetypes.proto";
|
|
||||||
|
|
||||||
// Backend methods
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/// while the protobuf descriptors expose the order services are defined in,
|
/// 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
|
/// that information is not available in prost, so we define an enum to make
|
||||||
/// sure all clients agree on the service index
|
/// sure all clients agree on the service index
|
||||||
|
@ -35,54 +26,6 @@ enum ServiceIndex {
|
||||||
SERVICE_INDEX_CARDS = 14;
|
SERVICE_INDEX_CARDS = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
message BackendInit {
|
message BackendInit {
|
||||||
repeated string preferred_langs = 1;
|
repeated string preferred_langs = 1;
|
||||||
string locale_folder_path = 2;
|
string locale_folder_path = 2;
|
||||||
|
@ -94,9 +37,6 @@ message I18nBackendInit {
|
||||||
string locale_folder_path = 5;
|
string locale_folder_path = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
message BackendError {
|
message BackendError {
|
||||||
enum Kind {
|
enum Kind {
|
||||||
INVALID_INPUT = 0;
|
INVALID_INPUT = 0;
|
||||||
|
@ -121,208 +61,3 @@ message BackendError {
|
||||||
// the error subtype
|
// the error subtype
|
||||||
Kind kind = 2;
|
Kind kind = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
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 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 AddMediaFileRequest {
|
|
||||||
string desired_name = 1;
|
|
||||||
bytes data = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CheckMediaResponse {
|
|
||||||
repeated string unused = 1;
|
|
||||||
repeated string missing = 2;
|
|
||||||
string report = 3;
|
|
||||||
bool have_trash = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TrashMediaFilesRequest {
|
|
||||||
repeated string fnames = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CongratsLearnMessageRequest {
|
|
||||||
float next_due = 1;
|
|
||||||
uint32 remaining = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SetTagCollapsedRequest {
|
|
||||||
string name = 1;
|
|
||||||
bool collapsed = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetChangedTagsResponse {
|
|
||||||
repeated string tags = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
message RenderMarkdownRequest {
|
|
||||||
string markdown = 1;
|
|
||||||
bool sanitize = 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;
|
||||||
|
}
|
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;
|
||||||
|
}
|
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;
|
||||||
|
}
|
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;
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ py_library(
|
||||||
"py.typed",
|
"py.typed",
|
||||||
":buildinfo",
|
":buildinfo",
|
||||||
":hooks_gen",
|
":hooks_gen",
|
||||||
":proto",
|
":proto_py",
|
||||||
"//pylib/anki/_backend",
|
"//pylib/anki/_backend",
|
||||||
],
|
],
|
||||||
imports = [
|
imports = [
|
||||||
|
@ -110,26 +110,17 @@ filegroup(
|
||||||
load("//pylib:protobuf.bzl", "py_proto")
|
load("//pylib:protobuf.bzl", "py_proto")
|
||||||
|
|
||||||
py_proto(
|
py_proto(
|
||||||
name = "proto_files",
|
name = "proto_py",
|
||||||
srcs = ["//proto"],
|
srcs = ["//proto"],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "proto",
|
|
||||||
srcs = [
|
|
||||||
# "__init__.py",
|
|
||||||
":proto_files",
|
|
||||||
],
|
|
||||||
visibility = ["//pylib:__subpackages__"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# only used for genbackend.py
|
# only used for genbackend.py
|
||||||
py_library(
|
py_library(
|
||||||
name = "proto_lib",
|
name = "proto_lib",
|
||||||
srcs = [":proto"],
|
srcs = [":proto_py", "__init__.py"],
|
||||||
imports = [".."],
|
imports = [".."],
|
||||||
visibility = ["//pylib:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
# 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 os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from anki.buildinfo import version
|
if not os.getenv("PROTOS_ONLY"):
|
||||||
from anki.collection import Collection
|
from anki.buildinfo import version
|
||||||
|
from anki.collection import Collection
|
||||||
|
|
||||||
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
|
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
|
||||||
raise Exception("Anki requires Python 3.7+")
|
raise Exception("Anki requires Python 3.7+")
|
||||||
|
|
||||||
# ensure unicode filenames are supported
|
# ensure unicode filenames are supported
|
||||||
try:
|
try:
|
||||||
"テスト".encode(sys.getfilesystemencoding())
|
"テスト".encode(sys.getfilesystemencoding())
|
||||||
except UnicodeEncodeError as exc:
|
except UnicodeEncodeError as exc:
|
||||||
raise Exception("Anki requires a UTF-8 locale.") from exc
|
raise Exception("Anki requires a UTF-8 locale.") from exc
|
||||||
|
|
||||||
__all__ = ["Collection"]
|
__all__ = ["Collection"]
|
||||||
|
|
|
@ -8,6 +8,8 @@ import sys
|
||||||
|
|
||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
|
|
||||||
|
os.environ["PROTOS_ONLY"] = "1"
|
||||||
|
|
||||||
import anki.backend_pb2
|
import anki.backend_pb2
|
||||||
import anki.i18n_pb2
|
import anki.i18n_pb2
|
||||||
import anki.cards_pb2
|
import anki.cards_pb2
|
||||||
|
@ -20,6 +22,10 @@ import anki.scheduler_pb2
|
||||||
import anki.sync_pb2
|
import anki.sync_pb2
|
||||||
import anki.configs_pb2
|
import anki.configs_pb2
|
||||||
import anki.search_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
|
||||||
|
|
||||||
|
@ -187,12 +193,16 @@ service_modules = dict(
|
||||||
SYNC=anki.sync_pb2,
|
SYNC=anki.sync_pb2,
|
||||||
CONFIGS=anki.configs_pb2,
|
CONFIGS=anki.configs_pb2,
|
||||||
SEARCH=anki.search_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:
|
for service in anki.backend_pb2.ServiceIndex.DESCRIPTOR.values:
|
||||||
# SERVICE_INDEX_TEST -> _TESTSERVICE
|
# SERVICE_INDEX_TEST -> _TESTSERVICE
|
||||||
base = service.name.replace("SERVICE_INDEX_", "")
|
base = service.name.replace("SERVICE_INDEX_", "")
|
||||||
service_pkg = service_modules.get(base) or anki.backend_pb2
|
service_pkg = service_modules.get(base)
|
||||||
service_var = "_" + base.replace("_", "") + "SERVICE"
|
service_var = "_" + base.replace("_", "") + "SERVICE"
|
||||||
service_obj = getattr(service_pkg, service_var)
|
service_obj = getattr(service_pkg, service_var)
|
||||||
service_index = service.number
|
service_index = service.number
|
||||||
|
|
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
|
|
@ -7,15 +7,21 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union, cast
|
from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union, cast
|
||||||
|
|
||||||
import anki.backend_pb2 as _pb
|
from anki import (
|
||||||
from anki import collection_pb2, configs_pb2, generic_pb2, search_pb2
|
card_rendering_pb2,
|
||||||
|
collection_pb2,
|
||||||
|
configs_pb2,
|
||||||
|
generic_pb2,
|
||||||
|
search_pb2,
|
||||||
|
stats_pb2,
|
||||||
|
)
|
||||||
from anki._legacy import DeprecatedNamesMixin, deprecated
|
from anki._legacy import DeprecatedNamesMixin, deprecated
|
||||||
|
|
||||||
# protobuf we publicly export - listed first to avoid circular imports
|
# protobuf we publicly export - listed first to avoid circular imports
|
||||||
SearchNode = search_pb2.SearchNode
|
SearchNode = search_pb2.SearchNode
|
||||||
Progress = collection_pb2.Progress
|
Progress = collection_pb2.Progress
|
||||||
EmptyCardsReport = _pb.EmptyCardsReport
|
EmptyCardsReport = card_rendering_pb2.EmptyCardsReport
|
||||||
GraphPreferences = _pb.GraphPreferences
|
GraphPreferences = stats_pb2.GraphPreferences
|
||||||
Preferences = configs_pb2.Preferences
|
Preferences = configs_pb2.Preferences
|
||||||
UndoStatus = collection_pb2.UndoStatus
|
UndoStatus = collection_pb2.UndoStatus
|
||||||
OpChanges = collection_pb2.OpChanges
|
OpChanges = collection_pb2.OpChanges
|
||||||
|
|
|
@ -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_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_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
|
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
|
|
@ -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_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_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))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ import sys
|
||||||
|
|
||||||
(protoc, mypy_protobuf, outdir, *protos) = sys.argv[1:]
|
(protoc, mypy_protobuf, outdir, *protos) = sys.argv[1:]
|
||||||
|
|
||||||
prefix = "proto/"
|
if protos[0].startswith("external"):
|
||||||
|
prefix = "external/anki/proto/"
|
||||||
|
else:
|
||||||
|
prefix = "proto/"
|
||||||
|
|
||||||
# invoke protoc
|
# invoke protoc
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
|
|
|
@ -14,6 +14,7 @@ macro_rules! protobuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
protobuf!(backend);
|
protobuf!(backend);
|
||||||
|
protobuf!(card_rendering);
|
||||||
protobuf!(cards);
|
protobuf!(cards);
|
||||||
protobuf!(collection);
|
protobuf!(collection);
|
||||||
protobuf!(configs);
|
protobuf!(configs);
|
||||||
|
@ -21,8 +22,11 @@ protobuf!(deckconfig);
|
||||||
protobuf!(decks);
|
protobuf!(decks);
|
||||||
protobuf!(generic);
|
protobuf!(generic);
|
||||||
protobuf!(i18n);
|
protobuf!(i18n);
|
||||||
|
protobuf!(media);
|
||||||
protobuf!(notes);
|
protobuf!(notes);
|
||||||
protobuf!(notetypes);
|
protobuf!(notetypes);
|
||||||
protobuf!(scheduler);
|
protobuf!(scheduler);
|
||||||
protobuf!(search);
|
protobuf!(search);
|
||||||
|
protobuf!(stats);
|
||||||
protobuf!(sync);
|
protobuf!(sync);
|
||||||
|
protobuf!(tags);
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
import type { PreferenceStore } from "sveltelib/preferences";
|
import type { PreferenceStore } from "sveltelib/preferences";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, buildHistogram } from "./added";
|
import { gatherData, buildHistogram } from "./added";
|
||||||
import type { GraphData } from "./added";
|
import type { GraphData } from "./added";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
|
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
let tableData: TableDatum[] = [];
|
let tableData: TableDatum[] = [];
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
import InputBox from "./InputBox.svelte";
|
import InputBox from "./InputBox.svelte";
|
||||||
|
@ -14,7 +14,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { renderButtons } from "./buttons";
|
import { renderButtons } from "./buttons";
|
||||||
import { defaultGraphBounds, GraphRange, RevlogRange } from "./graph-helpers";
|
import { defaultGraphBounds, GraphRange, RevlogRange } from "./graph-helpers";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let revlogRange: RevlogRange;
|
export let revlogRange: RevlogRange;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
import type { PreferenceStore } from "sveltelib/preferences";
|
import type { PreferenceStore } from "sveltelib/preferences";
|
||||||
|
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
@ -18,8 +18,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, renderCalendar } from "./calendar";
|
import { gatherData, renderCalendar } from "./calendar";
|
||||||
import type { GraphData } from "./calendar";
|
import type { GraphData } from "./calendar";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse;
|
export let sourceData: Stats.GraphsResponse;
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
export let revlogRange: RevlogRange;
|
export let revlogRange: RevlogRange;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let nightMode: boolean;
|
export let nightMode: boolean;
|
||||||
|
|
|
@ -4,7 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
import type { PreferenceStore } from "sveltelib/preferences";
|
import type { PreferenceStore } from "sveltelib/preferences";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
|
@ -15,9 +15,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, renderCards } from "./card-counts";
|
import { gatherData, renderCards } from "./card-counts";
|
||||||
import type { GraphData, TableDatum } from "./card-counts";
|
import type { GraphData, TableDatum } from "./card-counts";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse;
|
export let sourceData: Stats.GraphsResponse;
|
||||||
import * as tr2 from "lib/i18n";
|
import * as tr2 from "lib/i18n";
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
|
|
||||||
let { cardCountsSeparateInactive, browserLinksSupported } = preferences;
|
let { cardCountsSeparateInactive, browserLinksSupported } = preferences;
|
||||||
const dispatch = createEventDispatcher<SearchEventMap>();
|
const dispatch = createEventDispatcher<SearchEventMap>();
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
import type { PreferenceStore } from "sveltelib/preferences";
|
import type { PreferenceStore } from "sveltelib/preferences";
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, prepareData } from "./ease";
|
import { gatherData, prepareData } from "./ease";
|
||||||
import type { TableDatum, SearchEventMap } from "./graph-helpers";
|
import type { TableDatum, SearchEventMap } from "./graph-helpers";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<SearchEventMap>();
|
const dispatch = createEventDispatcher<SearchEventMap>();
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
import InputBox from "./InputBox.svelte";
|
import InputBox from "./InputBox.svelte";
|
||||||
|
@ -20,9 +20,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, buildHistogram } from "./future-due";
|
import { gatherData, buildHistogram } from "./future-due";
|
||||||
import type { GraphData } from "./future-due";
|
import type { GraphData } from "./future-due";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<SearchEventMap>();
|
const dispatch = createEventDispatcher<SearchEventMap>();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
import InputBox from "./InputBox.svelte";
|
import InputBox from "./InputBox.svelte";
|
||||||
|
@ -15,7 +15,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { defaultGraphBounds, RevlogRange, GraphRange } from "./graph-helpers";
|
import { defaultGraphBounds, RevlogRange, GraphRange } from "./graph-helpers";
|
||||||
import { renderHours } from "./hours";
|
import { renderHours } from "./hours";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let revlogRange: RevlogRange;
|
export let revlogRange: RevlogRange;
|
||||||
let graphRange: GraphRange = GraphRange.Year;
|
let graphRange: GraphRange = GraphRange.Year;
|
||||||
|
|
|
@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { timeSpan, MONTH } from "lib/time";
|
import { timeSpan, MONTH } from "lib/time";
|
||||||
|
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
import type { PreferenceStore } from "sveltelib/preferences";
|
import type { PreferenceStore } from "sveltelib/preferences";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import type { IntervalGraphData } from "./intervals";
|
import type { IntervalGraphData } from "./intervals";
|
||||||
import type { TableDatum, SearchEventMap } from "./graph-helpers";
|
import type { TableDatum, SearchEventMap } from "./graph-helpers";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
export let preferences: PreferenceStore<Backend.GraphPreferences>;
|
export let preferences: PreferenceStore<Stats.GraphPreferences>;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<SearchEventMap>();
|
const dispatch = createEventDispatcher<SearchEventMap>();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
import InputBox from "./InputBox.svelte";
|
import InputBox from "./InputBox.svelte";
|
||||||
|
@ -19,7 +19,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { gatherData, renderReviews } from "./reviews";
|
import { gatherData, renderReviews } from "./reviews";
|
||||||
import type { GraphData } from "./reviews";
|
import type { GraphData } from "./reviews";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
export let revlogRange: RevlogRange;
|
export let revlogRange: RevlogRange;
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Backend } from "lib/proto";
|
import type { Stats } from "lib/proto";
|
||||||
|
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
|
|
||||||
import type { TodayData } from "./today";
|
import type { TodayData } from "./today";
|
||||||
import { gatherData } from "./today";
|
import { gatherData } from "./today";
|
||||||
|
|
||||||
export let sourceData: Backend.GraphsResponse | null = null;
|
export let sourceData: Stats.GraphsResponse | null = null;
|
||||||
|
|
||||||
let todayData: TodayData | null = null;
|
let todayData: TodayData | null = null;
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
import type { PreferenceRaw, PreferencePayload } from "sveltelib/preferences";
|
import type { PreferenceRaw, PreferencePayload } from "sveltelib/preferences";
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
import { postRequest } from "lib/postrequest";
|
import { postRequest } from "lib/postrequest";
|
||||||
|
|
||||||
import useAsync from "sveltelib/async";
|
import useAsync from "sveltelib/async";
|
||||||
|
@ -21,24 +21,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
async function getGraphData(
|
async function getGraphData(
|
||||||
search: string,
|
search: string,
|
||||||
days: number
|
days: number
|
||||||
): Promise<Backend.GraphsResponse> {
|
): Promise<Stats.GraphsResponse> {
|
||||||
return Backend.GraphsResponse.decode(
|
return Stats.GraphsResponse.decode(
|
||||||
await postRequest("/_anki/graphData", JSON.stringify({ search, days }))
|
await postRequest("/_anki/graphData", JSON.stringify({ search, days }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGraphPreferences(): Promise<Backend.GraphPreferences> {
|
async function getGraphPreferences(): Promise<Stats.GraphPreferences> {
|
||||||
return Backend.GraphPreferences.decode(
|
return Stats.GraphPreferences.decode(
|
||||||
await postRequest("/_anki/graphPreferences", JSON.stringify({}))
|
await postRequest("/_anki/graphPreferences", JSON.stringify({}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setGraphPreferences(
|
async function setGraphPreferences(
|
||||||
prefs: PreferencePayload<Backend.GraphPreferences>
|
prefs: PreferencePayload<Stats.GraphPreferences>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await postRequest(
|
await postRequest(
|
||||||
"/_anki/setGraphPreferences",
|
"/_anki/setGraphPreferences",
|
||||||
Backend.GraphPreferences.encode(prefs).finish()
|
Stats.GraphPreferences.encode(prefs).finish()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,10 +56,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
getPreferences(
|
getPreferences(
|
||||||
getGraphPreferences,
|
getGraphPreferences,
|
||||||
setGraphPreferences,
|
setGraphPreferences,
|
||||||
Backend.GraphPreferences.toObject.bind(Backend.GraphPreferences) as (
|
Stats.GraphPreferences.toObject.bind(Stats.GraphPreferences) as (
|
||||||
preferences: Backend.GraphPreferences,
|
preferences: Stats.GraphPreferences,
|
||||||
options: { defaults: boolean }
|
options: { defaults: boolean }
|
||||||
) => PreferenceRaw<Backend.GraphPreferences>
|
) => PreferenceRaw<Stats.GraphPreferences>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
extent,
|
extent,
|
||||||
|
@ -28,7 +28,7 @@ export interface GraphData {
|
||||||
daysAdded: number[];
|
daysAdded: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse): GraphData {
|
export function gatherData(data: Stats.GraphsResponse): GraphData {
|
||||||
const daysAdded = (data.cards as Cards.Card[]).map((card) => {
|
const daysAdded = (data.cards as Cards.Card[]).map((card) => {
|
||||||
const elapsedSecs = (card.id as number) / 1000 - data.nextDayAtSecs;
|
const elapsedSecs = (card.id as number) / 1000 - data.nextDayAtSecs;
|
||||||
return Math.ceil(elapsedSecs / 86400);
|
return Math.ceil(elapsedSecs / 86400);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
interpolateRdYlGn,
|
interpolateRdYlGn,
|
||||||
|
@ -36,15 +36,15 @@ export interface GraphData {
|
||||||
mature: ButtonCounts;
|
mature: ButtonCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewKind = Backend.RevlogEntry.ReviewKind;
|
const ReviewKind = Stats.RevlogEntry.ReviewKind;
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse, range: GraphRange): GraphData {
|
export function gatherData(data: Stats.GraphsResponse, range: GraphRange): GraphData {
|
||||||
const cutoff = millisecondCutoffForRange(range, data.nextDayAtSecs);
|
const cutoff = millisecondCutoffForRange(range, data.nextDayAtSecs);
|
||||||
const learning: ButtonCounts = [0, 0, 0, 0];
|
const learning: ButtonCounts = [0, 0, 0, 0];
|
||||||
const young: ButtonCounts = [0, 0, 0, 0];
|
const young: ButtonCounts = [0, 0, 0, 0];
|
||||||
const mature: ButtonCounts = [0, 0, 0, 0];
|
const mature: ButtonCounts = [0, 0, 0, 0];
|
||||||
|
|
||||||
for (const review of data.revlog as Backend.RevlogEntry[]) {
|
for (const review of data.revlog as Stats.RevlogEntry[]) {
|
||||||
if (cutoff && (review.id as number) < cutoff) {
|
if (cutoff && (review.id as number) < cutoff) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ interface TotalCorrect {
|
||||||
export function renderButtons(
|
export function renderButtons(
|
||||||
svgElem: SVGElement,
|
svgElem: SVGElement,
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
origData: Backend.GraphsResponse,
|
origData: Stats.GraphsResponse,
|
||||||
range: GraphRange
|
range: GraphRange
|
||||||
): void {
|
): void {
|
||||||
const sourceData = gatherData(origData, range);
|
const sourceData = gatherData(origData, range);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
@typescript-eslint/no-non-null-assertion: "off",
|
@typescript-eslint/no-non-null-assertion: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
interpolateBlues,
|
interpolateBlues,
|
||||||
select,
|
select,
|
||||||
|
@ -49,16 +49,16 @@ interface DayDatum {
|
||||||
date: Date;
|
date: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
type WeekdayType = Backend.GraphPreferences.Weekday;
|
type WeekdayType = Stats.GraphPreferences.Weekday;
|
||||||
const Weekday = Backend.GraphPreferences.Weekday; /* enum */
|
const Weekday = Stats.GraphPreferences.Weekday; /* enum */
|
||||||
|
|
||||||
export function gatherData(
|
export function gatherData(
|
||||||
data: Backend.GraphsResponse,
|
data: Stats.GraphsResponse,
|
||||||
firstDayOfWeek: WeekdayType
|
firstDayOfWeek: WeekdayType
|
||||||
): GraphData {
|
): GraphData {
|
||||||
const reviewCount = new Map<number, number>();
|
const reviewCount = new Map<number, number>();
|
||||||
|
|
||||||
for (const review of data.revlog as Backend.RevlogEntry[]) {
|
for (const review of data.revlog as Stats.RevlogEntry[]) {
|
||||||
if (review.buttonChosen == 0) {
|
if (review.buttonChosen == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CardQueue, CardType } from "lib/cards";
|
import { CardQueue, CardType } from "lib/cards";
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
schemeGreens,
|
schemeGreens,
|
||||||
schemeBlues,
|
schemeBlues,
|
||||||
|
@ -124,7 +124,7 @@ function countCards(cards: Cards.ICard[], separateInactive: boolean): Count[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(
|
export function gatherData(
|
||||||
data: Backend.GraphsResponse,
|
data: Stats.GraphsResponse,
|
||||||
separateInactive: boolean
|
separateInactive: boolean
|
||||||
): GraphData {
|
): GraphData {
|
||||||
const totalCards = data.cards.length;
|
const totalCards = data.cards.length;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
extent,
|
extent,
|
||||||
histogram,
|
histogram,
|
||||||
|
@ -26,7 +26,7 @@ export interface GraphData {
|
||||||
eases: number[];
|
eases: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse): GraphData {
|
export function gatherData(data: Stats.GraphsResponse): GraphData {
|
||||||
const eases = (data.cards as Cards.Card[])
|
const eases = (data.cards as Cards.Card[])
|
||||||
.filter((c) => [CardType.Review, CardType.Relearn].includes(c.ctype))
|
.filter((c) => [CardType.Review, CardType.Relearn].includes(c.ctype))
|
||||||
.map((c) => c.easeFactor / 10);
|
.map((c) => c.easeFactor / 10);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
extent,
|
extent,
|
||||||
histogram,
|
histogram,
|
||||||
|
@ -30,7 +30,7 @@ export interface GraphData {
|
||||||
haveBacklog: boolean;
|
haveBacklog: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse): GraphData {
|
export function gatherData(data: Stats.GraphsResponse): GraphData {
|
||||||
const isLearning = (card: Cards.Card): boolean =>
|
const isLearning = (card: Cards.Card): boolean =>
|
||||||
[CardQueue.Learn, CardQueue.PreviewRepeat].includes(card.queue);
|
[CardQueue.Learn, CardQueue.PreviewRepeat].includes(card.queue);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
@typescript-eslint/ban-ts-comment: "off" */
|
@typescript-eslint/ban-ts-comment: "off" */
|
||||||
|
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
import type { Selection } from "d3";
|
import type { Selection } from "d3";
|
||||||
|
|
||||||
// amount of data to fetch from backend
|
// amount of data to fetch from backend
|
||||||
|
@ -29,7 +29,7 @@ export enum GraphRange {
|
||||||
|
|
||||||
export interface GraphsContext {
|
export interface GraphsContext {
|
||||||
cards: Cards.Card[];
|
cards: Cards.Card[];
|
||||||
revlog: Backend.RevlogEntry[];
|
revlog: Stats.RevlogEntry[];
|
||||||
revlogRange: RevlogRange;
|
revlogRange: RevlogRange;
|
||||||
nightMode: boolean;
|
nightMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
interpolateBlues,
|
interpolateBlues,
|
||||||
select,
|
select,
|
||||||
|
@ -37,15 +37,15 @@ interface Hour {
|
||||||
correctCount: number;
|
correctCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewKind = Backend.RevlogEntry.ReviewKind;
|
const ReviewKind = Stats.RevlogEntry.ReviewKind;
|
||||||
|
|
||||||
function gatherData(data: Backend.GraphsResponse, range: GraphRange): Hour[] {
|
function gatherData(data: Stats.GraphsResponse, range: GraphRange): Hour[] {
|
||||||
const hours = [...Array(24)].map((_n, idx: number) => {
|
const hours = [...Array(24)].map((_n, idx: number) => {
|
||||||
return { hour: idx, totalCount: 0, correctCount: 0 } as Hour;
|
return { hour: idx, totalCount: 0, correctCount: 0 } as Hour;
|
||||||
});
|
});
|
||||||
const cutoff = millisecondCutoffForRange(range, data.nextDayAtSecs);
|
const cutoff = millisecondCutoffForRange(range, data.nextDayAtSecs);
|
||||||
|
|
||||||
for (const review of data.revlog as Backend.RevlogEntry[]) {
|
for (const review of data.revlog as Stats.RevlogEntry[]) {
|
||||||
switch (review.reviewKind) {
|
switch (review.reviewKind) {
|
||||||
case ReviewKind.LEARNING:
|
case ReviewKind.LEARNING:
|
||||||
case ReviewKind.REVIEW:
|
case ReviewKind.REVIEW:
|
||||||
|
@ -74,7 +74,7 @@ function gatherData(data: Backend.GraphsResponse, range: GraphRange): Hour[] {
|
||||||
export function renderHours(
|
export function renderHours(
|
||||||
svgElem: SVGElement,
|
svgElem: SVGElement,
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
origData: Backend.GraphsResponse,
|
origData: Stats.GraphsResponse,
|
||||||
range: GraphRange
|
range: GraphRange
|
||||||
): void {
|
): void {
|
||||||
const data = gatherData(origData, range);
|
const data = gatherData(origData, range);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Backend, Cards } from "lib/proto";
|
import type { Stats, Cards } from "lib/proto";
|
||||||
import {
|
import {
|
||||||
extent,
|
extent,
|
||||||
histogram,
|
histogram,
|
||||||
|
@ -36,7 +36,7 @@ export enum IntervalRange {
|
||||||
All = 3,
|
All = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherIntervalData(data: Backend.GraphsResponse): IntervalGraphData {
|
export function gatherIntervalData(data: Stats.GraphsResponse): IntervalGraphData {
|
||||||
const intervals = (data.cards as Cards.Card[])
|
const intervals = (data.cards as Cards.Card[])
|
||||||
.filter((c) => [CardType.Review, CardType.Relearn].includes(c.ctype))
|
.filter((c) => [CardType.Review, CardType.Relearn].includes(c.ctype))
|
||||||
.map((c) => c.interval);
|
.map((c) => c.interval);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
@typescript-eslint/no-explicit-any: "off",
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
|
|
||||||
import { timeSpan, dayLabel } from "lib/time";
|
import { timeSpan, dayLabel } from "lib/time";
|
||||||
import {
|
import {
|
||||||
|
@ -50,15 +50,15 @@ export interface GraphData {
|
||||||
reviewTime: Map<number, Reviews>;
|
reviewTime: Map<number, Reviews>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewKind = Backend.RevlogEntry.ReviewKind;
|
const ReviewKind = Stats.RevlogEntry.ReviewKind;
|
||||||
type BinType = Bin<Map<number, Reviews[]>, number>;
|
type BinType = Bin<Map<number, Reviews[]>, number>;
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse): GraphData {
|
export function gatherData(data: Stats.GraphsResponse): GraphData {
|
||||||
const reviewCount = new Map<number, Reviews>();
|
const reviewCount = new Map<number, Reviews>();
|
||||||
const reviewTime = new Map<number, Reviews>();
|
const reviewTime = new Map<number, Reviews>();
|
||||||
const empty = { mature: 0, young: 0, learn: 0, relearn: 0, early: 0 };
|
const empty = { mature: 0, young: 0, learn: 0, relearn: 0, early: 0 };
|
||||||
|
|
||||||
for (const review of data.revlog as Backend.RevlogEntry[]) {
|
for (const review of data.revlog as Stats.RevlogEntry[]) {
|
||||||
if (review.reviewKind == ReviewKind.MANUAL) {
|
if (review.reviewKind == ReviewKind.MANUAL) {
|
||||||
// don't count days with only manual scheduling
|
// don't count days with only manual scheduling
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import { Backend } from "lib/proto";
|
import { Stats } from "lib/proto";
|
||||||
import { studiedToday } from "lib/time";
|
import { studiedToday } from "lib/time";
|
||||||
|
|
||||||
import * as tr from "lib/i18n";
|
import * as tr from "lib/i18n";
|
||||||
|
@ -11,9 +11,9 @@ export interface TodayData {
|
||||||
lines: string[];
|
lines: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewKind = Backend.RevlogEntry.ReviewKind;
|
const ReviewKind = Stats.RevlogEntry.ReviewKind;
|
||||||
|
|
||||||
export function gatherData(data: Backend.GraphsResponse): TodayData {
|
export function gatherData(data: Stats.GraphsResponse): TodayData {
|
||||||
let answerCount = 0;
|
let answerCount = 0;
|
||||||
let answerMillis = 0;
|
let answerMillis = 0;
|
||||||
let correctCount = 0;
|
let correctCount = 0;
|
||||||
|
@ -26,7 +26,7 @@ export function gatherData(data: Backend.GraphsResponse): TodayData {
|
||||||
|
|
||||||
const startOfTodayMillis = (data.nextDayAtSecs - 86400) * 1000;
|
const startOfTodayMillis = (data.nextDayAtSecs - 86400) * 1000;
|
||||||
|
|
||||||
for (const review of data.revlog as Backend.RevlogEntry[]) {
|
for (const review of data.revlog as Stats.RevlogEntry[]) {
|
||||||
if (review.id < startOfTodayMillis) {
|
if (review.id < startOfTodayMillis) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
// 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 } from "./backend_proto";
|
import { anki } from "./backend_proto";
|
||||||
import Backend = anki.backend;
|
|
||||||
import Cards = anki.cards;
|
import Cards = anki.cards;
|
||||||
import DeckConfig = anki.deckconfig;
|
import DeckConfig = anki.deckconfig;
|
||||||
import Notetypes = anki.notetypes;
|
import Notetypes = anki.notetypes;
|
||||||
import Scheduler = anki.scheduler;
|
import Scheduler = anki.scheduler;
|
||||||
export { Backend, Cards, DeckConfig, Notetypes, Scheduler };
|
import Stats = anki.stats;
|
||||||
|
export { Stats, Cards, DeckConfig, Notetypes, Scheduler };
|
||||||
|
|
Loading…
Reference in a new issue