dsu 2.4.4 → 3.0.0.alpha.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/CHANGELOG.md +42 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +2 -2
  6. data/Rakefile +6 -0
  7. data/current_project.bak +4 -0
  8. data/lib/dsu/cli.rb +24 -6
  9. data/lib/dsu/crud/json_file.rb +3 -0
  10. data/lib/dsu/migration/version.rb +1 -1
  11. data/lib/dsu/models/color_theme.rb +7 -58
  12. data/lib/dsu/models/configuration.rb +18 -3
  13. data/lib/dsu/models/entry_group.rb +0 -7
  14. data/lib/dsu/models/migration_version.rb +0 -1
  15. data/lib/dsu/models/project.rb +295 -0
  16. data/lib/dsu/presenters/base_presenter_ex.rb +1 -12
  17. data/lib/dsu/presenters/export/all_presenter.rb +14 -19
  18. data/lib/dsu/presenters/export/dates_presenter.rb +17 -20
  19. data/lib/dsu/presenters/import/all_presenter.rb +20 -25
  20. data/lib/dsu/presenters/import/dates_presenter.rb +25 -27
  21. data/lib/dsu/presenters/import/import_entry.rb +22 -0
  22. data/lib/dsu/presenters/import/import_file.rb +9 -1
  23. data/lib/dsu/presenters/project/create_presenter.rb +44 -0
  24. data/lib/dsu/presenters/project/delete_by_number_presenter.rb +54 -0
  25. data/lib/dsu/presenters/project/delete_presenter.rb +53 -0
  26. data/lib/dsu/presenters/project/list_presenter.rb +24 -0
  27. data/lib/dsu/presenters/project/rename_by_number_presenter.rb +63 -0
  28. data/lib/dsu/presenters/project/rename_presenter.rb +57 -0
  29. data/lib/dsu/presenters/project/use_by_number_presenter.rb +53 -0
  30. data/lib/dsu/presenters/project/use_presenter.rb +52 -0
  31. data/lib/dsu/services/entry_group/exporter_service.rb +22 -5
  32. data/lib/dsu/services/entry_group/importer_service.rb +41 -8
  33. data/lib/dsu/services/project/hydrator_service.rb +40 -0
  34. data/lib/dsu/services/project/rename_service.rb +70 -0
  35. data/lib/dsu/subcommands/export.rb +4 -2
  36. data/lib/dsu/subcommands/import.rb +7 -3
  37. data/lib/dsu/subcommands/project.rb +149 -0
  38. data/lib/dsu/support/ask.rb +10 -3
  39. data/lib/dsu/support/color_themable.rb +1 -1
  40. data/lib/dsu/support/command_hookable.rb +7 -2
  41. data/lib/dsu/support/descriptable.rb +5 -21
  42. data/lib/dsu/support/fileable.rb +39 -1
  43. data/lib/dsu/support/project_file_system.rb +121 -0
  44. data/lib/dsu/support/short_string.rb +24 -0
  45. data/lib/dsu/support/time_comparable.rb +2 -0
  46. data/lib/dsu/support/transform_project_name.rb +24 -0
  47. data/lib/dsu/validators/project_name_validator.rb +58 -0
  48. data/lib/dsu/version.rb +1 -1
  49. data/lib/dsu/views/base_list_view.rb +41 -0
  50. data/lib/dsu/views/export.rb +60 -6
  51. data/lib/dsu/views/import.rb +83 -7
  52. data/lib/dsu/views/import_dates.rb +17 -0
  53. data/lib/dsu/views/project/create.rb +87 -0
  54. data/lib/dsu/views/project/delete.rb +96 -0
  55. data/lib/dsu/views/project/delete_by_number.rb +19 -0
  56. data/lib/dsu/views/project/list.rb +115 -0
  57. data/lib/dsu/views/project/rename.rb +98 -0
  58. data/lib/dsu/views/project/rename_by_number.rb +21 -0
  59. data/lib/dsu/views/project/use.rb +97 -0
  60. data/lib/dsu/views/project/use_by_number.rb +19 -0
  61. data/lib/dsu.rb +2 -10
  62. data/lib/locales/en/active_record.yml +9 -0
  63. data/lib/locales/en/commands.yml +9 -3
  64. data/lib/locales/en/miscellaneous.yml +4 -0
  65. data/lib/locales/en/services.yml +4 -0
  66. data/lib/locales/en/subcommands.yml +247 -15
  67. data/project.bak +0 -0
  68. metadata +34 -9
  69. data/lib/dsu/presenters/export/messages.rb +0 -32
  70. data/lib/dsu/presenters/export/nothing_to_export.rb +0 -13
  71. data/lib/dsu/presenters/export/service_callable.rb +0 -20
  72. data/lib/dsu/presenters/import/messages.rb +0 -42
  73. data/lib/dsu/presenters/import/service_callable.rb +0 -21
