anki_record 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +33 -4
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +5 -3
  6. data/README.md +147 -11
  7. data/anki_record.gemspec +2 -6
  8. data/lib/anki_record/anki_package/anki_package.rb +245 -0
  9. data/lib/anki_record/anki_package/database_setup_constants.rb +91 -0
  10. data/lib/anki_record/card/card.rb +108 -0
  11. data/lib/anki_record/card/card_attributes.rb +39 -0
  12. data/lib/anki_record/{card_template.rb → card_template/card_template.rb} +20 -47
  13. data/lib/anki_record/card_template/card_template_attributes.rb +69 -0
  14. data/lib/anki_record/collection/collection.rb +180 -0
  15. data/lib/anki_record/collection/collection_attributes.rb +35 -0
  16. data/lib/anki_record/deck/deck.rb +101 -0
  17. data/lib/anki_record/deck/deck_attributes.rb +30 -0
  18. data/lib/anki_record/deck/deck_defaults.rb +19 -0
  19. data/lib/anki_record/{deck_options_group.rb → deck_options_group/deck_options_group.rb} +10 -23
  20. data/lib/anki_record/deck_options_group/deck_options_group_attributes.rb +23 -0
  21. data/lib/anki_record/helpers/checksum_helper.rb +17 -0
  22. data/lib/anki_record/helpers/data_query_helper.rb +13 -0
  23. data/lib/anki_record/helpers/shared_constants_helper.rb +1 -3
  24. data/lib/anki_record/helpers/time_helper.rb +7 -5
  25. data/lib/anki_record/note/note.rb +181 -0
  26. data/lib/anki_record/note/note_attributes.rb +56 -0
  27. data/lib/anki_record/note_field/note_field.rb +62 -0
  28. data/lib/anki_record/note_field/note_field_attributes.rb +39 -0
  29. data/lib/anki_record/note_field/note_field_defaults.rb +19 -0
  30. data/lib/anki_record/note_type/note_type.rb +161 -0
  31. data/lib/anki_record/note_type/note_type_attributes.rb +80 -0
  32. data/lib/anki_record/note_type/note_type_defaults.rb +38 -0
  33. data/lib/anki_record/version.rb +1 -1
  34. data/lib/anki_record.rb +1 -15
  35. metadata +32 -20
  36. data/.rdoc_options +0 -27
  37. data/lib/anki_record/anki_package.rb +0 -183
  38. data/lib/anki_record/collection.rb +0 -65
  39. data/lib/anki_record/db/anki_schema_definition.rb +0 -77
  40. data/lib/anki_record/db/clean_collection21_record.rb +0 -10
  41. data/lib/anki_record/db/clean_collection2_record.rb +0 -10
  42. data/lib/anki_record/deck.rb +0 -88
  43. data/lib/anki_record/note_field.rb +0 -63
  44. data/lib/anki_record/note_type.rb +0 -147
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../card_template/card_template"
4
+ require_relative "../helpers/shared_constants_helper"
5
+ require_relative "../helpers/time_helper"
6
+ require_relative "../note_field/note_field"
7
+ require_relative "note_type_attributes"
8
+ require_relative "note_type_defaults"
9
+
10
+ module AnkiRecord
11
+ ##
12
+ # NoteType represents an Anki note type (also called a model).
13
+ #
14
+ # The attributes are documented in the NoteTypeAttributes module.
15
+ class NoteType
16
+ include AnkiRecord::SharedConstantsHelper
17
+ include AnkiRecord::TimeHelper
18
+ include AnkiRecord::NoteTypeAttributes
19
+ include AnkiRecord::NoteTypeDefaults
20
+
21
+ NOTE_TYPES_WITHOUT_TAGS_AND_VERS_VALUES = ["Basic", "Basic (and reversed card)",
22
+ "Basic (optional reversed card)", "Basic (type in the answer)"].freeze
23
+
24
+ def initialize(collection:, name: nil, args: nil)
25
+ raise ArgumentError unless (name && args.nil?) || (args && args["name"])
26
+
27
+ @collection = collection
28
+
29
+ if args
30
+ setup_note_type_instance_variables_from_existing(args: args)
31
+ else
32
+ setup_instance_variables_for_new_note_type(name: name)
33
+ end
34
+
35
+ @collection.add_note_type self
36
+ save
37
+ end
38
+
39
+ ##
40
+ # Saves the note type to the collection.anki21 database
41
+ def save
42
+ collection_models_hash = collection.models_json
43
+ collection_models_hash[@id] = to_h
44
+ sql = "update col set models = ? where id = ?"
45
+ collection.anki_package.prepare(sql).execute([JSON.generate(collection_models_hash), collection.id])
46
+ end
47
+
48
+ def to_h # :nodoc:
49
+ self_to_h = { id: @id, name: @name, type: @cloze ? 1 : 0,
50
+ mod: @last_modified_timestamp, usn: @usn, sortf: @sort_field, did: @deck_id,
51
+ tmpls: @card_templates.map(&:to_h), flds: @note_fields.map(&:to_h), css: @css,
52
+ latexPre: @latex_preamble, latexPost: @latex_postamble, latexsvg: @latex_svg,
53
+ req: @req }
54
+ self_to_h.merge({ tags: @tags, vers: @vers }) unless NOTE_TYPES_WITHOUT_TAGS_AND_VERS_VALUES.include?(@name)
55
+ self_to_h
56
+ end
57
+
58
+ ##
59
+ # Returns the note type object's card template with name +name+ or nil if it is not found
60
+ def find_card_template_by(name:)
61
+ card_templates.find { |template| template.name == name }
62
+ end
63
+
64
+ ##
65
+ # Returns an array of the note type's fields' names ordered by field ordinal values.
66
+ def field_names_in_order
67
+ @note_fields.sort_by(&:ordinal_number).map(&:name)
68
+ end
69
+
70
+ def snake_case_field_names # :nodoc:
71
+ field_names_in_order.map { |field_name| field_name.downcase.gsub(" ", "_") }
72
+ end
73
+
74
+ ##
75
+ # Returns the name of the note type's field used to sort notes in the browser.
76
+ def sort_field_name
77
+ @note_fields.find { |field| field.ordinal_number == @sort_field }&.name
78
+ end
79
+
80
+ def snake_case_sort_field_name # :nodoc:
81
+ sort_field_name.downcase.gsub(" ", "_")
82
+ end
83
+
84
+ ##
85
+ # Returns allowed field_name values in {{field_name}} in the question format.
86
+ def allowed_card_template_question_format_field_names
87
+ allowed = field_names_in_order
88
+ cloze ? allowed + field_names_in_order.map { |field_name| "cloze:#{field_name}" } : allowed
89
+ end
90
+
91
+ ##
92
+ # Returns allowed field_name values in {{field_name}} in the answer format.
93
+ def allowed_card_template_answer_format_field_names
94
+ allowed_card_template_question_format_field_names + ["FrontSide"]
95
+ end
96
+
97
+ def add_note_field(note_field) # :nodoc:
98
+ raise ArgumentError unless note_field.instance_of?(AnkiRecord::NoteField)
99
+
100
+ @note_fields << note_field
101
+ end
102
+
103
+ def add_card_template(card_template) # :nodoc:
104
+ raise ArgumentError unless card_template.instance_of?(AnkiRecord::CardTemplate)
105
+
106
+ @card_templates << card_template
107
+ end
108
+
109
+ private
110
+
111
+ def setup_note_type_instance_variables_from_existing(args:)
112
+ setup_collaborator_object_instance_variables_from_existing(args: args)
113
+ setup_simple_instance_variables_from_existing(args: args)
114
+ end
115
+
116
+ def setup_collaborator_object_instance_variables_from_existing(args:)
117
+ @note_fields = []
118
+ args["flds"].each { |fld| NoteField.new(note_type: self, args: fld) }
119
+ @card_templates = []
120
+ args["tmpls"].each { |tmpl| CardTemplate.new(note_type: self, args: tmpl) }
121
+ end
122
+
123
+ def setup_simple_instance_variables_from_existing(args:)
124
+ %w[id name usn css req tags vers].each do |note_type_attribute|
125
+ instance_variable_set "@#{note_type_attribute}", args[note_type_attribute]
126
+ end
127
+ @cloze = args["type"] == 1
128
+ @last_modified_timestamp = args["mod"]
129
+ @sort_field = args["sortf"]
130
+ @deck_id = args["did"]
131
+ @latex_preamble = args["latexPre"]
132
+ @latex_postamble = args["latexPost"]
133
+ @latex_svg = args["latexsvg"]
134
+ end
135
+
136
+ def setup_instance_variables_for_new_note_type(name:)
137
+ @name = name
138
+ @cloze = false
139
+ setup_collaborator_object_instance_variables_for_new_note_type
140
+ setup_simple_instance_variables_for_new_note_type
141
+ end
142
+
143
+ def setup_collaborator_object_instance_variables_for_new_note_type
144
+ @note_fields = []
145
+ @card_templates = []
146
+ end
147
+
148
+ def setup_simple_instance_variables_for_new_note_type
149
+ @id = milliseconds_since_epoch
150
+ @last_modified_timestamp = seconds_since_epoch
151
+ @usn = NEW_OBJECT_USN
152
+ @sort_field = default_note_type_sort_field
153
+ @deck_id = @tags = @vers = nil
154
+ @css = default_css
155
+ @latex_preamble = default_latex_preamble
156
+ @latex_postamble = default_latex_postamble
157
+ @latex_svg = false
158
+ @req = []
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnkiRecord
4
+ ##
5
+ # Module with the NoteType class's attribute readers, writers, and accessors.
6
+ module NoteTypeAttributes
7
+ ##
8
+ # The note type's collection object.
9
+ attr_reader :collection
10
+
11
+ ##
12
+ # The note type's id.
13
+ attr_reader :id
14
+
15
+ ##
16
+ # The note type's name.
17
+ attr_accessor :name
18
+
19
+ ##
20
+ # A boolean that indicates if this note type is a cloze-deletion note type.
21
+ attr_accessor :cloze
22
+
23
+ ##
24
+ # The number of seconds since the 1970 epoch at which the note type was last modified.
25
+ attr_reader :last_modified_timestamp
26
+
27
+ ##
28
+ # The note type's update sequence number.
29
+ attr_reader :usn
30
+
31
+ ##
32
+ # The note type's sort field.
33
+ attr_reader :sort_field
34
+
35
+ ##
36
+ # The note type's CSS.
37
+ attr_accessor :css
38
+
39
+ ##
40
+ # The note type's LaTeX preamble.
41
+ attr_reader :latex_preamble
42
+
43
+ ##
44
+ # The note type's LaTeX postamble.
45
+ attr_reader :latex_postamble
46
+
47
+ ##
48
+ # The note type's card template objects, as an array.
49
+ attr_reader :card_templates
50
+
51
+ ##
52
+ # The note type's field objects, as an array.
53
+ attr_reader :note_fields
54
+
55
+ ##
56
+ # The note type's deck's id.
57
+ attr_reader :deck_id
58
+
59
+ ##
60
+ # The note type's deck.
61
+ def deck
62
+ return nil unless @deck_id
63
+
64
+ @collection.find_deck_by id: @deck_id
65
+ end
66
+
67
+ ##
68
+ # Sets the note type's deck object.
69
+ def deck=(deck)
70
+ unless deck.instance_of?(AnkiRecord::Deck)
71
+ raise ArgumentError,
72
+ "You can only set this attribute to an instance of AnkiRecord::Deck."
73
+ end
74
+
75
+ @deck_id = deck.id
76
+ end
77
+
78
+ attr_reader :latex_svg, :tags, :req, :vers
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnkiRecord
4
+ module NoteTypeDefaults # :nodoc:
5
+ private
6
+
7
+ def default_note_type_sort_field
8
+ 0
9
+ end
10
+
11
+ def default_css
12
+ <<~CSS
13
+ .card {
14
+ color: black;
15
+ background-color: transparent;
16
+ text-align: center;
17
+ }
18
+ CSS
19
+ end
20
+
21
+ def default_latex_preamble
22
+ <<~LATEX_PRE
23
+ \\documentclass[12pt]{article}
24
+ \\special{papersize=3in,5in}
25
+ \\usepackage{amssymb,amsmath}
26
+ \\pagestyle{empty}
27
+ \\setlength{\\parindent}{0in}
28
+ \\begin{document}
29
+ LATEX_PRE
30
+ end
31
+
32
+ def default_latex_postamble
33
+ <<~LATEX_POST
34
+ \\end{document}
35
+ LATEX_POST
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AnkiRecord
4
- VERSION = "0.1.1" # :nodoc:
4
+ VERSION = "0.3.0" # :nodoc:
5
5
  end
