anki_connect 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +97 -9
- data/examples/add_elevenlabs_tts_audio_to_cloze_cards.rb +75 -0
- data/examples/basic_usage.rb +48 -0
- data/lib/anki_connect/cards.rb +56 -32
- data/lib/anki_connect/client.rb +144 -17
- data/lib/anki_connect/decks.rb +15 -5
- data/lib/anki_connect/graphical.rb +36 -13
- data/lib/anki_connect/media.rb +10 -7
- data/lib/anki_connect/miscellaneous.rb +10 -8
- data/lib/anki_connect/note_types.rb +293 -0
- data/lib/anki_connect/notes.rb +188 -40
- data/lib/anki_connect/params.rb +22 -0
- data/lib/anki_connect/statistics.rb +8 -8
- data/lib/anki_connect/version.rb +1 -1
- data/lib/anki_connect.rb +2 -1
- metadata +12 -4
- data/lib/anki_connect/models.rb +0 -238
data/lib/anki_connect/notes.rb
CHANGED
|
@@ -7,38 +7,59 @@ module AnkiConnect
|
|
|
7
7
|
# Creates a new note.
|
|
8
8
|
#
|
|
9
9
|
# @param deck_name [String] Target deck
|
|
10
|
-
# @param
|
|
10
|
+
# @param note_type_name [String] Note type
|
|
11
11
|
# @param fields [Hash] Field names to values
|
|
12
12
|
# @param tags [Array<String>] Tags (optional)
|
|
13
13
|
# @param media [Hash, nil] Media to add (audio:, video:, picture: arrays)
|
|
14
|
-
# @param options [Hash, nil] Options
|
|
15
|
-
# @return [Integer
|
|
16
|
-
def add_note(deck_name:,
|
|
17
|
-
note = {
|
|
18
|
-
note
|
|
19
|
-
note[:options] = options
|
|
20
|
-
request(:addNote, note: note)
|
|
14
|
+
# @param options [Hash, nil] Options such as allow_duplicate and duplicate_scope
|
|
15
|
+
# @return [Integer] Note ID on success
|
|
16
|
+
def add_note(deck_name:, note_type_name:, fields:, tags: [], media: nil, options: nil)
|
|
17
|
+
note = { deck_name: deck_name, note_type_name: note_type_name, fields: fields, tags: tags }
|
|
18
|
+
note[:media] = media unless media.nil?
|
|
19
|
+
note[:options] = options unless options.nil?
|
|
20
|
+
request(:addNote, note: normalize_note(note))
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Creates multiple notes.
|
|
24
24
|
#
|
|
25
25
|
# @param notes [Array<Hash>] Array of note hashes (same keys as add_note)
|
|
26
|
-
#
|
|
26
|
+
# The operation is all-or-nothing; AnkiConnect rolls back all additions if any note fails.
|
|
27
|
+
#
|
|
28
|
+
# @return [Array<Integer>] Array of note IDs
|
|
27
29
|
def add_notes(notes)
|
|
28
|
-
request(:addNotes, notes: notes)
|
|
30
|
+
request(:addNotes, notes: notes.map { |note| normalize_note(note) })
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
# Checks if
|
|
33
|
+
# Checks if one note can be added.
|
|
32
34
|
#
|
|
33
|
-
# @param
|
|
34
|
-
# @
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
# @param note [Hash] Candidate note using snake_case keys
|
|
36
|
+
# @return [Boolean] Whether the note can be added
|
|
37
|
+
def note_addable?(note)
|
|
38
|
+
request(:canAddNote, note: normalize_note(note))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Gets addability details for one note.
|
|
42
|
+
#
|
|
43
|
+
# @param note [Hash] Candidate note
|
|
44
|
+
# @return [Hash] Hash with canAdd and optional error
|
|
45
|
+
def note_addability(note)
|
|
46
|
+
request(:canAddNoteWithErrorDetail, note: normalize_note(note))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Gets addability status for each note, preserving input order.
|
|
50
|
+
#
|
|
51
|
+
# @param notes [Array<Hash>] Candidate notes
|
|
52
|
+
# @return [Array<Boolean>] Addability statuses
|
|
53
|
+
def note_addability_statuses(notes)
|
|
54
|
+
request(:canAddNotes, notes: notes.map { |note| normalize_note(note) })
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Gets addability details for each note, preserving input order.
|
|
58
|
+
#
|
|
59
|
+
# @param notes [Array<Hash>] Candidate notes
|
|
60
|
+
# @return [Array<Hash>] Hashes with canAdd and optional error
|
|
61
|
+
def note_addability_details(notes)
|
|
62
|
+
request(:canAddNotesWithErrorDetail, notes: notes.map { |note| normalize_note(note) })
|
|
42
63
|
end
|
|
43
64
|
|
|
44
65
|
# Updates a note's fields, tags, or media.
|
|
@@ -49,29 +70,56 @@ module AnkiConnect
|
|
|
49
70
|
# @param media [Hash, nil] Media to add (audio:, video:, picture: arrays)
|
|
50
71
|
# @return [nil]
|
|
51
72
|
def update_note(id, fields: nil, tags: nil, media: nil)
|
|
73
|
+
if fields.nil? && tags.nil? && media.nil?
|
|
74
|
+
raise ArgumentError, 'provide fields, tags, or media to update'
|
|
75
|
+
end
|
|
76
|
+
|
|
52
77
|
note = { id: id }
|
|
53
|
-
note[:fields] = fields
|
|
54
|
-
note[:tags] = tags
|
|
55
|
-
|
|
78
|
+
note[:fields] = fields || {} unless fields.nil? && media.nil?
|
|
79
|
+
note[:tags] = tags unless tags.nil?
|
|
80
|
+
merge_media!(note, media) unless media.nil?
|
|
56
81
|
request(:updateNote, note: note)
|
|
57
82
|
end
|
|
58
83
|
|
|
59
|
-
#
|
|
84
|
+
# Updates a note's fields and optional media.
|
|
60
85
|
#
|
|
61
86
|
# @param id [Integer] Note ID
|
|
62
|
-
# @param
|
|
63
|
-
# @param
|
|
64
|
-
# @param tags [Array<String>] New tags
|
|
87
|
+
# @param fields [Hash] Field names to new values
|
|
88
|
+
# @param media [Hash, nil] Media to add (audio:, video:, picture:)
|
|
65
89
|
# @return [nil]
|
|
66
|
-
def
|
|
67
|
-
|
|
90
|
+
def update_note_fields(id, fields:, media: nil)
|
|
91
|
+
note = { id: id, fields: fields }
|
|
92
|
+
merge_media!(note, media) unless media.nil?
|
|
93
|
+
request(:updateNoteFields, note: note)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Replaces all tags on a note.
|
|
97
|
+
#
|
|
98
|
+
# @param id [Integer] Note ID
|
|
99
|
+
# @param tags [String, Array<String>] Replacement tags
|
|
100
|
+
# @return [nil]
|
|
101
|
+
def update_note_tags(id, tags)
|
|
102
|
+
request(:updateNoteTags, note: id, tags: normalize_note_tags(tags))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Changes a note's note type.
|
|
106
|
+
#
|
|
107
|
+
# @param id [Integer] Note ID
|
|
108
|
+
# @param note_type_name [String] New note type name
|
|
109
|
+
# All fields not supplied are cleared when the note type changes.
|
|
110
|
+
#
|
|
111
|
+
# @param fields [Hash] Complete values for fields in the new note type
|
|
112
|
+
# @param tags [Array<String>] Replacement tags
|
|
113
|
+
# @return [nil]
|
|
114
|
+
def change_note_type(id, note_type_name:, fields:, tags:)
|
|
115
|
+
request(:updateNoteModel, note: { id: id, modelName: note_type_name, fields: fields, tags: tags })
|
|
68
116
|
end
|
|
69
117
|
|
|
70
118
|
# Gets tags for a note.
|
|
71
119
|
#
|
|
72
120
|
# @param note_id [Integer] Note ID
|
|
73
121
|
# @return [Array<String>] Array of tag strings
|
|
74
|
-
def
|
|
122
|
+
def note_tags(note_id)
|
|
75
123
|
request(:getNoteTags, note: note_id)
|
|
76
124
|
end
|
|
77
125
|
|
|
@@ -81,7 +129,7 @@ module AnkiConnect
|
|
|
81
129
|
# @param tags [String, Array<String>] Tag(s) to add
|
|
82
130
|
# @return [nil]
|
|
83
131
|
def add_tags(note_ids, tags)
|
|
84
|
-
request(:addTags, notes: note_ids, tags: tags)
|
|
132
|
+
request(:addTags, notes: note_ids, tags: normalize_tag_query(tags))
|
|
85
133
|
end
|
|
86
134
|
|
|
87
135
|
# Removes tags from notes.
|
|
@@ -90,13 +138,13 @@ module AnkiConnect
|
|
|
90
138
|
# @param tags [String, Array<String>] Tag(s) to remove
|
|
91
139
|
# @return [nil]
|
|
92
140
|
def remove_tags(note_ids, tags)
|
|
93
|
-
request(:removeTags, notes: note_ids, tags: tags)
|
|
141
|
+
request(:removeTags, notes: note_ids, tags: normalize_tag_query(tags))
|
|
94
142
|
end
|
|
95
143
|
|
|
96
144
|
# Gets all tags in collection.
|
|
97
145
|
#
|
|
98
146
|
# @return [Array<String>] Array of all tag strings
|
|
99
|
-
def
|
|
147
|
+
def tags
|
|
100
148
|
request(:getTags)
|
|
101
149
|
end
|
|
102
150
|
|
|
@@ -134,10 +182,12 @@ module AnkiConnect
|
|
|
134
182
|
# @param note_ids [Array<Integer>, nil] Array of note IDs
|
|
135
183
|
# @param query [String, nil] Search query string
|
|
136
184
|
# @return [Array<Hash>] Array of note objects
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
185
|
+
def notes(note_ids: nil, query: nil)
|
|
186
|
+
unless [note_ids, query].count { |selector| !selector.nil? } == 1
|
|
187
|
+
raise ArgumentError, 'provide exactly one of note_ids or query'
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
params = note_ids.nil? ? { query: query } : { notes: note_ids }
|
|
141
191
|
request(:notesInfo, **params)
|
|
142
192
|
end
|
|
143
193
|
|
|
@@ -145,7 +195,7 @@ module AnkiConnect
|
|
|
145
195
|
#
|
|
146
196
|
# @param note_ids [Array<Integer>] Array of note IDs
|
|
147
197
|
# @return [Array<Hash>] Array of objects with noteId and mod
|
|
148
|
-
def
|
|
198
|
+
def note_modification_times(note_ids)
|
|
149
199
|
request(:notesModTime, notes: note_ids)
|
|
150
200
|
end
|
|
151
201
|
|
|
@@ -157,12 +207,110 @@ module AnkiConnect
|
|
|
157
207
|
request(:deleteNotes, notes: note_ids)
|
|
158
208
|
end
|
|
159
209
|
|
|
160
|
-
# Removes
|
|
210
|
+
# Removes note types that are not used by any notes.
|
|
211
|
+
# The upstream action name is misleading; it does not remove notes with empty fields.
|
|
161
212
|
#
|
|
162
213
|
# @return [nil]
|
|
163
|
-
def
|
|
214
|
+
def remove_unused_note_types
|
|
164
215
|
request(:removeEmptyNotes)
|
|
165
216
|
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
NOTE_KEYS = {
|
|
221
|
+
'deck_name' => :deckName,
|
|
222
|
+
'note_type_name' => :modelName,
|
|
223
|
+
'fields' => :fields,
|
|
224
|
+
'tags' => :tags,
|
|
225
|
+
'options' => :options,
|
|
226
|
+
'media' => :media,
|
|
227
|
+
'audio' => :audio,
|
|
228
|
+
'video' => :video,
|
|
229
|
+
'picture' => :picture
|
|
230
|
+
}.freeze
|
|
231
|
+
GUI_NOTE_KEYS = NOTE_KEYS.reject { |key, _value| key == 'options' }.freeze
|
|
232
|
+
MEDIA_KEYS = {
|
|
233
|
+
'audio' => :audio,
|
|
234
|
+
'video' => :video,
|
|
235
|
+
'picture' => :picture
|
|
236
|
+
}.freeze
|
|
237
|
+
OPTION_KEYS = {
|
|
238
|
+
'allow_duplicate' => :allowDuplicate,
|
|
239
|
+
'duplicate_scope' => :duplicateScope,
|
|
240
|
+
'duplicate_scope_options' => :duplicateScopeOptions
|
|
241
|
+
}.freeze
|
|
242
|
+
SCOPE_OPTION_KEYS = {
|
|
243
|
+
'deck_name' => :deckName,
|
|
244
|
+
'check_children' => :checkChildren,
|
|
245
|
+
'check_all_note_types' => :checkAllModels
|
|
246
|
+
}.freeze
|
|
247
|
+
MEDIA_ITEM_KEYS = {
|
|
248
|
+
'filename' => :filename,
|
|
249
|
+
'fields' => :fields,
|
|
250
|
+
'data' => :data,
|
|
251
|
+
'path' => :path,
|
|
252
|
+
'url' => :url,
|
|
253
|
+
'skip_hash' => :skipHash,
|
|
254
|
+
'overwrite' => :deleteExisting
|
|
255
|
+
}.freeze
|
|
256
|
+
private_constant :NOTE_KEYS, :GUI_NOTE_KEYS, :MEDIA_KEYS, :OPTION_KEYS, :SCOPE_OPTION_KEYS, :MEDIA_ITEM_KEYS
|
|
257
|
+
|
|
258
|
+
def normalize_note(note, key_map: NOTE_KEYS)
|
|
259
|
+
normalized = normalize_keys(note, key_map, name: 'note') do |key, value|
|
|
260
|
+
if key == :options
|
|
261
|
+
normalize_options(value)
|
|
262
|
+
elsif MEDIA_KEYS.value?(key)
|
|
263
|
+
normalize_media(value)
|
|
264
|
+
else
|
|
265
|
+
value
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
merge_media!(normalized, normalized.delete(:media)) if normalized.key?(:media)
|
|
270
|
+
normalized
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def normalize_gui_note(note)
|
|
274
|
+
normalize_note(note, key_map: GUI_NOTE_KEYS)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def merge_media!(note, media)
|
|
278
|
+
normalized_media = normalize_keys(media, MEDIA_KEYS, name: 'media') do |_key, value|
|
|
279
|
+
normalize_media(value)
|
|
280
|
+
end
|
|
281
|
+
normalized_media.each do |key, value|
|
|
282
|
+
raise ArgumentError, "duplicate media key: #{key}" if note.key?(key)
|
|
283
|
+
|
|
284
|
+
note[key] = value
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def normalize_note_tags(tags)
|
|
289
|
+
tags = [tags] if tags.is_a?(String)
|
|
290
|
+
unless tags.is_a?(Array) && tags.all? { |tag| tag.is_a?(String) }
|
|
291
|
+
raise ArgumentError, 'tags must be a String or an Array of Strings'
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
tags
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def normalize_tag_query(tags)
|
|
298
|
+
normalize_note_tags(tags).join(' ')
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def normalize_options(options)
|
|
302
|
+
normalize_keys(options, OPTION_KEYS, name: 'options') do |key, value|
|
|
303
|
+
key == :duplicateScopeOptions ? normalize_keys(value, SCOPE_OPTION_KEYS, name: 'duplicate_scope_options') : value
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def normalize_media(media)
|
|
308
|
+
media.is_a?(Array) ? media.map { |item| normalize_media_item(item) } : normalize_media_item(media)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def normalize_media_item(item)
|
|
312
|
+
normalize_keys(item, MEDIA_ITEM_KEYS, name: 'media item')
|
|
313
|
+
end
|
|
166
314
|
end
|
|
167
315
|
end
|
|
168
316
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AnkiConnect
|
|
4
|
+
class Client
|
|
5
|
+
# Shared normalization for snake_case convenience inputs.
|
|
6
|
+
module Params
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def normalize_keys(hash, key_map, name: 'parameters')
|
|
10
|
+
raise ArgumentError, "#{name} must be a Hash" unless hash.is_a?(Hash)
|
|
11
|
+
|
|
12
|
+
hash.each_with_object({}) do |(key, value), normalized|
|
|
13
|
+
normalized_key = key_map[key.to_s]
|
|
14
|
+
raise ArgumentError, "unknown #{name} key: #{key}" unless normalized_key
|
|
15
|
+
raise ArgumentError, "duplicate #{name} key: #{normalized_key}" if normalized.key?(normalized_key)
|
|
16
|
+
|
|
17
|
+
normalized[normalized_key] = block_given? ? yield(normalized_key, value) : value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -28,12 +28,12 @@ module AnkiConnect
|
|
|
28
28
|
request(:getCollectionStatsHTML, wholeCollection: whole_collection)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
# Gets all card reviews for a deck after a
|
|
31
|
+
# Gets all card reviews for a deck after a review log ID.
|
|
32
32
|
#
|
|
33
33
|
# @param deck_name [String] Deck name
|
|
34
|
-
# @param after [Integer]
|
|
34
|
+
# @param after [Integer] Review log ID in epoch milliseconds (exclusive)
|
|
35
35
|
# @return [Array<Array>] Array of 9-tuples: (reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)
|
|
36
|
-
def
|
|
36
|
+
def reviews(deck_name, after:)
|
|
37
37
|
request(:cardReviews, deck: deck_name, startID: after)
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -41,21 +41,21 @@ module AnkiConnect
|
|
|
41
41
|
#
|
|
42
42
|
# @param card_ids [Array<Integer>] Array of card IDs
|
|
43
43
|
# @return [Hash] Dictionary mapping card IDs to arrays of review objects with id, usn, ease, ivl, lastIvl, factor, time, type
|
|
44
|
-
def
|
|
44
|
+
def reviews_for_cards(card_ids)
|
|
45
45
|
request(:getReviewsOfCards, cards: card_ids)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Gets
|
|
48
|
+
# Gets the latest review log ID for a deck.
|
|
49
49
|
#
|
|
50
50
|
# @param deck_name [String] Deck name
|
|
51
|
-
# @return [Integer]
|
|
52
|
-
def
|
|
51
|
+
# @return [Integer] Review log ID in epoch milliseconds, or 0 if no reviews
|
|
52
|
+
def latest_review_id(deck_name)
|
|
53
53
|
request(:getLatestReviewID, deck: deck_name)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# Inserts review records into database.
|
|
57
57
|
#
|
|
58
|
-
# @param reviews [Array<Array>] Array of 9-tuples (same format as
|
|
58
|
+
# @param reviews [Array<Array>] Array of 9-tuples (same format as reviews output)
|
|
59
59
|
# @return [nil]
|
|
60
60
|
def insert_reviews(reviews)
|
|
61
61
|
request(:insertReviews, reviews: reviews)
|
data/lib/anki_connect/version.rb
CHANGED
data/lib/anki_connect.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'anki_connect/version'
|
|
4
|
+
require_relative 'anki_connect/params'
|
|
4
5
|
require_relative 'anki_connect/cards'
|
|
5
6
|
require_relative 'anki_connect/decks'
|
|
6
|
-
require_relative 'anki_connect/
|
|
7
|
+
require_relative 'anki_connect/note_types'
|
|
7
8
|
require_relative 'anki_connect/notes'
|
|
8
9
|
require_relative 'anki_connect/media'
|
|
9
10
|
require_relative 'anki_connect/graphical'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: anki_connect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- vadik49b
|
|
@@ -17,8 +17,11 @@ executables: []
|
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
19
19
|
files:
|
|
20
|
+
- CHANGELOG.md
|
|
20
21
|
- LICENSE
|
|
21
22
|
- README.md
|
|
23
|
+
- examples/add_elevenlabs_tts_audio_to_cloze_cards.rb
|
|
24
|
+
- examples/basic_usage.rb
|
|
22
25
|
- lib/anki_connect.rb
|
|
23
26
|
- lib/anki_connect/cards.rb
|
|
24
27
|
- lib/anki_connect/client.rb
|
|
@@ -26,8 +29,9 @@ files:
|
|
|
26
29
|
- lib/anki_connect/graphical.rb
|
|
27
30
|
- lib/anki_connect/media.rb
|
|
28
31
|
- lib/anki_connect/miscellaneous.rb
|
|
29
|
-
- lib/anki_connect/
|
|
32
|
+
- lib/anki_connect/note_types.rb
|
|
30
33
|
- lib/anki_connect/notes.rb
|
|
34
|
+
- lib/anki_connect/params.rb
|
|
31
35
|
- lib/anki_connect/statistics.rb
|
|
32
36
|
- lib/anki_connect/version.rb
|
|
33
37
|
homepage: https://github.com/vadik49b/anki-connect.rb
|
|
@@ -35,7 +39,11 @@ licenses:
|
|
|
35
39
|
- MIT
|
|
36
40
|
metadata:
|
|
37
41
|
homepage_uri: https://github.com/vadik49b/anki-connect.rb
|
|
38
|
-
source_code_uri: https://github.com/vadik49b/anki-connect.rb
|
|
42
|
+
source_code_uri: https://github.com/vadik49b/anki-connect.rb/tree/main
|
|
43
|
+
documentation_uri: https://rubydoc.info/gems/anki_connect
|
|
44
|
+
bug_tracker_uri: https://github.com/vadik49b/anki-connect.rb/issues
|
|
45
|
+
changelog_uri: https://github.com/vadik49b/anki-connect.rb/blob/main/CHANGELOG.md
|
|
46
|
+
rubygems_mfa_required: 'true'
|
|
39
47
|
rdoc_options: []
|
|
40
48
|
require_paths:
|
|
41
49
|
- lib
|
|
@@ -50,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
50
58
|
- !ruby/object:Gem::Version
|
|
51
59
|
version: '0'
|
|
52
60
|
requirements: []
|
|
53
|
-
rubygems_version:
|
|
61
|
+
rubygems_version: 4.0.10
|
|
54
62
|
specification_version: 4
|
|
55
63
|
summary: Ruby wrapper for the Anki-Connect HTTP API
|
|
56
64
|
test_files: []
|
data/lib/anki_connect/models.rb
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module AnkiConnect
|
|
4
|
-
class Client
|
|
5
|
-
# Methods to create and modify note types (models).
|
|
6
|
-
module Models
|
|
7
|
-
# Gets complete list of model names.
|
|
8
|
-
#
|
|
9
|
-
# @return [Array<String>] Array of model name strings
|
|
10
|
-
def model_names
|
|
11
|
-
request(:modelNames)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Gets model names with their IDs.
|
|
15
|
-
#
|
|
16
|
-
# @return [Hash] Model names mapped to IDs
|
|
17
|
-
def model_names_and_ids
|
|
18
|
-
request(:modelNamesAndIds)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Gets models by ID.
|
|
22
|
-
#
|
|
23
|
-
# @param ids [Array<Integer>] Array of model IDs
|
|
24
|
-
# @return [Array<Hash>] Array of model objects
|
|
25
|
-
def get_models_by_id(ids)
|
|
26
|
-
request(:findModelsById, modelIds: ids)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Gets models by name.
|
|
30
|
-
#
|
|
31
|
-
# @param names [Array<String>] Array of model names
|
|
32
|
-
# @return [Array<Hash>] Array of model objects
|
|
33
|
-
def get_models_by_name(names)
|
|
34
|
-
request(:findModelsByName, modelNames: names)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Gets field names for a model.
|
|
38
|
-
#
|
|
39
|
-
# @param model_name [String] Model name
|
|
40
|
-
# @return [Array<String>] Array of field names in order
|
|
41
|
-
def get_field_names(model_name)
|
|
42
|
-
request(:modelFieldNames, modelName: model_name)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Gets field descriptions for a model.
|
|
46
|
-
#
|
|
47
|
-
# @param model_name [String] Model name
|
|
48
|
-
# @return [Array<String>] Array of description strings
|
|
49
|
-
def get_field_descriptions(model_name)
|
|
50
|
-
request(:modelFieldDescriptions, modelName: model_name)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Gets field fonts and sizes for a model.
|
|
54
|
-
#
|
|
55
|
-
# @param model_name [String] Model name
|
|
56
|
-
# @return [Hash] Field names mapped to { font:, size: }
|
|
57
|
-
def get_field_fonts(model_name)
|
|
58
|
-
request(:modelFieldFonts, modelName: model_name)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Gets fields used on templates.
|
|
62
|
-
#
|
|
63
|
-
# @param model_name [String] Model name
|
|
64
|
-
# @return [Hash] Template names mapped to [questionFields, answerFields]
|
|
65
|
-
def get_fields_on_templates(model_name)
|
|
66
|
-
request(:modelFieldsOnTemplates, modelName: model_name)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Creates a new model.
|
|
70
|
-
#
|
|
71
|
-
# @param name [String] Model name
|
|
72
|
-
# @param fields [Array<String>] Field names in order
|
|
73
|
-
# @param templates [Array<Hash>] Template objects with Name, Front, Back
|
|
74
|
-
# @param css [String, nil] CSS styling
|
|
75
|
-
# @param is_cloze [Boolean] true for cloze type
|
|
76
|
-
# @return [Hash] Complete model object
|
|
77
|
-
def create_model(name:, fields:, templates:, css: nil, is_cloze: false)
|
|
78
|
-
params = { modelName: name, inOrderFields: fields, cardTemplates: templates, isCloze: is_cloze }
|
|
79
|
-
params[:css] = css if css
|
|
80
|
-
request(:createModel, **params)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Gets templates for a model.
|
|
84
|
-
#
|
|
85
|
-
# @param model_name [String] Model name
|
|
86
|
-
# @return [Hash] Template names mapped to { Front:, Back: }
|
|
87
|
-
def get_templates(model_name)
|
|
88
|
-
request(:modelTemplates, modelName: model_name)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Gets CSS styling for a model.
|
|
92
|
-
#
|
|
93
|
-
# @param model_name [String] Model name
|
|
94
|
-
# @return [Hash] Object with css property
|
|
95
|
-
def get_styling(model_name)
|
|
96
|
-
request(:modelStyling, modelName: model_name)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Updates a model's templates and/or CSS.
|
|
100
|
-
#
|
|
101
|
-
# @param name [String] Model name
|
|
102
|
-
# @param templates [Hash, nil] Template names mapped to Front/Back
|
|
103
|
-
# @param css [String, nil] CSS styling
|
|
104
|
-
# @return [nil]
|
|
105
|
-
def update_model(name, templates: nil, css: nil)
|
|
106
|
-
request(:updateModelTemplates, model: { name: name, templates: templates }) if templates
|
|
107
|
-
return unless css
|
|
108
|
-
|
|
109
|
-
request(:updateModelStyling, model: { name: name, css: css })
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Find and replace in model templates/CSS.
|
|
113
|
-
#
|
|
114
|
-
# @param model_name [String] Model name
|
|
115
|
-
# @param find [String] Text to find
|
|
116
|
-
# @param replace [String] Replacement text
|
|
117
|
-
# @param front [Boolean] Search front templates
|
|
118
|
-
# @param back [Boolean] Search back templates
|
|
119
|
-
# @param css [Boolean] Search CSS
|
|
120
|
-
# @return [Integer] Number of replacements made
|
|
121
|
-
def find_and_replace_in_model(model_name:, find:, replace:, front: true, back: true, css: true)
|
|
122
|
-
request(:findAndReplaceInModels, model: {
|
|
123
|
-
modelName: model_name, findText: find, replaceText: replace,
|
|
124
|
-
front: front, back: back, css: css
|
|
125
|
-
})
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Renames a template.
|
|
129
|
-
#
|
|
130
|
-
# @param model_name [String] Model name
|
|
131
|
-
# @param from [String] Current template name
|
|
132
|
-
# @param to [String] New template name
|
|
133
|
-
# @return [nil]
|
|
134
|
-
def rename_template(model_name, from:, to:)
|
|
135
|
-
request(:modelTemplateRename, modelName: model_name, oldTemplateName: from, newTemplateName: to)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Moves a template to a new position.
|
|
139
|
-
#
|
|
140
|
-
# @param model_name [String] Model name
|
|
141
|
-
# @param template_name [String] Template name
|
|
142
|
-
# @param index [Integer] New position (0-based)
|
|
143
|
-
# @return [nil]
|
|
144
|
-
def reposition_template(model_name, template_name, index)
|
|
145
|
-
request(:modelTemplateReposition, modelName: model_name, templateName: template_name, index: index)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Adds a template to a model.
|
|
149
|
-
#
|
|
150
|
-
# @param model_name [String] Model name
|
|
151
|
-
# @param template [Hash] Template with Name, Front, Back
|
|
152
|
-
# @return [nil]
|
|
153
|
-
def add_template(model_name, template)
|
|
154
|
-
request(:modelTemplateAdd, modelName: model_name, template: template)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# Removes a template from a model.
|
|
158
|
-
#
|
|
159
|
-
# @param model_name [String] Model name
|
|
160
|
-
# @param template_name [String] Template name
|
|
161
|
-
# @return [nil]
|
|
162
|
-
def remove_template(model_name, template_name)
|
|
163
|
-
request(:modelTemplateRemove, modelName: model_name, templateName: template_name)
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# Renames a field.
|
|
167
|
-
#
|
|
168
|
-
# @param model_name [String] Model name
|
|
169
|
-
# @param from [String] Current field name
|
|
170
|
-
# @param to [String] New field name
|
|
171
|
-
# @return [nil]
|
|
172
|
-
def rename_field(model_name, from:, to:)
|
|
173
|
-
request(:modelFieldRename, modelName: model_name, oldFieldName: from, newFieldName: to)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Moves a field to a new position.
|
|
177
|
-
#
|
|
178
|
-
# @param model_name [String] Model name
|
|
179
|
-
# @param field_name [String] Field name
|
|
180
|
-
# @param index [Integer] New position (0-based)
|
|
181
|
-
# @return [nil]
|
|
182
|
-
def reposition_field(model_name, field_name, index)
|
|
183
|
-
request(:modelFieldReposition, modelName: model_name, fieldName: field_name, index: index)
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
# Adds a field to a model.
|
|
187
|
-
#
|
|
188
|
-
# @param model_name [String] Model name
|
|
189
|
-
# @param field_name [String] Field name
|
|
190
|
-
# @param index [Integer, nil] Position (defaults to end)
|
|
191
|
-
# @return [nil]
|
|
192
|
-
def add_field(model_name, field_name, index: nil)
|
|
193
|
-
params = { modelName: model_name, fieldName: field_name }
|
|
194
|
-
params[:index] = index if index
|
|
195
|
-
request(:modelFieldAdd, **params)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Removes a field from a model.
|
|
199
|
-
#
|
|
200
|
-
# @param model_name [String] Model name
|
|
201
|
-
# @param field_name [String] Field name
|
|
202
|
-
# @return [nil]
|
|
203
|
-
def remove_field(model_name, field_name)
|
|
204
|
-
request(:modelFieldRemove, modelName: model_name, fieldName: field_name)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Sets font for a field.
|
|
208
|
-
#
|
|
209
|
-
# @param model_name [String] Model name
|
|
210
|
-
# @param field_name [String] Field name
|
|
211
|
-
# @param font [String] Font name
|
|
212
|
-
# @return [nil]
|
|
213
|
-
def set_field_font(model_name, field_name, font)
|
|
214
|
-
request(:modelFieldSetFont, modelName: model_name, fieldName: field_name, font: font)
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# Sets font size for a field.
|
|
218
|
-
#
|
|
219
|
-
# @param model_name [String] Model name
|
|
220
|
-
# @param field_name [String] Field name
|
|
221
|
-
# @param size [Integer] Font size
|
|
222
|
-
# @return [nil]
|
|
223
|
-
def set_field_font_size(model_name, field_name, size)
|
|
224
|
-
request(:modelFieldSetFontSize, modelName: model_name, fieldName: field_name, fontSize: size)
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# Sets description for a field.
|
|
228
|
-
#
|
|
229
|
-
# @param model_name [String] Model name
|
|
230
|
-
# @param field_name [String] Field name
|
|
231
|
-
# @param description [String] Description text
|
|
232
|
-
# @return [Boolean] true on success
|
|
233
|
-
def set_field_description(model_name, field_name, description)
|
|
234
|
-
request(:modelFieldSetDescription, modelName: model_name, fieldName: field_name, description: description)
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
end
|