@@ -0,0 +1,295 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ require_relative '../crud/json_file'
6
+ require_relative '../migration/version'
7
+ require_relative '../models/configuration'
8
+ require_relative '../services/project/hydrator_service'
9
+ require_relative '../services/project/rename_service'
10
+ require_relative '../support/descriptable'
11
+ require_relative '../support/fileable'
12
+ require_relative '../support/project_file_system'
13
+ require_relative '../validators/description_validator'
14
+ require_relative '../validators/project_name_validator'
15
+ require_relative '../validators/version_validator'
16
+
17
+ module Dsu
18
+ module Models
19
+ # This class represents a project. A project is a collection of entry groups.
20
+ class Project
21
+ include ActiveModel::Model
22
+ include Support::Descriptable
23
+ include Support::Fileable
24
+ include Support::ProjectFileSystem
25
+
26
+ VERSION = Migration::VERSION
27
+ MIN_PROJECT_NAME_LENGTH = 2
28
+ MAX_PROJECT_NAME_LENGTH = 24
29
+ MIN_DESCRIPTION_LENGTH = 2
30
+ MAX_DESCRIPTION_LENGTH = 32
31
+
32
+ attr_reader :project_name, :current_project_file, :description, :version, :options
33
+
34
+ validates_with Validators::DescriptionValidator
35
+ validates_with Validators::ProjectNameValidator
36
+ validates_with Validators::VersionValidator
37
+
38
+ def initialize(project_name:, description: nil, version: nil, options: {})
39
+ raise ArgumentError, 'project_name is blank' if project_name.blank?
40
+ raise ArgumentError, 'version is the wrong object type' unless version.is_a?(Integer) || version.nil?
41
+
42
+ self.project_name = project_name
43
+ self.description = description
44
+ self.version = version || VERSION
45
+ self.options = options || {}
46
+ end
47
+
48
+ # Override == and hash so that we can compare Entry Group objects.
49
+ def ==(other)
50
+ other.is_a?(Project) &&
51
+ project_name == other.project_name &&
52
+ description == other.description &&
53
+ version == other.version
54
+ end
55
+ alias eql? ==
56
+
57
+ def can_delete?
58
+ self.class.can_delete?(project_name: project_name)
59
+ end
60
+
61
+ def create
62
+ self.class.create(project_name: project_name, description: description)
63
+ end
64
+ alias save create
65
+
66
+ def create!
67
+ self.class.create!(project_name: project_name, description: description)
68
+ end
69
+ alias save! create!
70
+
71
+ def current_project?
72
+ self.class.current_project?(project_name: project_name)
73
+ end
74
+
75
+ def default!
76
+ return if default_project?
77
+
78
+ self.class.default!(project: self)
79
+ end
80
+
81
+ def default_project?
82
+ self.class.default_project?(project_name: project_name)
83
+ end
84
+
85
+ def delete
86
+ self.class.delete(project_name: project_name)
87
+ end
88
+
89
+ def delete!
90
+ self.class.delete!(project_name: project_name)
91
+ end
92
+
93
+ def hash
94
+ [project_name, description, version].map(&:hash).hash
95
+ end
96
+
97
+ def project_file
98
+ self.class.project_file(project_name: project_name)
99
+ end
100
+
101
+ def project_folder
102
+ self.class.project_folder(project_name: project_name)
103
+ end
104
+
105
+ def rename!(new_project_name:, new_project_description: nil)
106
+ self.class.rename!(project_name: project_name,
107
+ new_project_name: new_project_name, new_project_description: new_project_description, options: options)
108
+ end
109
+
110
+ def to_h
111
+ {
112
+ version: version,
113
+ project_name: project_name,
114
+ description: description
115
+ }
116
+ end
117
+
118
+ # def update
119
+ # self.class.update(project_name: project_name, description: description, version: version, options: options)
120
+ # end
121
+
122
+ # def update!
123
+ # self.class.update!(project_name: project_name, description: description, version: version, options: options)
124
+ # end
125
+
126
+ def use!
127
+ return if current_project?
128
+
129
+ self.class.use!(project: self)
130
+ end
131
+
132
+ class << self
133
+ delegate :project_file_for, :project_folder_for, to: Support::Fileable
134
+
135
+ def all
136
+ project_metadata.map do |metadata|
137
+ find(project_name: metadata[:project_name])
138
+ end
139
+ end
140
+
141
+ def can_delete?(project_name:)
142
+ exist?(project_name: project_name) &&
143
+ # Cannot delete the last project.
144
+ count > 1 &&
145
+ # Do not allow the project to be deleted if it
146
+ # is currently the default project.
147
+ # The user needs to change to another default
148
+ # project before they can delete this project.
149
+ !default_project?(project_name: project_name)
150
+ end
151
+
152
+ def count
153
+ project_metadata.count
154
+ end
155
+
156
+ def create(project_name:, description: nil, options: {})
157
+ Models::Project.new(project_name: project_name, description: description, options: options).tap do |project|
158
+ project.validate!
159
+ initialize_project(project_name: project_name)
160
+ Crud::JsonFile.write!(file_data: project.to_h,
161
+ file_path: project_file_for(project_name: project_name))
162
+ end
163
+ end
164
+ alias update create
165
+
166
+ def create!(project_name:, description: nil, options: {})
167
+ if exist?(project_name: project_name)
168
+ raise I18n.t('models.project.errors.already_exists', project_name: project_name)
169
+ end
170
+
171
+ create(project_name: project_name, description: description, options: options)
172
+ end
173
+ alias update! create!
174
+
175
+ def current_project
176
+ find(project_name: current_project_name)
177
+ end
178
+
179
+ def current_project?(project_name:)
180
+ current_project_name == project_name
181
+ end
182
+
183
+ def default!(project:)
184
+ project.validate!
185
+
186
+ Models::Configuration.new.tap do |configuration|
187
+ configuration.default_project = project.project_name
188
+ configuration.save!
189
+ end
190
+ end
191
+
192
+ def default_project
193
+ find(project_name: default_project_name)
194
+ end
195
+
196
+ def default_project?(project_name:)
197
+ project_name == default_project_name
198
+ end
199
+
200
+ def delete(project_name:)
201
+ return false unless can_delete?(project_name: project_name)
202
+
203
+ project_folder = project_folder_for(project_name: project_name)
204
+ FileUtils.rm_rf(project_folder)
205
+
206
+ true
207
+ end
208
+
209
+ def delete!(project_name:)
210
+ unless exist?(project_name: project_name)
211
+ raise I18n.t('models.project.errors.does_not_exist', project_name: project_name)
212
+ end
213
+
214
+ raise I18n.t('models.project.errors.delete_only_project', project_name: project_name) unless count > 1
215
+
216
+ if default_project?(project_name: project_name)
217
+ raise I18n.t('models.project.errors.delete_default_project', project_name: project_name)
218
+ end
219
+
220
+ delete(project_name: project_name)
221
+ end
222
+
223
+ def find(project_name:)
224
+ unless project_folder_exist?(project_name: project_name)
225
+ raise I18n.t('models.project.errors.does_not_exist', project_name: project_name)
226
+ end
227
+
228
+ project_file = project_file_for(project_name: project_name)
229
+
230
+ unless project_file_exist?(project_name: project_name)
231
+ raise I18n.t('models.project.errors.project_file_not_exist', project_file: project_file)
232
+ end
233
+
234
+ project_hash = Crud::JsonFile.read!(file_path: project_file)
235
+ Services::Project::HydratorService.new(project_hash: project_hash).call
236
+ end
237
+
238
+ # project_number is 1 based.
239
+ def find_by_number(project_number:)
240
+ project = project_metadata.find do |metadata|
241
+ metadata[:project_number] == project_number.to_i
242
+ end
243
+ return unless project
244
+
245
+ find(project_name: project[:project_name])
246
+ end
247
+
248
+ # def find_or_create(project_name:)
249
+ # find_or_initialize(project_name: project_name).tap do |project|
250
+ # project.save! unless project.persisted?
251
+ # end
252
+ # end
253
+
254
+ def find_or_initialize(project_name:)
255
+ return Models::Project.new(project_name: project_name) unless project_file_exist?(project_name: project_name)
256
+
257
+ project_file = project_file_for(project_name: project_name)
258
+ project_hash = Crud::JsonFile.read!(file_path: project_file)
259
+ Services::Project::HydratorService.new(project_hash: project_hash).call
260
+ end
261
+
262
+ def rename!(project_name:, new_project_name:, new_project_description: nil, options: {})
263
+ Services::Project::RenameService.new(from_project_name: project_name,
264
+ to_project_name: new_project_name, to_project_description: new_project_description, options: options).call
265
+ end
266
+
267
+ def use!(project:)
268
+ project.validate!
269
+
270
+ current_project_hash = { version: project.version, project_name: project.project_name }
271
+ Crud::JsonFile.write!(file_data: current_project_hash, file_path: current_project_file)
272
+ end
273
+ end
274
+
275
+ private
276
+
277
+ attr_writer :current_project_file, :options, :version
278
+
279
+ def description=(value)
280
+ @description = if value.blank?
281
+ "#{project_name} project"
282
+ else
283
+ value
284
+ end
285
+ end
286
+
287
+ def project_name=(value)
288
+ @project_name = begin
289
+ @current_project_file = project_folder_for(project_name: value)
290
+ value
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
@@ -1,26 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'delegate'
4
- require_relative '../models/color_theme'
5
- require_relative '../support/color_themable'
6
-
7
3
  module Dsu
