anki_record 0.3.2 → 0.4
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/.rspec +0 -1
- data/.rubocop.yml +2 -7
- data/CHANGELOG.md +9 -1
- data/Gemfile +4 -0
- data/Gemfile.lock +9 -2
- data/README.md +79 -132
- data/anki_record.gemspec +2 -2
- data/lib/anki_record/anki21_database/anki21_database.rb +138 -0
- data/lib/anki_record/anki21_database/anki21_database_attributes.rb +31 -0
- data/lib/anki_record/anki21_database/anki21_database_constructors.rb +52 -0
- data/lib/anki_record/anki2_database/anki2_database.rb +44 -0
- data/lib/anki_record/anki_package/anki_package.rb +110 -176
- data/lib/anki_record/card/card.rb +17 -33
- data/lib/anki_record/card/card_attributes.rb +3 -34
- data/lib/anki_record/card_template/card_template.rb +2 -2
- data/lib/anki_record/card_template/card_template_attributes.rb +11 -11
- data/lib/anki_record/collection/collection.rb +20 -154
- data/lib/anki_record/collection/collection_attributes.rb +2 -30
- data/lib/anki_record/deck/deck.rb +11 -10
- data/lib/anki_record/deck/deck_attributes.rb +6 -8
- data/lib/anki_record/deck/deck_defaults.rb +1 -1
- data/lib/anki_record/deck_options_group/deck_options_group.rb +8 -6
- data/lib/anki_record/deck_options_group/deck_options_group_attributes.rb +5 -7
- data/lib/anki_record/helpers/anki_guid_helper.rb +20 -0
- data/lib/anki_record/media/media.rb +36 -0
- data/lib/anki_record/note/note.rb +62 -86
- data/lib/anki_record/note/note_attributes.rb +18 -17
- data/lib/anki_record/note_field/note_field.rb +3 -3
- data/lib/anki_record/note_field/note_field_attributes.rb +9 -9
- data/lib/anki_record/note_type/note_type.rb +13 -14
- data/lib/anki_record/note_type/note_type_attributes.rb +17 -21
- data/lib/anki_record/version.rb +1 -1
- metadata +11 -7
- data/lib/anki_record/helpers/data_query_helper.rb +0 -15
- data/lib/anki_record/note/note_guid_helper.rb +0 -10
@@ -4,7 +4,6 @@ require "json"
|
|
4
4
|
|
5
5
|
require_relative "../deck/deck"
|
6
6
|
require_relative "../deck_options_group/deck_options_group"
|
7
|
-
require_relative "../helpers/data_query_helper"
|
8
7
|
require_relative "../helpers/time_helper"
|
9
8
|
require_relative "../note_type/note_type"
|
10
9
|
require_relative "collection_attributes"
|
@@ -12,171 +11,38 @@ require_relative "collection_attributes"
|
|
12
11
|
module AnkiRecord
|
13
12
|
##
|
14
13
|
# Collection represents the single record in the Anki collection.anki21 database's `col` table.
|
15
|
-
# The note types, decks, and deck options groups data are contained within this record
|
14
|
+
# The note types, decks, and deck options groups data are contained within this record, but
|
15
|
+
# for simplicity of the gem's API, they are managed by the Anki21Database class.
|
16
16
|
class Collection
|
17
|
-
include Helpers::DataQueryHelper
|
18
17
|
include Helpers::TimeHelper
|
19
18
|
include CollectionAttributes
|
20
19
|
|
21
|
-
|
22
|
-
setup_collection_instance_variables(anki_package: anki_package)
|
23
|
-
end
|
24
|
-
|
25
|
-
def add_note_type(note_type) # :nodoc:
|
26
|
-
raise ArgumentError unless note_type.instance_of?(AnkiRecord::NoteType)
|
27
|
-
|
28
|
-
existing_note_type = nil
|
29
|
-
@note_types.each do |nt|
|
30
|
-
existing_note_type = nt if nt.id == note_type.id
|
31
|
-
end
|
32
|
-
@note_types.delete(existing_note_type) if existing_note_type
|
33
|
-
|
34
|
-
@note_types << note_type
|
35
|
-
end
|
36
|
-
|
37
|
-
def add_deck(deck) # :nodoc:
|
38
|
-
raise ArgumentError unless deck.instance_of?(AnkiRecord::Deck)
|
39
|
-
|
40
|
-
@decks << deck
|
41
|
-
end
|
42
|
-
|
43
|
-
def add_deck_options_group(deck_options_group) # :nodoc:
|
44
|
-
raise ArgumentError unless deck_options_group.instance_of?(AnkiRecord::DeckOptionsGroup)
|
45
|
-
|
46
|
-
@deck_options_groups << deck_options_group
|
47
|
-
end
|
48
|
-
|
49
|
-
##
|
50
|
-
# Returns the collection's note type object found by either +name+ or +id+, or nil if it is not found.
|
51
|
-
def find_note_type_by(name: nil, id: nil)
|
52
|
-
if (id && name) || (id.nil? && name.nil?)
|
53
|
-
raise ArgumentError,
|
54
|
-
"You must pass either an id or name keyword argument."
|
55
|
-
end
|
56
|
-
|
57
|
-
name ? find_note_type_by_name(name: name) : find_note_type_by_id(id: id)
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def find_note_type_by_name(name:)
|
63
|
-
note_types.find { |note_type| note_type.name == name }
|
64
|
-
end
|
65
|
-
|
66
|
-
def find_note_type_by_id(id:)
|
67
|
-
note_types.find { |note_type| note_type.id == id }
|
68
|
-
end
|
69
|
-
|
70
|
-
public
|
71
|
-
|
72
|
-
##
|
73
|
-
# Returns the collection's deck object found by either +name+ or +id+, or nil if it is not found.
|
74
|
-
def find_deck_by(name: nil, id: nil)
|
75
|
-
if (id && name) || (id.nil? && name.nil?)
|
76
|
-
raise ArgumentError,
|
77
|
-
"You must pass either an id or name keyword argument."
|
78
|
-
end
|
79
|
-
|
80
|
-
name ? find_deck_by_name(name: name) : find_deck_by_id(id: id)
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def find_deck_by_name(name:)
|
86
|
-
decks.find { |deck| deck.name == name }
|
87
|
-
end
|
88
|
-
|
89
|
-
def find_deck_by_id(id:)
|
90
|
-
decks.find { |deck| deck.id == id }
|
91
|
-
end
|
92
|
-
|
93
|
-
public
|
94
|
-
|
95
|
-
##
|
96
|
-
# Returns the collection's deck options group object found by +id+, or nil if it is not found.
|
97
|
-
def find_deck_options_group_by(id:)
|
98
|
-
deck_options_groups.find { |deck_options_group| deck_options_group.id == id }
|
99
|
-
end
|
100
|
-
|
101
|
-
##
|
102
|
-
# Returns the collection's note object found by +id+, or nil if it is not found.
|
103
|
-
def find_note_by(id:)
|
104
|
-
note_cards_data = note_cards_data_for_note_id sql_able: anki_package, id: id
|
105
|
-
return nil unless note_cards_data
|
106
|
-
|
107
|
-
AnkiRecord::Note.new collection: self, data: note_cards_data
|
108
|
-
end
|
109
|
-
|
110
|
-
# :nodoc:
|
111
|
-
def decks_json
|
112
|
-
JSON.parse(anki_package.prepare("select decks from col;").execute.first["decks"])
|
113
|
-
end
|
20
|
+
attr_reader :anki21_database
|
114
21
|
|
22
|
+
# rubocop:disable Metrics/AbcSize
|
23
|
+
# rubocop:disable Metrics/MethodLength
|
115
24
|
# :nodoc:
|
116
|
-
def
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@
|
122
|
-
|
123
|
-
|
25
|
+
def initialize(anki21_database:)
|
26
|
+
@anki21_database = anki21_database
|
27
|
+
@id = col_record["id"]
|
28
|
+
@created_at_timestamp = col_record["crt"]
|
29
|
+
@last_modified_timestamp = col_record["mod"]
|
30
|
+
@scm = col_record["scm"]
|
31
|
+
@ver = col_record["ver"]
|
32
|
+
@dty = col_record["dty"]
|
33
|
+
@usn = col_record["usn"]
|
34
|
+
@ls = col_record["ls"]
|
35
|
+
@configuration = JSON.parse(col_record["conf"])
|
36
|
+
@tags = JSON.parse(col_record["tags"])
|
124
37
|
remove_instance_variable(:@col_record)
|
125
38
|
end
|
39
|
+
# rubocop:enable Metrics/AbcSize
|
40
|
+
# rubocop:enable Metrics/MethodLength
|
126
41
|
|
127
42
|
private
|
128
43
|
|
129
|
-
def setup_collection_instance_variables(anki_package:)
|
130
|
-
@anki_package = anki_package
|
131
|
-
setup_simple_collaborator_objects
|
132
|
-
setup_custom_collaborator_objects
|
133
|
-
remove_instance_variable(:@col_record)
|
134
|
-
end
|
135
|
-
|
136
44
|
def col_record
|
137
|
-
@col_record ||=
|
138
|
-
end
|
139
|
-
|
140
|
-
# rubocop:disable Metrics/AbcSize
|
141
|
-
def setup_simple_collaborator_objects
|
142
|
-
@id = col_record["id"]
|
143
|
-
@created_at_timestamp = col_record["crt"]
|
144
|
-
@last_modified_timestamp = col_record["mod"]
|
145
|
-
@scm = col_record["scm"]
|
146
|
-
@ver = col_record["ver"]
|
147
|
-
@dty = col_record["dty"]
|
148
|
-
@usn = col_record["usn"]
|
149
|
-
@ls = col_record["ls"]
|
150
|
-
@configuration = JSON.parse(col_record["conf"])
|
151
|
-
@tags = JSON.parse(col_record["tags"])
|
152
|
-
end
|
153
|
-
# rubocop:enable Metrics/AbcSize
|
154
|
-
|
155
|
-
def setup_custom_collaborator_objects
|
156
|
-
setup_note_type_collaborators
|
157
|
-
setup_deck_options_groups_collaborators
|
158
|
-
setup_deck_collaborators
|
159
|
-
end
|
160
|
-
|
161
|
-
def setup_note_type_collaborators
|
162
|
-
@note_types = []
|
163
|
-
JSON.parse(col_record["models"]).values.map do |model_hash|
|
164
|
-
NoteType.new(collection: self, args: model_hash)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def setup_deck_collaborators
|
169
|
-
@decks = []
|
170
|
-
JSON.parse(col_record["decks"]).values.map do |deck_hash|
|
171
|
-
Deck.new(collection: self, args: deck_hash)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def setup_deck_options_groups_collaborators
|
176
|
-
@deck_options_groups = []
|
177
|
-
JSON.parse(col_record["dconf"]).values.map do |dconf_hash|
|
178
|
-
DeckOptionsGroup.new(collection: self, args: dconf_hash)
|
179
|
-
end
|
45
|
+
@col_record ||= anki21_database.col_record
|
180
46
|
end
|
181
47
|
end
|
182
48
|
end
|
@@ -1,35 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AnkiRecord
|
4
|
-
|
5
|
-
|
6
|
-
module CollectionAttributes
|
7
|
-
##
|
8
|
-
# The collection's Anki package object.
|
9
|
-
attr_reader :anki_package
|
10
|
-
|
11
|
-
##
|
12
|
-
# The collection's id, which is also the id of the col record in the collection.anki21 database (usually 1).
|
13
|
-
attr_reader :id
|
14
|
-
|
15
|
-
##
|
16
|
-
# The number of milliseconds since the 1970 epoch when the collection record was created.
|
17
|
-
attr_reader :created_at_timestamp
|
18
|
-
|
19
|
-
##
|
20
|
-
# The number of milliseconds since the 1970 epoch at which the collection record was last modified.
|
21
|
-
attr_reader :last_modified_timestamp
|
22
|
-
|
23
|
-
##
|
24
|
-
# The collection's note type objects as an array.
|
25
|
-
attr_reader :note_types
|
26
|
-
|
27
|
-
##
|
28
|
-
# The collection's deck objects as an array
|
29
|
-
attr_reader :decks
|
30
|
-
|
31
|
-
##
|
32
|
-
# The collection's deck option group objects as an array.
|
33
|
-
attr_reader :deck_options_groups
|
4
|
+
module CollectionAttributes # :nodoc:
|
5
|
+
attr_reader :id, :created_at_timestamp, :last_modified_timestamp
|
34
6
|
end
|
35
7
|
end
|
@@ -15,28 +15,28 @@ module AnkiRecord
|
|
15
15
|
include Helpers::TimeHelper
|
16
16
|
|
17
17
|
##
|
18
|
-
# Instantiates a new Deck object belonging to +
|
19
|
-
def initialize(
|
18
|
+
# Instantiates a new Deck object belonging to +anki21_database+ with name +name+.
|
19
|
+
def initialize(anki21_database:, name: nil, args: nil)
|
20
20
|
raise ArgumentError unless (name && args.nil?) || (args && args["name"])
|
21
21
|
|
22
|
-
@
|
22
|
+
@anki21_database = anki21_database
|
23
23
|
if args
|
24
|
-
setup_deck_instance_variables_from_existing(args:
|
24
|
+
setup_deck_instance_variables_from_existing(args:)
|
25
25
|
else
|
26
|
-
setup_deck_instance_variables(name:
|
26
|
+
setup_deck_instance_variables(name:)
|
27
27
|
end
|
28
28
|
|
29
|
-
@
|
29
|
+
@anki21_database.add_deck self
|
30
30
|
save if args
|
31
31
|
end
|
32
32
|
|
33
33
|
##
|
34
34
|
# Saves the deck to the collection.anki21 database.
|
35
35
|
def save
|
36
|
-
collection_decks_hash =
|
36
|
+
collection_decks_hash = anki21_database.decks_json
|
37
37
|
collection_decks_hash[@id] = to_h
|
38
38
|
sql = "update col set decks = ? where id = ?"
|
39
|
-
|
39
|
+
anki21_database.prepare(sql).execute([JSON.generate(collection_decks_hash), anki21_database.collection.id])
|
40
40
|
end
|
41
41
|
|
42
42
|
def to_h # :nodoc:
|
@@ -54,6 +54,7 @@ module AnkiRecord
|
|
54
54
|
def inspect
|
55
55
|
"#<AnkiRecord::Deck:#{object_id} id: #{id} name: #{name} description: #{description}>"
|
56
56
|
end
|
57
|
+
# :nocov:
|
57
58
|
|
58
59
|
private
|
59
60
|
|
@@ -72,7 +73,7 @@ module AnkiRecord
|
|
72
73
|
@collapsed_in_browser = args["browserCollapsed"]
|
73
74
|
@description = args["desc"]
|
74
75
|
@dyn = args["dyn"]
|
75
|
-
@deck_options_group =
|
76
|
+
@deck_options_group = anki21_database.find_deck_options_group_by(id: args["conf"])
|
76
77
|
@extend_new = args["extendNew"]
|
77
78
|
@extend_review = args["extendRev"]
|
78
79
|
end
|
@@ -90,7 +91,7 @@ module AnkiRecord
|
|
90
91
|
@collapsed_in_browser = default_collapsed
|
91
92
|
@description = ""
|
92
93
|
@dyn = NON_FILTERED_DECK_DYN
|
93
|
-
@deck_options_group =
|
94
|
+
@deck_options_group = anki21_database.find_deck_options_group_by id: default_deck_options_group_id
|
94
95
|
@extend_new = 0
|
95
96
|
@extend_review = 0
|
96
97
|
end
|
@@ -3,28 +3,26 @@
|
|
3
3
|
module AnkiRecord
|
4
4
|
# Module with the Deck class's attribute readers, writers, and accessors.
|
5
5
|
module DeckAttributes
|
6
|
-
|
7
|
-
# The deck's collection object.
|
8
|
-
attr_reader :collection
|
6
|
+
attr_reader :anki21_database # :nodoc:
|
9
7
|
|
10
8
|
##
|
11
|
-
# The deck's name
|
9
|
+
# The deck's name
|
12
10
|
attr_accessor :name
|
13
11
|
|
14
12
|
##
|
15
|
-
# The deck's description
|
13
|
+
# The deck's description
|
16
14
|
attr_accessor :description
|
17
15
|
|
18
16
|
##
|
19
|
-
# The deck's id
|
17
|
+
# The deck's id
|
20
18
|
attr_reader :id
|
21
19
|
|
22
20
|
##
|
23
|
-
# The number of seconds since the 1970 epoch when the deck was last modified
|
21
|
+
# The number of seconds since the 1970 epoch when the deck was last modified
|
24
22
|
attr_reader :last_modified_timestamp
|
25
23
|
|
26
24
|
##
|
27
|
-
# The deck's deck options group
|
25
|
+
# The deck's deck options group
|
28
26
|
attr_reader :deck_options_group
|
29
27
|
end
|
30
28
|
end
|
@@ -7,25 +7,27 @@ require_relative "deck_options_group_attributes"
|
|
7
7
|
module AnkiRecord
|
8
8
|
##
|
9
9
|
# DeckOptionsGroup represents a set of options that can be applied to an Anki deck.
|
10
|
+
# The interface to this has not been explored much so using the gem with these directly
|
11
|
+
# may involve breaking encapsulation.
|
10
12
|
class DeckOptionsGroup
|
11
13
|
include DeckOptionsGroupAttributes
|
12
14
|
include Helpers::SharedConstantsHelper
|
13
15
|
include Helpers::TimeHelper
|
14
16
|
|
15
17
|
##
|
16
|
-
# Instantiates a new deck options group belonging to +
|
17
|
-
def initialize(
|
18
|
+
# Instantiates a new deck options group belonging to +anki21_database+ with name +name+.
|
19
|
+
def initialize(anki21_database:, name: nil, args: nil)
|
18
20
|
raise ArgumentError unless (name && args.nil?) || (args && args["name"])
|
19
21
|
|
20
|
-
@
|
22
|
+
@anki21_database = anki21_database
|
21
23
|
|
22
24
|
if args
|
23
|
-
setup_deck_options_group_instance_variables_from_existing(args:
|
25
|
+
setup_deck_options_group_instance_variables_from_existing(args:)
|
24
26
|
else
|
25
|
-
setup_deck_options_group_instance_variables(name:
|
27
|
+
setup_deck_options_group_instance_variables(name:)
|
26
28
|
end
|
27
29
|
|
28
|
-
@
|
30
|
+
@anki21_database.add_deck_options_group self
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
@@ -2,22 +2,20 @@
|
|
2
2
|
|
3
3
|
module AnkiRecord
|
4
4
|
##
|
5
|
-
# Module with
|
5
|
+
# Module with DeckOptionsGroup's attribute readers, writers, and accessors.
|
6
6
|
module DeckOptionsGroupAttributes
|
7
|
-
|
8
|
-
# The deck options group's collection object.
|
9
|
-
attr_reader :collection
|
7
|
+
attr_reader :anki21_database # :nodoc:
|
10
8
|
|
11
9
|
##
|
12
|
-
# The deck option group's name
|
10
|
+
# The deck option group's name
|
13
11
|
attr_accessor :name
|
14
12
|
|
15
13
|
##
|
16
|
-
# The deck option group's id
|
14
|
+
# The deck option group's id
|
17
15
|
attr_reader :id
|
18
16
|
|
19
17
|
##
|
20
|
-
# The number of milliseconds since the 1970 epoch at which the deck options group was modified
|
18
|
+
# The number of milliseconds since the 1970 epoch at which the deck options group was modified
|
21
19
|
attr_reader :last_modified_timestamp
|
22
20
|
end
|
23
21
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
|
5
|
+
module AnkiRecord
|
6
|
+
module Helpers
|
7
|
+
##
|
8
|
+
# A module for the method that computes the guid value of notes.
|
9
|
+
#
|
10
|
+
# This guid is used by Anki when importing a deck package to update existing notes
|
11
|
+
# rather than create duplicates of them.
|
12
|
+
module AnkiGuidHelper
|
13
|
+
##
|
14
|
+
# Returns a random string of 10 characters sliced out of a random base64 string (RFC 3548).
|
15
|
+
def self.globally_unique_id
|
16
|
+
SecureRandom.base64(9).slice(1, 10)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnkiRecord
|
4
|
+
class Media # :nodoc:
|
5
|
+
attr_reader :anki_package, :media_file
|
6
|
+
|
7
|
+
FILENAME = "media"
|
8
|
+
|
9
|
+
def self.create_new(anki_package:)
|
10
|
+
media = new
|
11
|
+
media.create_initialize(anki_package:)
|
12
|
+
media
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_initialize(anki_package:)
|
16
|
+
@anki_package = anki_package
|
17
|
+
media_file_path = FileUtils.touch("#{anki_package.tmpdir}/#{FILENAME}")[0]
|
18
|
+
@media_file = File.open(media_file_path, mode: "w")
|
19
|
+
media_file.write("{}")
|
20
|
+
media_file.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.update_new(anki_package:)
|
24
|
+
media = new
|
25
|
+
media.update_initialize(anki_package:)
|
26
|
+
media
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_initialize(anki_package:)
|
30
|
+
@anki_package = anki_package
|
31
|
+
@media_file = File.open("#{anki_package.tmpdir}/#{FILENAME}", mode: "w")
|
32
|
+
media_file.write("{}")
|
33
|
+
media_file.close
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|