data/lib/anki_record.rb CHANGED
@@ -1,25 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "securerandom"
4
3
  require "sqlite3"
5
4
  require "zip"
6
5
 
7
- require_relative "anki_record/anki_package"
6
+ require_relative "anki_record/anki_package/anki_package"
8
7
  require_relative "anki_record/version"
9
8
 
10
- ##
11
- # This module is the namespace for all AnkiRecord classes:
12
- # - AnkiPackage
13
- # - CardTemplate
14
- # - Collection
15
- # - DeckOptionsGroup
16
- # - Deck
17
- # - NoteField
18
- # - NoteType
19
- #
20
- # And modules:
21
- # - SharedConstantsHelper
22
- # - TimeHelper
23
9
  module AnkiRecord
24
10
  class Error < StandardError; end # :nodoc:
25
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anki_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Rego
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-24 00:00:00.000000000 Z
11
+ date: 2023-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -28,25 +28,24 @@ dependencies:
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
41
- description: " This Ruby library, which is currently in development, will provide
42
- an interface to inspect, update, and create Anki SQLite3 databases (*.apkg files).\n"
40
+ version: '1.3'
41
+ description: " A Ruby library which provides a programmatic interface to Anki flashcard
42
+ decks (.apkg files/zipped Anki SQLite databases).\n"
43
43
  email:
44
44
  - regoky@outlook.com
45
45
  executables: []
46
46
  extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
- - ".rdoc_options"
50
49
  - ".rspec"
51
50
  - ".rubocop.yml"
52
51
  - CHANGELOG.md
@@ -58,18 +57,31 @@ files:
58
57
  - Rakefile
59
58
  - anki_record.gemspec
60
59
  - lib/anki_record.rb
61
- - lib/anki_record/anki_package.rb
62
- - lib/anki_record/card_template.rb
63
- - lib/anki_record/collection.rb
64
- - lib/anki_record/db/anki_schema_definition.rb
65
- - lib/anki_record/db/clean_collection21_record.rb
66
- - lib/anki_record/db/clean_collection2_record.rb
67
- - lib/anki_record/deck.rb
68
- - lib/anki_record/deck_options_group.rb
60
+ - lib/anki_record/anki_package/anki_package.rb
61
+ - lib/anki_record/anki_package/database_setup_constants.rb
62
+ - lib/anki_record/card/card.rb
63
+ - lib/anki_record/card/card_attributes.rb
64
+ - lib/anki_record/card_template/card_template.rb
65
+ - lib/anki_record/card_template/card_template_attributes.rb
66
+ - lib/anki_record/collection/collection.rb
67
+ - lib/anki_record/collection/collection_attributes.rb
68
+ - lib/anki_record/deck/deck.rb
69
+ - lib/anki_record/deck/deck_attributes.rb
70
+ - lib/anki_record/deck/deck_defaults.rb
71
+ - lib/anki_record/deck_options_group/deck_options_group.rb
72
+ - lib/anki_record/deck_options_group/deck_options_group_attributes.rb
73
+ - lib/anki_record/helpers/checksum_helper.rb
74
+ - lib/anki_record/helpers/data_query_helper.rb
69
75
  - lib/anki_record/helpers/shared_constants_helper.rb