8
4
  module Presenters
9
5
  class BasePresenterEx
10
- include Support::ColorThemable
11
-
12
6
  def initialize(options: {})
13
7
  @options = options&.dup || {}
14
- @color_theme = Models::ColorTheme.find(theme_name: theme_name)
15
8
  end
16
9
 
17
10
  private
18
11
 
19
- attr_reader :color_theme, :options
20
-
21
- def theme_name
22
- @theme_name ||= options.fetch(:theme_name, Models::Configuration.new.theme_name)
23
- end
12
+ attr_accessor :options
24
13
  end
25
14
  end
26
15
  end
@@ -2,32 +2,26 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/exporter_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
7
- require_relative 'messages'
8
- require_relative 'nothing_to_export'
9
- require_relative 'service_callable'
10
6
 
11
7
  module Dsu
12
8
  module Presenters
13
9
  module Export
14
10
  class AllPresenter < BasePresenterEx
15
- include Messages
16
- include NothingToExport
17
- include ServiceCallable
18
- include Support::Ask
11
+ attr_reader :export_file_path
19
12
 
20
- def render(response:)
21
- return display_cancelled_message unless response
13
+ def respond(response:)
14
+ return false unless response
22
15
 
23
- export_file_path = exporter_service_call
16
+ @export_file_path = exporter_service.call
17
+ end
24
18
 
