Anki/proto/anki/stats.proto
Damien Elmes 6941bccde4 Add support for proto3 optional scalars
Protobuf 3.15 introduced support for marking scalar fields like
uint32 as optional, and all of our tooling appears to support it
now. This allows us to use simple optional/null checks in our Rust/
TypeScript code, without having to resort to an inner message.

I had to apply a minor patch to protobufjs to get this working with
the json-module output; this has also been submitted upstream:
https://github.com/protobufjs/protobuf.js/pull/1693

I've modified CardStatsResponse as an example of the new syntax.

One thing to note: while the Rust and TypeScript bindings use optional/
null fields, as that is the norm in those languages, Google's Python
bindings are not very Pythonic. Referencing an optional field that is
missing will yield the default value, and a separate HasField() call
is required, eg:

```
>>> from anki.stats_pb2 import CardStatsResponse as R
... msg = R.FromString(b"")
... print(msg.first_review)
... print(msg.HasField("first_review"))
0
False
```
2022-02-27 19:42:06 +10:00

98 lines
2.3 KiB
Protocol Buffer

// 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 (CardStatsResponse);
rpc Graphs(GraphsRequest) returns (GraphsResponse);
rpc GetGraphPreferences(generic.Empty) returns (GraphPreferences);
rpc SetGraphPreferences(GraphPreferences) returns (generic.Empty);
}
message CardStatsResponse {
message StatsRevlogEntry {
int64 time = 1;
RevlogEntry.ReviewKind review_kind = 2;
uint32 button_chosen = 3;
// seconds
uint32 interval = 4;
// per mill
uint32 ease = 5;
float taken_secs = 6;
}
repeated StatsRevlogEntry revlog = 1;
int64 card_id = 2;
int64 note_id = 3;
string deck = 4;
// Unix timestamps
int64 added = 5;
optional int64 first_review = 6;
optional int64 latest_review = 7;
optional int64 due_date = 8;
optional int32 due_position = 9;
// days
uint32 interval = 10;
// per mill
uint32 ease = 11;
uint32 reviews = 12;
uint32 lapses = 13;
float average_secs = 14;
float total_secs = 15;
string card_type = 16;
string notetype = 17;
}
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;
// Recent Anki versions only use this when rescheduling disabled
FILTERED = 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;
}