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.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/Rakefile +6 -0
- data/current_project.bak +4 -0
- data/lib/dsu/cli.rb +24 -6
- data/lib/dsu/crud/json_file.rb +3 -0
- data/lib/dsu/migration/version.rb +1 -1
- data/lib/dsu/models/color_theme.rb +7 -58
- data/lib/dsu/models/configuration.rb +18 -3
- data/lib/dsu/models/entry_group.rb +0 -7
- data/lib/dsu/models/migration_version.rb +0 -1
- data/lib/dsu/models/project.rb +295 -0
- data/lib/dsu/presenters/base_presenter_ex.rb +1 -12
- data/lib/dsu/presenters/export/all_presenter.rb +14 -19
- data/lib/dsu/presenters/export/dates_presenter.rb +17 -20
- data/lib/dsu/presenters/import/all_presenter.rb +20 -25
- data/lib/dsu/presenters/import/dates_presenter.rb +25 -27
- data/lib/dsu/presenters/import/import_entry.rb +22 -0
- data/lib/dsu/presenters/import/import_file.rb +9 -1
- data/lib/dsu/presenters/project/create_presenter.rb +44 -0
- data/lib/dsu/presenters/project/delete_by_number_presenter.rb +54 -0
- data/lib/dsu/presenters/project/delete_presenter.rb +53 -0
- data/lib/dsu/presenters/project/list_presenter.rb +24 -0
- data/lib/dsu/presenters/project/rename_by_number_presenter.rb +63 -0
- data/lib/dsu/presenters/project/rename_presenter.rb +57 -0
- data/lib/dsu/presenters/project/use_by_number_presenter.rb +53 -0
- data/lib/dsu/presenters/project/use_presenter.rb +52 -0
- data/lib/dsu/services/entry_group/exporter_service.rb +22 -5
- data/lib/dsu/services/entry_group/importer_service.rb +41 -8
- data/lib/dsu/services/project/hydrator_service.rb +40 -0
- data/lib/dsu/services/project/rename_service.rb +70 -0
- data/lib/dsu/subcommands/export.rb +4 -2
- data/lib/dsu/subcommands/import.rb +7 -3
- data/lib/dsu/subcommands/project.rb +149 -0
- data/lib/dsu/support/ask.rb +10 -3
- data/lib/dsu/support/color_themable.rb +1 -1
- data/lib/dsu/support/command_hookable.rb +7 -2
- data/lib/dsu/support/descriptable.rb +5 -21
- data/lib/dsu/support/fileable.rb +39 -1
- data/lib/dsu/support/project_file_system.rb +121 -0
- data/lib/dsu/support/short_string.rb +24 -0
- data/lib/dsu/support/time_comparable.rb +2 -0
- data/lib/dsu/support/transform_project_name.rb +24 -0
- data/lib/dsu/validators/project_name_validator.rb +58 -0
- data/lib/dsu/version.rb +1 -1
- data/lib/dsu/views/base_list_view.rb +41 -0
- data/lib/dsu/views/export.rb +60 -6
- data/lib/dsu/views/import.rb +83 -7
- data/lib/dsu/views/import_dates.rb +17 -0
- data/lib/dsu/views/project/create.rb +87 -0
- data/lib/dsu/views/project/delete.rb +96 -0
- data/lib/dsu/views/project/delete_by_number.rb +19 -0
- data/lib/dsu/views/project/list.rb +115 -0
- data/lib/dsu/views/project/rename.rb +98 -0
- data/lib/dsu/views/project/rename_by_number.rb +21 -0
- data/lib/dsu/views/project/use.rb +97 -0
- data/lib/dsu/views/project/use_by_number.rb +19 -0
- data/lib/dsu.rb +2 -10
- data/lib/locales/en/active_record.yml +9 -0
- data/lib/locales/en/commands.yml +9 -3
- data/lib/locales/en/miscellaneous.yml +4 -0
- data/lib/locales/en/services.yml +4 -0
- data/lib/locales/en/subcommands.yml +247 -15
- data/project.bak +0 -0
- metadata +34 -9
- data/lib/dsu/presenters/export/messages.rb +0 -32
- data/lib/dsu/presenters/export/nothing_to_export.rb +0 -13
- data/lib/dsu/presenters/export/service_callable.rb +0 -20
- data/lib/dsu/presenters/import/messages.rb +0 -42
- 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
|
-
|
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
|
-
|
16
|
-
include NothingToExport
|
17
|
-
include ServiceCallable
|
18
|
-
include Support::Ask
|
11
|
+
attr_reader :export_file_path
|
19
12
|
|
20
|
-
def
|
21
|
-
return
|
13
|
+
def respond(response:)
|
14
|
+
return false unless response
|
22
15
|
|
23
|
-
export_file_path =
|
16
|
+
@export_file_path = exporter_service.call
|
17
|
+
end
|
24
18
|
|
25
|
-
|
26
|
-
|
19
|
+
def nothing_to_export?
|
20
|
+
entry_group_count.zero?
|
27
21
|
end
|
28
22
|
|
29
|
-
def
|
30
|
-
|
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
|
40
|
-
|
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
|
44
|
-
|
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 '../../
|
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
|
-
|
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
|
27
|
-
return
|
22
|
+
def respond(response:)
|
23
|
+
return false unless response
|
28
24
|
|
29
|
-
export_file_path =
|
25
|
+
@export_file_path = exporter_service.call
|
26
|
+
end
|
30
27
|
|
31
|
-
|
32
|
-
|
28
|
+
def nothing_to_export?
|
29
|
+
entry_groups.empty?
|
33
30
|
end
|
34
31
|
|
35
|
-
def
|
36
|
-
|
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
|
48
|
-
|
49
|
-
|
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
|
53
|
-
|
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
|
-
|
17
|
-
|
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
|
27
|
-
|
28
|
-
|
29
|
-
display_import_messages importer_service_call
|
24
|
+
def respond
|
25
|
+
@import_messages = importer_service.call
|
30
26
|
end
|
31
27
|
|
32
|
-
def
|
33
|
-
|
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
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
53
|
-
|
54
|
-
|
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
|
-
|
17
|
-
|
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
|
29
|
-
|
30
|
-
|
31
|
-
display_import_messages importer_service_call
|
26
|
+
def respond
|
27
|
+
@import_messages = importer_service.call
|
32
28
|
end
|
33
29
|
|
34
|
-
def
|
35
|
-
|
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
|
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
|
-
|
48
|
-
|
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[
|
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
|
58
|
-
|
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.
|
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
|