25
- display_exported_message
26
- display_exported_to_message(file_path: export_file_path)
19
+ def nothing_to_export?
20
+ entry_group_count.zero?
27
21
  end
28
22
 
29
- def display_export_prompt
30
- yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
23
+ def entry_group_count
24
+ entry_groups&.count || 0
31
25
  end
32
26
 
33
27
  private
@@ -36,12 +30,13 @@ module Dsu
36
30
  @entry_groups ||= Models::EntryGroup.all
37
31
  end
38
32
 
39
- def export_prompt
40
- I18n.t('subcommands.export.prompts.export_all_confirm', count: entry_groups.count)
33
+ def exporter_service
34
+ Services::EntryGroup::ExporterService.new(project_name: project_name,
35
+ entry_groups: entry_groups, options: options)
41
36
  end
42
37
 
43
- def export_prompt_options
44
- I18n.t('subcommands.export.prompts.options')
38
+ def project_name
39
+ @project_name ||= Models::Project.current_project.project_name
45
40
  end
46
41
  end
47
42
  end
@@ -1,39 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../models/entry_group'
4
- require_relative '../../support/ask'
4
+ require_relative '../../services/entry_group/exporter_service'
5
5
  require_relative '../base_presenter_ex'
6
- require_relative 'messages'
7
- require_relative 'nothing_to_export'
8
- require_relative 'service_callable'
9
6
 
