Anki/proto/anki/import_export.proto
RumovZ 6da5e5b042
CSV import/export fixes and features (#1898)
* Fix footer moving upwards

* Fix column detection

Was broken because escaped line breaks were not considered.
Also removes delimiter detection on `#columns:` line. User must use tabs
or set delimiter beforehand.

* Add CSV preview

* Parse `#tags column:`

* Optionally export deck and notetype with CSV

* Avoid clones in CSV export

* Prevent bottom of page appearing under footer (dae)

* Increase padding to 1em (dae)

With 0.5em, when a vertical scrollbar is shown, it sits right next to
the right edge of the content, making it look like there's no right
margin.

* Experimental changes to make table fit+scroll (dae)

- limit individual cells to 15em, and show ellipses when truncated
- limit total table width to body width, so that inner table is shown
with scrollbar
- use class rather than id - ids are bad practice in Svelte components,
as more than one may be displayed on a single page

* Skip importing foreign notes with filtered decks

Were implicitly imported into the default deck before.
Also some refactoring to fetch deck ids and names beforehand.

* Hide spacer below hidden field mapping

* Fix guid being replaced when updating note

* Fix dupe identity check

Canonify tags before checking if dupe is identical, but only add update
tags later if appropriate.

* Fix deck export for notes with missing card 1

* Fix note lines starting with `#`

csv crate doesn't support escaping a leading comment char. :(

* Support import/export of guids

* Strip HTML from preview rows

* Fix initially set deck if current is filtered

* Make isHtml toggle reactive

* Fix `html_to_text_line()` stripping sound names

* Tweak export option labels

* Switch to patched rust-csv fork

Fixes writing lines starting with `#`, so revert 5ece10ad05.

* List column options with first column field

* Fix flag for exports with HTML stripped
2022-06-09 10:28:01 +10:00

194 lines
5.1 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.import_export;
import "anki/cards.proto";
import "anki/collection.proto";
import "anki/notes.proto";
import "anki/generic.proto";
service ImportExportService {
rpc ImportCollectionPackage(ImportCollectionPackageRequest)
returns (generic.Empty);
rpc ExportCollectionPackage(ExportCollectionPackageRequest)
returns (generic.Empty);
rpc ImportAnkiPackage(ImportAnkiPackageRequest) returns (ImportResponse);
rpc ExportAnkiPackage(ExportAnkiPackageRequest) returns (generic.UInt32);
rpc GetCsvMetadata(CsvMetadataRequest) returns (CsvMetadata);
rpc ImportCsv(ImportCsvRequest) returns (ImportResponse);
rpc ExportNoteCsv(ExportNoteCsvRequest) returns (generic.UInt32);
rpc ExportCardCsv(ExportCardCsvRequest) returns (generic.UInt32);
rpc ImportJsonFile(generic.String) returns (ImportResponse);
rpc ImportJsonString(generic.String) returns (ImportResponse);
}
message ImportCollectionPackageRequest {
string col_path = 1;
string backup_path = 2;
string media_folder = 3;
string media_db = 4;
}
message ExportCollectionPackageRequest {
string out_path = 1;
bool include_media = 2;
bool legacy = 3;
}
message ImportAnkiPackageRequest {
string package_path = 1;
}
message ImportResponse {
message Note {
notes.NoteId id = 1;
repeated string fields = 2;
}
message Log {
repeated Note new = 1;
repeated Note updated = 2;
repeated Note duplicate = 3;
repeated Note conflicting = 4;
repeated Note first_field_match = 5;
repeated Note missing_notetype = 6;
repeated Note missing_deck = 7;
repeated Note empty_first_field = 8;
ImportCsvRequest.DupeResolution dupe_resolution = 9;
// Usually the sum of all queues, but may be lower if multiple duplicates
// have been updated with the same note.
uint32 found_notes = 10;
}
collection.OpChanges changes = 1;
Log log = 2;
}
message ExportAnkiPackageRequest {
string out_path = 1;
bool with_scheduling = 2;
bool with_media = 3;
bool legacy = 4;
ExportLimit limit = 5;
}
message PackageMetadata {
enum Version {
VERSION_UNKNOWN = 0;
// When `meta` missing, and collection.anki2 file present.
VERSION_LEGACY_1 = 1;
// When `meta` missing, and collection.anki21 file present.
VERSION_LEGACY_2 = 2;
// Implies MediaEntry media map, and zstd compression.
// collection.21b file
VERSION_LATEST = 3;
}
Version version = 1;
}
message MediaEntries {
message MediaEntry {
string name = 1;
uint32 size = 2;
bytes sha1 = 3;
/// Legacy media maps may include gaps in the media list, so the original
/// file index is recorded when importing from a HashMap. This field is not
/// set when exporting.
optional uint32 legacy_zip_filename = 255;
}
repeated MediaEntry entries = 1;
}
message ImportCsvRequest {
enum DupeResolution {
UPDATE = 0;
ADD = 1;
IGNORE = 2;
// UPDATE_IF_NEWER = 3;
}
string path = 1;
CsvMetadata metadata = 2;
DupeResolution dupe_resolution = 3;
}
message CsvMetadataRequest {
string path = 1;
optional CsvMetadata.Delimiter delimiter = 2;
optional int64 notetype_id = 3;
optional bool is_html = 4;
}
// Column indices are 1-based to make working with them in TS easier, where
// unset numerical fields default to 0.
message CsvMetadata {
// Order roughly in ascending expected frequency in note text, because the
// delimiter detection algorithm is stupidly picking the first one it
// encounters.
enum Delimiter {
TAB = 0;
PIPE = 1;
SEMICOLON = 2;
COLON = 3;
COMMA = 4;
SPACE = 5;
}
message MappedNotetype {
int64 id = 1;
// Source column indices for note fields. One-based. 0 means n/a.
repeated uint32 field_columns = 2;
}
Delimiter delimiter = 1;
bool is_html = 2;
repeated string global_tags = 3;
repeated string updated_tags = 4;
// Column names as defined by the file or empty strings otherwise. Also used
// to determine the number of columns.
repeated string column_labels = 5;
oneof deck {
int64 deck_id = 6;
// One-based. 0 means n/a.
uint32 deck_column = 7;
}
oneof notetype {
// One notetype for all rows with given column mapping.
MappedNotetype global_notetype = 8;
// Row-specific notetypes with automatic mapping by index.
// One-based. 0 means n/a.
uint32 notetype_column = 9;
}
// One-based. 0 means n/a.
uint32 tags_column = 10;
bool force_delimiter = 11;
bool force_is_html = 12;
repeated generic.StringList preview = 13;
uint32 guid_column = 14;
}
message ExportCardCsvRequest {
string out_path = 1;
bool with_html = 2;
ExportLimit limit = 3;
}
message ExportNoteCsvRequest {
string out_path = 1;
bool with_html = 2;
bool with_tags = 3;
bool with_deck = 4;
bool with_notetype = 5;
bool with_guid = 6;
ExportLimit limit = 7;
}
message ExportLimit {
oneof limit {
generic.Empty whole_collection = 1;
int64 deck_id = 2;
notes.NoteIds note_ids = 3;
cards.CardIds card_ids = 4;
}
}