70
76
  - lib/anki_record/helpers/time_helper.rb
71
- - lib/anki_record/note_field.rb
72
- - lib/anki_record/note_type.rb
77
+ - lib/anki_record/note/note.rb
78
+ - lib/anki_record/note/note_attributes.rb
79
+ - lib/anki_record/note_field/note_field.rb
80
+ - lib/anki_record/note_field/note_field_attributes.rb
81
+ - lib/anki_record/note_field/note_field_defaults.rb
82
+ - lib/anki_record/note_type/note_type.rb
83
+ - lib/anki_record/note_type/note_type_attributes.rb
84
+ - lib/anki_record/note_type/note_type_defaults.rb
73
85
  - lib/anki_record/version.rb
74
86
  homepage: https://github.com/KyleRego/anki_record
75
87
  licenses:
@@ -94,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
106
  - !ruby/object:Gem::Version
95
107
  version: '0'
96
108
  requirements: []
97
- rubygems_version: 3.4.7
109
+ rubygems_version: 3.4.6
98
110
  signing_key:
99
111
  specification_version: 4
100
112
  summary: Automate Anki flashcard editing with the Ruby programming language.
data/.rdoc_options DELETED
@@ -1,27 +0,0 @@
1
- ---
2
- encoding: UTF-8
3
- static_path: []
4
- rdoc_include: []
5
- page_dir:
6
- charset: UTF-8
7
- exclude:
8
- - "~\\z"
9
- - "\\.orig\\z"
10
- - "\\.rej\\z"
11
- - "\\.bak\\z"
12
- - "\\.gemspec\\z"
13
- hyperlink_all: false
14
- line_numbers: false
15
- locale:
16
- locale_dir: locale
17
- locale_name:
18
- main_page:
19
- markup: rdoc
20
- output_decoration: true
21
- show_hash: false
22
- skip_tests: true
23
- tab_width: 8
24
- template_stylesheets: []
25
- title:
26
- visibility: :protected
27
- webcvs:
@@ -1,183 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pry"
4
- require "pathname"
5
-
6
- require_relative "db/anki_schema_definition"
7
- require_relative "db/clean_collection2_record"
8
- require_relative "db/clean_collection21_record"
9
- require_relative "collection"
10
-
11
- module AnkiRecord
12
- ##
13
- # Represents an Anki SQLite3 package/database
14
- #
15
- # Use ::new to create a new object or ::open to create an object from an existing one
16
- class AnkiPackage
17
- NAME_ERROR_MESSAGE = "The name argument must be a string without spaces."
18
- PATH_ERROR_MESSAGE = "*No .apkg file was found at the given path."
19
- STANDARD_ERROR_MESSAGE = <<-MSG
20
- An error occurred.
21
- The temporary *.anki21 database has been deleted.
22
- No *.apkg zip file has been saved.
23
- MSG
24
-
25
- private_constant :NAME_ERROR_MESSAGE, :PATH_ERROR_MESSAGE, :STANDARD_ERROR_MESSAGE
26
-
27
- ##
28
- # Creates a new object which represents an Anki SQLite3 database
29
- #
30
- # This method takes an optional block argument.
31
- #
32
- # When a block argument is used, execution is yielded to the block.
33
- # After the block executes, the temporary files are zipped into the +name+.apkg file
34
- # which is saved in +directory+. +directory+ is the current working directory by default.
35
- # If the block throws a runtime error, the temporary files are deleted but the zip file is not created.
36
- #
37
- # When no block argument is used, #zip_and_close must be called explicitly at the end of your script.
38
- def initialize(name:, directory: Dir.pwd)
39
- setup_package_instance_variables(name: name, directory: directory)
40
-
41
- return unless block_given?
42
-
43
- begin
44
- yield self
45
- rescue StandardError => e
46
- close
47
- puts_error_and_standard_message(error: e)
48
- else
49
- zip_and_close
50
- end
51
- end
52
-
53
- ##
54
- # Executes a raw SQL statement against the *.anki21 database
55
- #
56
- # Do not use this to execute data definition language SQL statements
57
- # (i.e. do not create, alter, or drop tables or indexes)
58
- # unless you have a good reason to change the database schema.
59
- def execute(raw_sql_string)
60
- @anki21_database.execute raw_sql_string
61
- end
62
-
63
- private
64
-
65
- def setup_package_instance_variables(name:, directory:)
66
- @name = check_name_is_valid(name: name)
67
- @directory = directory # TODO: check directory is valid
68
- @tmpdir = Dir.mktmpdir
69
- @tmp_files = []
70
- @anki21_database = setup_anki21_database_object
71
- @anki2_database = setup_anki2_database_object
72
- @media_file = setup_media
73
- @collection = Collection.new(anki_package: self)
74
- end
75
-
76
- def check_name_is_valid(name:)
77
- raise ArgumentError, NAME_ERROR_MESSAGE unless name.instance_of?(String) && !name.empty? && !name.include?(" ")
78
-
79
- name.end_with?(".apkg") ? name[0, name.length - 5] : name
80
- end
81
-
82
- def setup_anki21_database_object
83
- anki21_file_name = "collection.anki21"
84
- db = SQLite3::Database.new "#{@tmpdir}/#{anki21_file_name}", options: {}
85
- @tmp_files << anki21_file_name
86
- db.execute_batch ANKI_SCHEMA_DEFINITION
87
- db.execute CLEAN_COLLECTION_21_RECORD
88
- db.results_as_hash = true
89
- db
90
- end
91
-
92
- def setup_anki2_database_object
93
- anki2_file_name = "collection.anki2"
94
- db = SQLite3::Database.new "#{@tmpdir}/#{anki2_file_name}", options: {}
95
- @tmp_files << anki2_file_name
96
- db.execute_batch ANKI_SCHEMA_DEFINITION
97
- db.execute CLEAN_COLLECTION_2_RECORD
98
- db.close
99
- db
100
- end
101
-
102
- def setup_media
103
- media_file_path = FileUtils.touch("#{@tmpdir}/media")[0]
104
- media_file = File.open(media_file_path, mode: "w")
105
- media_file.write("{}")
106
- media_file.close
107
- @tmp_files << "media"
108
- media_file
109
- end
110
-
111
- def puts_error_and_standard_message(error:)
112
- puts "#{error}\n#{STANDARD_ERROR_MESSAGE}"
113
- end
114
-
115
- public
116
-
117
- ##
118
- # Creates a new object which represents the Anki SQLite3 database file at +path+
119
- #
120
- # Development has focused on ::new so this method is not recommended at this time
121
- def self.open(path:, create_backup: true)
122
- pathname = check_file_at_path_is_valid(path: path)
123
- copy_apkg_file(pathname: pathname) if create_backup
124
- @anki_package = new(name: pathname.basename.to_s, directory: pathname.dirname)
125
- end
126
-
127
- class << self
128
- private
129
-
130
- def check_file_at_path_is_valid(path:)
131
- pathname = Pathname.new(path)
132
- raise PATH_ERROR_MESSAGE unless pathname.file? && pathname.extname == ".apkg"
133
-
134
- pathname
135
- end
136
-
137
- def copy_apkg_file(pathname:)
138
- path = pathname.to_s
139
- FileUtils.cp path, "#{path}.copy-#{Time.now.to_i}"
140
- end
141
- end
142
-
143
- ##
144
- # Zips the temporary files into the *.apkg package and deletes the temporary files.
145
- def zip_and_close
146
- zip && close
147
- end
148
-
149
- private
150
-
151
- def zip
152
- Zip::File.open(target_zip_file, create: true) do |zip_file|
153
- @tmp_files.each do |file_name|
154
- zip_file.add(file_name, File.join(@tmpdir, file_name))
155
- end
156
- end
157
- true
158
- end
159
-
160
- def target_zip_file
161
- "#{@directory}/#{@name}.apkg"
162
- end
163
-
164
- def close
165
- @anki21_database.close
166
- FileUtils.rm_rf(@tmpdir)
167
- end
168
-
169
- public
170
-
171
- ##
172
- # Returns true if the database is open
173
- def open?
174
- !closed?
175
- end
176
-
177
- ##
178
- # Returns true if the database is closed
179
- def closed?
180
- @anki21_database.closed?
181
- end
182
- end
183
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pry"
4
- require "json"
5
-
6
- require_relative "deck"
7
- require_relative "deck_options_group"
8
- require_relative "helpers/time_helper"
9
- require_relative "note_type"
10
-
11
- module AnkiRecord
12
- ##
13
- # Collection represents the single record in the Anki database `col` table
14
- class Collection
15
- include AnkiRecord::TimeHelper
16
-
17
- ##
18
- # An array of the collection's note type objects
19
- attr_reader :note_types
20
-
21
- ##
22
- # An array of the collection's deck objects
23
- attr_reader :decks
24
-
25
- ##
26
- # Instantiates the collection object for the +anki_package+
27
- def initialize(anki_package:)
28
- setup_collection_instance_variables(anki_package: anki_package)
29
- end
30
-
31
- private
32
-
33
- # rubocop:disable Metrics/MethodLength
34
- # rubocop:disable Metrics/AbcSize
35
- def setup_collection_instance_variables(anki_package:)
36
- @anki_package = anki_package
37
- @id = col_record["id"]
38
- @crt = col_record["crt"]
39
- @last_modified_time = (mod = col_record["mod"]).zero? ? milliseconds_since_epoch : mod
40
- @scm = col_record["scm"]
41
- @ver = col_record["ver"]
42
- @dty = col_record["dty"]
43
- @usn = col_record["usn"]
44
- @ls = col_record["ls"]
45
- @configuration = JSON.parse(col_record["conf"])
46
- @note_types = JSON.parse(col_record["models"]).values.map do |model_hash|
47
- NoteType.new(collection: self, args: model_hash)
48
- end
49
- @decks = JSON.parse(col_record["decks"]).values.map do |deck_hash|
50
- Deck.new(collection: self, args: deck_hash)
51
- end
52
- @deck_option_groups = JSON.parse(col_record["dconf"]).values.map do |dconf_hash|
53
- DeckOptionsGroup.new(collection: self, args: dconf_hash)
54
- end
55
- @tags = JSON.parse(col_record["tags"])
56
- remove_instance_variable(:@col_record)
57
- end
58
- # rubocop:enable Metrics/AbcSize
59
- # rubocop:enable Metrics/MethodLength
60
-
61
- def col_record
62
- @col_record ||= @anki_package.execute("select * from col;").first
63
- end
64
- end
65
- end