10
7
  module Dsu
11
8
  module Presenters
12
9
  module Export
13
10
  class DatesPresenter < BasePresenterEx
14
- include Messages
15
- include NothingToExport
16
- include ServiceCallable
17
- include Support::Ask
11
+ attr_reader :export_file_path
18
12
 
19
13
  def initialize(from:, to:, options: {})
20
14
  super(options: options)
21
15
 
22
16
  @from = from
23
17
  @to = to
18
+
19
+ self.options[:times] = [from, to]
24
20
  end
25
21
 
26
- def render(response:)
27
- return display_cancelled_message unless response
22
+ def respond(response:)
23
+ return false unless response
28
24
 
29
- export_file_path = exporter_service_call
25
+ @export_file_path = exporter_service.call
26
+ end
30
27
 
31
- display_exported_message
32
- display_exported_to_message(file_path: export_file_path)
28
+ def nothing_to_export?
29
+ entry_groups.empty?
33
30
  end
34
31
 
35
- def display_export_prompt
36
- yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
32
+ def entry_group_count
33
+ entry_groups&.count || 0
37
34
  end
38
35
 
39
36
  private
@@ -44,13 +41,13 @@ module Dsu
44
41
  Models::EntryGroup.entry_groups(between: [from, to])
45
42
  end
46
43
 
47
- def export_prompt
48
- I18n.t('subcommands.export.prompts.export_dates_confirm',
49
- from: from.to_date, to: to.to_date, count: entry_groups.count)
44
+ def exporter_service
45
+ Services::EntryGroup::ExporterService.new(project_name: project_name,
46
+ entry_groups: entry_groups, options: options)
50
47
  end
51
48
 
52
- def export_prompt_options
53
- I18n.t('subcommands.export.prompts.options')
49
+ def project_name
50
+ @project_name ||= Models::Project.current_project.project_name
54
51
  end
55
52
  end
56
53
  end
@@ -2,20 +2,18 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/importer_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
6
+ require_relative 'import_entry'
7
7
  require_relative 'import_file'
8
- require_relative 'messages'
9
- require_relative 'service_callable'
10
8
 
11
9
  module Dsu
12
10
  module Presenters
13
11
  module Import
14
12
  class AllPresenter < BasePresenterEx
13
+ include ImportEntry
15
14
  include ImportFile
16
- include Messages
17
- include ServiceCallable
18
- include Support::Ask
15
+
16
+ attr_reader :import_file_path, :import_messages
19
17
 
20
18
  def initialize(import_file_path:, options: {})
21
19
  super(options: options)
@@ -23,38 +21,35 @@ module Dsu
23
21
  @import_file_path = import_file_path
24
22
  end
25
23
 
26
- def render(response:)
27
- return display_cancelled_message unless response
28
-
29
- display_import_messages importer_service_call
24
+ def respond
25
+ @import_messages = importer_service.call
30
26
  end
31
27
 
32
- def display_import_prompt
33
- yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
28
+ def project_name
29
+ @project_name ||= Models::Project.current_project.project_name
34
30
  end
35
31
 
36
32
  private
37
33
 
38
- attr_reader :import_file_path, :options
39
-
40
34
  def import_entry_groups
41
35
  @import_entry_groups ||= CSV.foreach(import_file_path,
42
- headers: true).with_object({}) do |entry_group_entry, entry_groups_hash|
43
- next unless entry_group_entry['version'].to_i == Dsu::Migration::VERSION
36
+ headers: true, header_converters: :symbol).with_object({}) do |entry_group_entry, entry_groups_hash|
37
+ next unless import_entry?(entry_group_entry)
44
38
 
45
- Date.parse(entry_group_entry['entry_group']).to_s.tap do |time|
46
- entry_groups_hash[time] = [] unless entry_groups_hash.key?(time)
47
- entry_groups_hash[time] << entry_group_entry['entry_group_entry']
39
+ project_name = entry_group_entry[:project_name]
40
+ entry_groups_hash[project_name] = {} unless entry_groups_hash.key?(project_name)
41
+
42
+ Date.parse(entry_group_entry[:entry_group]).to_s.tap do |time|
43
+ entry_groups_hash[project_name][time] = [] unless entry_groups_hash[project_name].key?(time)
44
+ entry_groups_hash[project_name][time] << entry_group_entry[:entry_group_entry]
48
45
  end
49
46
  end
50
47
  end
51
48
 
52
- def import_prompt
53
- I18n.t('subcommands.import.prompts.import_all_confirm', count: import_entry_groups.count)
54
- end
55
-
56
- def import_prompt_options
57
- I18n.t('subcommands.import.prompts.options')
49
+ def importer_service
50
+ @importer_service ||= Services::EntryGroup::ImporterService.new(
51
+ import_projects: import_entry_groups, options: options
52
+ )
58
53
  end
59
54
  end
60
55
  end
@@ -2,20 +2,18 @@
2
2
 
3
3
  require_relative '../../models/entry_group'
4
4
  require_relative '../../services/entry_group/importer_service'
5
- require_relative '../../support/ask'
6
5
  require_relative '../base_presenter_ex'
6
+ require_relative 'import_entry'
7
7
  require_relative 'import_file'
8
- require_relative 'messages'
9
- require_relative 'service_callable'
10
8
 
11
9
  module Dsu
12
10
  module Presenters
13
11
  module Import
14
12
  class DatesPresenter < BasePresenterEx
13
+ include ImportEntry
15
14
  include ImportFile
16
- include Messages
17
- include ServiceCallable
18
- include Support::Ask
15
+
16
+ attr_reader :from, :to, :import_file_path, :import_messages
19
17
 
20
18
  def initialize(from:, to:, import_file_path:, options: {})
21
19
  super(options: options)
@@ -25,47 +23,47 @@ module Dsu
25
23
  @import_file_path = import_file_path
26
24
  end
27
25
 
28
- def render(response:)
29
- return display_cancelled_message unless response
30
-
31
- display_import_messages importer_service_call
26
+ def respond
27
+ @import_messages = importer_service.call
32
28
  end
33
29
 
34
- def display_import_prompt
35
- yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
30
+ def project_name
31
+ @project_name ||= Models::Project.current_project.project_name
36
32
  end
37
33
 
38
34
  private
39
35
 
40
- attr_reader :from, :to, :import_file_path, :options
41
-
42
36
  def import_entry_groups
43
37
  @import_entry_groups ||= CSV.foreach(import_file_path,
44
- headers: true).with_object({}) do |entry_group_entry, entry_groups_hash|
45
- next unless entry_group_entry['version'].to_i == Dsu::Migration::VERSION
38
+ headers: true, header_converters: :symbol).with_object({}) do |entry_group_entry, entry_groups_hash|
39
+ next unless import_entry?(entry_group_entry)
40
+
41
+ entry_group_time = middle_of_day_for(entry_group_entry[:entry_group])
42
+ next unless time_between_to_and_from_dates?(entry_group_time)
46
43
 
47
- entry_group_time = middle_of_day_for(entry_group_entry['entry_group'])
48
- next unless entry_group_time.to_date.between?(from.to_date, to.to_date)
44
+ project_name = entry_group_entry[:project_name]
45
+ entry_groups_hash[project_name] = {} unless entry_groups_hash.key?(project_name)
49
46
 
50
47
  entry_group_time.to_date.to_s.tap do |time|
51
- entry_groups_hash[time] = [] unless entry_groups_hash.key?(time)
52
- entry_groups_hash[time] << entry_group_entry['entry_group_entry']
48
+ entry_groups_hash[project_name][time] = [] unless entry_groups_hash[project_name].key?(time)
49
+ entry_groups_hash[project_name][time] << entry_group_entry[:entry_group_entry]
53
50
  end
54
51
  end
55
52
  end
56
53
 
57
- def import_prompt
58
- I18n.t('subcommands.import.prompts.import_dates_confirm',
59
- from: from.to_date, to: to.to_date, count: import_entry_groups.keys.count)
60
- end
61
-
62
- def import_prompt_options
63
- I18n.t('subcommands.import.prompts.options')
54
+ def time_between_to_and_from_dates?(entry_group_time)
55
+ entry_group_time.to_date.between?(from.to_date, to.to_date)
64
56
  end
65
57
 
66
58
  def middle_of_day_for(date_string)
67
59
  Time.parse(date_string).in_time_zone.middle_of_day
68
60
  end
61
+
62
+ def importer_service
63
+ @importer_service ||= Services::EntryGroup::ImporterService.new(
64
+ import_projects: import_entry_groups, options: options
65
+ )
66
+ end
69
67
  end
70
68
  end
71
69
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../migration/version'
4
+
5
+ module Dsu
6
+ module Presenters
7
+ module Import
8
+ module ImportEntry
9
+ def overriding_project?
10
+ options&.fetch(:override, false)
11
+ end
12
+
13
+ private
14
+
15
+ def import_entry?(entry_group_entry)
16
+ entry_group_entry[:version].to_i == Dsu::Migration::VERSION &&
17
+ (overriding_project? || entry_group_entry[:project_name] == project_name)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -11,7 +11,7 @@ module Dsu
11
11
  def nothing_to_import?
12
12
  return true unless import_file_path_exist?
13
13
 
14
- import_entry_groups.empty?
14
+ import_entry_groups.count.zero?
15
15
  end
16
16
 
17
17
  def import_entry_groups
@@ -19,6 +19,14 @@ module Dsu
19
19
  # Example: { '2023-12-32' => ['Entry description 1', 'Entry description 2', ...] }
20
20
  raise NotImplementedError
21
21
  end
22
+
23
+ def import_entry_groups_count
24
+ if overriding_project?
25
+ import_entry_groups&.first&.count || 0
26
+ else
27
+ import_entry_groups[project_name]&.count || 0
28
+ end
29
+ end
22
30
  end
23
31
  end
24
32
  end