dsu 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/Gemfile.lock +33 -6
- data/README.md +40 -2
- data/lib/dsu/cli.rb +5 -0
- data/lib/dsu/models/color_theme.rb +3 -0
- data/lib/dsu/models/entry.rb +3 -0
- data/lib/dsu/models/entry_group.rb +8 -2
- data/lib/dsu/presenters/export/all_presenter.rb +3 -1
- data/lib/dsu/presenters/export/dates_presenter.rb +3 -1
- data/lib/dsu/presenters/export/messages.rb +4 -3
- data/lib/dsu/presenters/export/nothing_to_export.rb +13 -0
- data/lib/dsu/presenters/import/all_presenter.rb +62 -0
- data/lib/dsu/presenters/import/dates_presenter.rb +72 -0
- data/lib/dsu/presenters/import/import_file.rb +25 -0
- data/lib/dsu/presenters/import/messages.rb +42 -0
- data/lib/dsu/presenters/import/service_callable.rb +21 -0
- data/lib/dsu/services/entry_group/exporter_service.rb +39 -21
- data/lib/dsu/services/entry_group/importer_service.rb +84 -0
- data/lib/dsu/subcommands/export.rb +2 -1
- data/lib/dsu/subcommands/import.rb +68 -0
- data/lib/dsu/support/ask.rb +6 -2
- data/lib/dsu/support/command_options/dsu_times.rb +1 -1
- data/lib/dsu/validators/description_validator.rb +16 -5
- data/lib/dsu/validators/entries_validator.rb +1 -0
- data/lib/dsu/version.rb +1 -1
- data/lib/dsu/views/export.rb +2 -0
- data/lib/dsu/views/import.rb +29 -0
- data/lib/locales/en/commands.yml +4 -0
- data/lib/locales/en/subcommands.yml +93 -0
- metadata +21 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '049f61bd321689f9690d42ba2cb72e0901c63e78a1c6454ee8fe0126e8dc0396'
|
4
|
+
data.tar.gz: e1687331fa05a352b29800f86b43de93b780a8efa50d74e1be13ce83fa331b23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d5547b1ce5e805ec2ea8399ed01b76a7c03180b3ddc14b1ad9225cbacec79dbc963b37b24224fac6397b0a88b51772741e0a2840db70064f37f179b2e1d7aa5
|
7
|
+
data.tar.gz: a0146a22e7fe3404ee63de7f26d1ce66d945685ec21e41fd1b20eaed4b5cea037920552a5dc52be26c6394dfdf90ff9382d8702ffcfe3b9de571e9b1c0d59e6d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## [2.4.0] 2024-01-01
|
2
|
+
|
3
|
+
Enhancements
|
4
|
+
|
5
|
+
- Add `dsu import` command to import DSU entries from a comma-delimited csv file. See `dsu help import` for more information.
|
6
|
+
- Update README.md to reflect new `dsu import` command.
|
7
|
+
|
8
|
+
Changes
|
9
|
+
|
10
|
+
- Update ruby gems.
|
11
|
+
|
12
|
+
## [2.3.2] 2023-12-30
|
13
|
+
|
14
|
+
Changes
|
15
|
+
|
16
|
+
- Display a "Nothing to export" message if no entries are found, rather than prompting the user "export 0 entry groups" when using the `dsu export` command.
|
17
|
+
- Add specs for Export::AllPresenter and Export::DatesPresenter
|
18
|
+
|
1
19
|
## [2.3.1] 2023-12-25
|
2
20
|
|
3
21
|
Changes
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dsu (2.
|
4
|
+
dsu (2.4.0)
|
5
5
|
activemodel (>= 7.0.8, < 8.0)
|
6
6
|
activesupport (>= 7.0.8, < 8.0)
|
7
7
|
colorize (>= 0.8.1, < 1.0)
|
@@ -37,13 +37,38 @@ GEM
|
|
37
37
|
dotenv (2.8.1)
|
38
38
|
drb (2.2.0)
|
39
39
|
ruby2_keywords
|
40
|
-
|
40
|
+
dry-configurable (1.1.0)
|
41
|
+
dry-core (~> 1.0, < 2)
|
42
|
+
zeitwerk (~> 2.6)
|
43
|
+
dry-core (1.0.1)
|
44
|
+
concurrent-ruby (~> 1.0)
|
45
|
+
zeitwerk (~> 2.6)
|
46
|
+
dry-inflector (1.0.0)
|
47
|
+
dry-initializer (3.1.1)
|
48
|
+
dry-logic (1.5.0)
|
49
|
+
concurrent-ruby (~> 1.0)
|
50
|
+
dry-core (~> 1.0, < 2)
|
51
|
+
zeitwerk (~> 2.6)
|
52
|
+
dry-schema (1.13.3)
|
53
|
+
concurrent-ruby (~> 1.0)
|
54
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
55
|
+
dry-core (~> 1.0, < 2)
|
56
|
+
dry-initializer (~> 3.0)
|
57
|
+
dry-logic (>= 1.4, < 2)
|
58
|
+
dry-types (>= 1.7, < 2)
|
59
|
+
zeitwerk (~> 2.6)
|
60
|
+
dry-types (1.7.1)
|
61
|
+
concurrent-ruby (~> 1.0)
|
62
|
+
dry-core (~> 1.0)
|
63
|
+
dry-inflector (~> 1.0)
|
64
|
+
dry-logic (~> 1.4)
|
65
|
+
zeitwerk (~> 2.6)
|
66
|
+
factory_bot (6.4.5)
|
41
67
|
activesupport (>= 5.0.0)
|
42
68
|
ffaker (2.23.0)
|
43
69
|
i18n (1.14.1)
|
44
70
|
concurrent-ruby (~> 1.0)
|
45
71
|
json (2.7.1)
|
46
|
-
kwalify (0.7.2)
|
47
72
|
language_server-protocol (3.17.0.3)
|
48
73
|
method_source (1.0.0)
|
49
74
|
minitest (5.20.0)
|
@@ -62,10 +87,11 @@ GEM
|
|
62
87
|
racc (1.7.3)
|
63
88
|
rainbow (3.1.1)
|
64
89
|
rake (13.1.0)
|
65
|
-
reek (6.
|
66
|
-
|
90
|
+
reek (6.2.0)
|
91
|
+
dry-schema (~> 1.13.0)
|
67
92
|
parser (~> 3.2.0)
|
68
93
|
rainbow (>= 2.0, < 4.0)
|
94
|
+
rexml (~> 3.1)
|
69
95
|
regexp_parser (2.8.3)
|
70
96
|
rexml (3.2.6)
|
71
97
|
rspec (3.12.0)
|
@@ -114,10 +140,11 @@ GEM
|
|
114
140
|
simplecov-html (0.12.3)
|
115
141
|
simplecov_json_formatter (0.1.4)
|
116
142
|
thor (1.3.0)
|
117
|
-
thor_nested_subcommand (1.0.
|
143
|
+
thor_nested_subcommand (1.0.5)
|
118
144
|
tzinfo (2.0.6)
|
119
145
|
concurrent-ruby (~> 1.0)
|
120
146
|
unicode-display_width (2.5.0)
|
147
|
+
zeitwerk (2.6.12)
|
121
148
|
|
122
149
|
PLATFORMS
|
123
150
|
x86_64-darwin-19
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# `dsu`- Streamline Your Daily Stand-Up Meeting Participation!
|
2
2
|
|
3
3
|
[![Ruby](https://github.com/gangelo/dsu/actions/workflows/ruby.yml/badge.svg)](https://github.com/gangelo/dsu/actions/workflows/ruby.yml)
|
4
|
-
[![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=
|
4
|
+
[![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=4)](https://badge.fury.io/gh/gangelo%2Fdsu)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=4)](https://badge.fury.io/rb/dsu)
|
6
6
|
[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/dsu/)
|
7
7
|
[![Report Issues](https://img.shields.io/badge/report-issues-red.svg)](https://github.com/gangelo/dsu/issues)
|
8
8
|
[![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
|
@@ -44,6 +44,7 @@ Commands:
|
|
44
44
|
dsu edit|e SUBCOMMAND # Edit DSU entries...
|
45
45
|
dsu export|x SUBCOMMAND # Export DSU entries...
|
46
46
|
dsu help [COMMAND] # Describe available...
|
47
|
+
dsu import|m SUBCOMMAND # Imports DSU entries...
|
47
48
|
dsu info|i # Displays information...
|
48
49
|
dsu list|l SUBCOMMAND # Displays DSU entries...
|
49
50
|
dsu theme|t SUBCOMMAND # Manage DSU themes...
|
@@ -413,6 +414,43 @@ The following command, when run on December 25, 2023, at 20:15:46...
|
|
413
414
|
|
414
415
|
For more information, see `dsu` help (`$ dsu export` or `dsu help export`) for more information.
|
415
416
|
|
417
|
+
## Importing DSU Entries
|
418
|
+
|
419
|
+
`dsu` provides a means to import entry group entry data from a previously exported `csv` file (see [Exporting DSU Entries](#exporting-dsu-entries)).
|
420
|
+
|
421
|
+
If you want to import a previously expoeted `csv` file, you can import `dsu` entries from a `csv` file by using any of the following commands:
|
422
|
+
|
423
|
+
- `$ dsu import all`
|
424
|
+
- `$ dsu m a` # Equivalent to the above, only using shortcuts
|
425
|
+
- `$ dsu import dates OPTIONS`
|
426
|
+
- `$ dsu m dd OPTIONS` # Equivalent to the above, only using shortcuts
|
427
|
+
|
428
|
+
**NOTE:** Each `import` command will prompt you to confirm the import. If confirmed, `dsu` will import the entry group entry data from the `csv` file into `dsu`.
|
429
|
+
|
430
|
+
### For example
|
431
|
+
|
432
|
+
### Importing all entries from a `csv` file
|
433
|
+
You can import _all_ entry group entries from a `csv` file.
|
434
|
+
|
435
|
+
The following command will import all the `dsu` entries from the given `csv` file, and merge the imported entries with any existing entry group entries you may have:
|
436
|
+
|
437
|
+
`$ dsu import all -i ~/Downloads/dsu-20231225201546-2023-01-01-thru-2024-01-01.csv`
|
438
|
+
|
439
|
+
The following command will import all the `dsu` entries from the given `csv` file, and **_overwrite_** all entry groups entries with the same entry group date using the `dsu export all` shortcut command:
|
440
|
+
|
441
|
+
`$ dsu m a -m false -i ~/Downloads/dsu-20231225201546-2023-01-01-thru-2024-01-01.csv`
|
442
|
+
|
443
|
+
### Importing specific entries from a `csv` file
|
444
|
+
You can import _specific_ entry group entries from a `csv` file for a date range.
|
445
|
+
|
446
|
+
The following command will import the `dsu` entries from the given `csv` file for the given date range, and merge the imported entries with any existing entry group entries you may have:
|
447
|
+
|
448
|
+
`$ dsu import dates --from 1/1/2023 --to 12/31/2023 -i ~/Downloads/dsu-20231225201546-2023-01-01-thru-2024-01-01.csv`
|
449
|
+
|
450
|
+
The following command will import the `dsu` entries from the given `csv` file for the given date range, and **_overwrite_** all entry groups entries with the same entry group date using the `dsu import dates` shortcut command:
|
451
|
+
|
452
|
+
`$ dsu m dd -m false -f 1/1/2023 -t 12/31/2023 -i ~/Downloads/dsu-20231225201546-2023-01-01-thru-2024-01-01.csv`
|
453
|
+
|
416
454
|
## Customizing the `dsu` Configuration File
|
417
455
|
To customize the `dsu` configuration file, you may follow the instructions outlined here. It is only recommended that you customize the `dsu` configuration file *only* if you are working with an official release (`n.n.n.n`).
|
418
456
|
|
data/lib/dsu/cli.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative 'subcommands/config'
|
|
8
8
|
require_relative 'subcommands/delete'
|
9
9
|
require_relative 'subcommands/edit'
|
10
10
|
require_relative 'subcommands/export'
|
11
|
+
require_relative 'subcommands/import'
|
11
12
|
require_relative 'subcommands/list'
|
12
13
|
require_relative 'subcommands/theme'
|
13
14
|
|
@@ -21,6 +22,7 @@ module Dsu
|
|
21
22
|
map I18n.t('commands.edit.key_mappings') => :edit
|
22
23
|
map I18n.t('commands.export.key_mappings') => :export
|
23
24
|
map I18n.t('commands.help.key_mappings') => :help
|
25
|
+
map I18n.t('commands.import.key_mappings') => :import
|
24
26
|
map I18n.t('commands.info.key_mappings') => :info
|
25
27
|
map I18n.t('commands.list.key_mappings') => :list
|
26
28
|
map I18n.t('commands.theme.key_mappings') => :theme
|
@@ -68,6 +70,9 @@ module Dsu
|
|
68
70
|
desc I18n.t('commands.theme.desc'), I18n.t('commands.theme.usage')
|
69
71
|
subcommand :theme, Subcommands::Theme
|
70
72
|
|
73
|
+
desc I18n.t('commands.import.desc'), I18n.t('commands.import.usage')
|
74
|
+
subcommand :import, Subcommands::Import
|
75
|
+
|
71
76
|
desc I18n.t('commands.info.desc'), I18n.t('commands.info.usage')
|
72
77
|
def info
|
73
78
|
configuration_version = Models::Configuration::VERSION
|
@@ -54,6 +54,9 @@ module Dsu
|
|
54
54
|
description: 'Default theme.'
|
55
55
|
}.merge(DEFAULT_THEME_COLORS).freeze
|
56
56
|
|
57
|
+
MIN_DESCRIPTION_LENGTH = 2
|
58
|
+
MAX_DESCRIPTION_LENGTH = 256
|
59
|
+
|
57
60
|
# TODO: Validate other attrs.
|
58
61
|
validates_with Validators::DescriptionValidator
|
59
62
|
validates_with Validators::ColorThemeValidator
|
data/lib/dsu/models/entry.rb
CHANGED
@@ -191,13 +191,19 @@ module Dsu
|
|
191
191
|
end
|
192
192
|
|
193
193
|
def write(file_data:, file_path:)
|
194
|
-
|
194
|
+
if file_data[:entries].empty?
|
195
|
+
superclass.delete(file_path: file_path)
|
196
|
+
return true
|
197
|
+
end
|
195
198
|
|
196
199
|
super
|
197
200
|
end
|
198
201
|
|
199
202
|
def write!(file_data:, file_path:)
|
200
|
-
|
203
|
+
if file_data[:entries].empty?
|
204
|
+
superclass.delete!(file_path: file_path)
|
205
|
+
return
|
206
|
+
end
|
201
207
|
|
202
208
|
super
|
203
209
|
end
|
@@ -5,6 +5,7 @@ require_relative '../../services/entry_group/exporter_service'
|
|
5
5
|
require_relative '../../support/ask'
|
6
6
|
require_relative '../base_presenter_ex'
|
7
7
|
require_relative 'messages'
|
8
|
+
require_relative 'nothing_to_export'
|
8
9
|
require_relative 'service_callable'
|
9
10
|
|
10
11
|
module Dsu
|
@@ -12,6 +13,7 @@ module Dsu
|
|
12
13
|
module Export
|
13
14
|
class AllPresenter < BasePresenterEx
|
14
15
|
include Messages
|
16
|
+
include NothingToExport
|
15
17
|
include ServiceCallable
|
16
18
|
include Support::Ask
|
17
19
|
|
@@ -25,7 +27,7 @@ module Dsu
|
|
25
27
|
end
|
26
28
|
|
27
29
|
def display_export_prompt
|
28
|
-
yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options))
|
30
|
+
yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
@@ -4,6 +4,7 @@ require_relative '../../models/entry_group'
|
|
4
4
|
require_relative '../../support/ask'
|
5
5
|
require_relative '../base_presenter_ex'
|
6
6
|
require_relative 'messages'
|
7
|
+
require_relative 'nothing_to_export'
|
7
8
|
require_relative 'service_callable'
|
8
9
|
|
9
10
|
module Dsu
|
@@ -11,6 +12,7 @@ module Dsu
|
|
11
12
|
module Export
|
12
13
|
class DatesPresenter < BasePresenterEx
|
13
14
|
include Messages
|
15
|
+
include NothingToExport
|
14
16
|
include ServiceCallable
|
15
17
|
include Support::Ask
|
16
18
|
|
@@ -31,7 +33,7 @@ module Dsu
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def display_export_prompt
|
34
|
-
yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options))
|
36
|
+
yes?(prompt_with_options(prompt: export_prompt, options: export_prompt_options), options: options)
|
35
37
|
end
|
36
38
|
|
37
39
|
private
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../../models/entry_group'
|
4
|
-
require_relative '../base_presenter_ex'
|
5
|
-
|
6
3
|
module Dsu
|
7
4
|
module Presenters
|
8
5
|
module Export
|
@@ -11,6 +8,10 @@ module Dsu
|
|
11
8
|
raise NotImplementedError
|
12
9
|
end
|
13
10
|
|
11
|
+
def display_nothing_to_export_message
|
12
|
+
puts apply_theme(I18n.t('subcommands.export.messages.nothing_to_export'), theme_color: color_theme.info)
|
13
|
+
end
|
14
|
+
|
14
15
|
private
|
15
16
|
|
16
17
|
def display_cancelled_message
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../models/entry_group'
|
4
|
+
require_relative '../../services/entry_group/importer_service'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../base_presenter_ex'
|
7
|
+
require_relative 'import_file'
|
8
|
+
require_relative 'messages'
|
9
|
+
require_relative 'service_callable'
|
10
|
+
|
11
|
+
module Dsu
|
12
|
+
module Presenters
|
13
|
+
module Import
|
14
|
+
class AllPresenter < BasePresenterEx
|
15
|
+
include ImportFile
|
16
|
+
include Messages
|
17
|
+
include ServiceCallable
|
18
|
+
include Support::Ask
|
19
|
+
|
20
|
+
def initialize(import_file_path:, options: {})
|
21
|
+
super(options: options)
|
22
|
+
|
23
|
+
@import_file_path = import_file_path
|
24
|
+
end
|
25
|
+
|
26
|
+
def render(response:)
|
27
|
+
return display_cancelled_message unless response
|
28
|
+
|
29
|
+
display_import_messages importer_service_call
|
30
|
+
end
|
31
|
+
|
32
|
+
def display_import_prompt
|
33
|
+
yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :import_file_path, :options
|
39
|
+
|
40
|
+
def import_entry_groups
|
41
|
+
@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
|
44
|
+
|
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']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
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')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../models/entry_group'
|
4
|
+
require_relative '../../services/entry_group/importer_service'
|
5
|
+
require_relative '../../support/ask'
|
6
|
+
require_relative '../base_presenter_ex'
|
7
|
+
require_relative 'import_file'
|
8
|
+
require_relative 'messages'
|
9
|
+
require_relative 'service_callable'
|
10
|
+
|
11
|
+
module Dsu
|
12
|
+
module Presenters
|
13
|
+
module Import
|
14
|
+
class DatesPresenter < BasePresenterEx
|
15
|
+
include ImportFile
|
16
|
+
include Messages
|
17
|
+
include ServiceCallable
|
18
|
+
include Support::Ask
|
19
|
+
|
20
|
+
def initialize(from:, to:, import_file_path:, options: {})
|
21
|
+
super(options: options)
|
22
|
+
|
23
|
+
@from = from.beginning_of_day
|
24
|
+
@to = to.end_of_day
|
25
|
+
@import_file_path = import_file_path
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(response:)
|
29
|
+
return display_cancelled_message unless response
|
30
|
+
|
31
|
+
display_import_messages importer_service_call
|
32
|
+
end
|
33
|
+
|
34
|
+
def display_import_prompt
|
35
|
+
yes?(prompt_with_options(prompt: import_prompt, options: import_prompt_options), options: options)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :from, :to, :import_file_path, :options
|
41
|
+
|
42
|
+
def import_entry_groups
|
43
|
+
@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
|
46
|
+
|
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)
|
49
|
+
|
50
|
+
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']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
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')
|
64
|
+
end
|
65
|
+
|
66
|
+
def middle_of_day_for(date_string)
|
67
|
+
Time.parse(date_string).in_time_zone.middle_of_day
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dsu
|
4
|
+
module Presenters
|
5
|
+
module Import
|
6
|
+
module ImportFile
|
7
|
+
def import_file_path_exist?
|
8
|
+
File.exist? import_file_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def nothing_to_import?
|
12
|
+
return true unless import_file_path_exist?
|
13
|
+
|
14
|
+
import_entry_groups.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def import_entry_groups
|
18
|
+
# Should return a Hash of entry group entries
|
19
|
+
# Example: { '2023-12-32' => ['Entry description 1', 'Entry description 2', ...] }
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dsu
|
4
|
+
module Presenters
|
5
|
+
module Import
|
6
|
+
module Messages
|
7
|
+
def display_import_prompt
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
|
11
|
+
def display_import_file_not_exist_message
|
12
|
+
puts apply_theme(I18n.t('subcommands.import.messages.file_not_exist',
|
13
|
+
file_path: import_file_path), theme_color: color_theme.info)
|
14
|
+
end
|
15
|
+
|
16
|
+
def display_nothing_to_import_message
|
17
|
+
puts apply_theme(I18n.t('subcommands.import.messages.nothing_to_import'), theme_color: color_theme.info)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def display_cancelled_message
|
23
|
+
puts apply_theme(I18n.t('subcommands.import.messages.cancelled'), theme_color: color_theme.info)
|
24
|
+
end
|
25
|
+
|
26
|
+
def display_import_messages(import_results)
|
27
|
+
import_results.each_pair do |entry_group_date, errors|
|
28
|
+
if errors.empty?
|
29
|
+
puts apply_theme(I18n.t('subcommands.import.messages.import_success',
|
30
|
+
date: entry_group_date), theme_color: color_theme.success)
|
31
|
+
else
|
32
|
+
errors.each do |error|
|
33
|
+
puts apply_theme(I18n.t('subcommands.import.messages.import_error',
|
34
|
+
date: entry_group_date, error: error), theme_color: color_theme.error)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../services/entry_group/importer_service'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Presenters
|
7
|
+
module Import
|
8
|
+
module ServiceCallable
|
9
|
+
private
|
10
|
+
|
11
|
+
def importer_service_call
|
12
|
+
@importer_service_call ||= begin
|
13
|
+
importer_service = Services::EntryGroup::ImporterService.new(import_entry_groups: import_entry_groups,
|
14
|
+
options: options)
|
15
|
+
importer_service.call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -11,51 +11,69 @@ module Dsu
|
|
11
11
|
include Support::Fileable
|
12
12
|
|
13
13
|
def initialize(entry_groups:, options: {})
|
14
|
-
raise ArgumentError, 'Argument entry_groups is
|
14
|
+
raise ArgumentError, 'Argument entry_groups is blank' if entry_groups.blank?
|
15
|
+
raise ArgumentError, 'Argument entry_groups are not all valid' unless entry_groups.all?(&:valid?)
|
16
|
+
|
17
|
+
validate_entry_group_entries_present! entry_groups
|
15
18
|
|
16
19
|
@entry_groups = entry_groups
|
17
20
|
@options = options
|
18
21
|
end
|
19
22
|
|
20
23
|
def call
|
21
|
-
CSV.open(
|
24
|
+
CSV.open(export_file_path, 'w') do |csv|
|
22
25
|
csv << %i[version entry_group entry_no total_entries entry_group_entry]
|
23
26
|
|
24
27
|
entry_groups.each do |entry_group|
|
25
|
-
|
26
|
-
|
27
|
-
entry_group.entries.each_with_index do |entry, index|
|
28
|
-
csv << [
|
29
|
-
entry_group.version,
|
30
|
-
entry_group.time.to_date,
|
31
|
-
index + 1,
|
32
|
-
entry_group.entries.count,
|
33
|
-
entry.description
|
34
|
-
]
|
35
|
-
end
|
28
|
+
export_entry_group(entry_group: entry_group, csv: csv)
|
36
29
|
end
|
37
30
|
end
|
38
31
|
|
39
|
-
|
32
|
+
export_file_path
|
40
33
|
end
|
41
34
|
|
42
|
-
def
|
43
|
-
@
|
44
|
-
file_name = "dsu-#{export_timestamp}-#{times.min.to_date}-thru-#{times.max.to_date}.csv"
|
45
|
-
File.join(temp_folder, file_name)
|
46
|
-
end
|
35
|
+
def export_file_path
|
36
|
+
@export_file_path ||= File.join(temp_folder, export_file_name)
|
47
37
|
end
|
48
38
|
|
49
39
|
private
|
50
40
|
|
51
41
|
attr_reader :entry_groups, :options
|
52
42
|
|
43
|
+
def export_entry_data(entry_group:, entry:, entry_index:)
|
44
|
+
[
|
45
|
+
entry_group.version,
|
46
|
+
entry_group.time.to_date,
|
47
|
+
entry_index + 1,
|
48
|
+
entry_group.entries.count,
|
49
|
+
entry.description
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
def export_entry_group(entry_group:, csv:)
|
54
|
+
entry_group.entries.each_with_index do |entry, index|
|
55
|
+
csv << export_entry_data(entry_group: entry_group, entry: entry, entry_index: index)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def export_file_name
|
60
|
+
"dsu-#{timestamp}-#{times.min.to_date}-thru-#{times.max.to_date}.csv"
|
61
|
+
end
|
62
|
+
|
53
63
|
def times
|
54
64
|
@times ||= entry_groups.map(&:time)
|
55
65
|
end
|
56
66
|
|
57
|
-
def
|
58
|
-
Time.now.in_time_zone.strftime('%Y%m%d%H%M%S')
|
67
|
+
def timestamp
|
68
|
+
@timestamp ||= Time.now.in_time_zone.strftime('%Y%m%d%H%M%S')
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_entry_group_entries_present!(entry_groups)
|
72
|
+
entry_groups.each do |entry_group|
|
73
|
+
next if entry_group.entries.present?
|
74
|
+
|
75
|
+
raise ArgumentError, "Argument entry_groups entry group for #{entry_group.time_yyyy_mm_dd} has no entries"
|
76
|
+
end
|
59
77
|
end
|
60
78
|
end
|
61
79
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
require_relative '../../models/entry_group'
|
5
|
+
|
6
|
+
module Dsu
|
7
|
+
module Services
|
8
|
+
module EntryGroup
|
9
|
+
# Expects a hash having the following format:
|
10
|
+
# {
|
11
|
+
# "2023-12-29" => ["Entry 1 description", "Entry 2 description", ...],
|
12
|
+
# "2023-12-30" => ["Entry 1 description", ...],
|
13
|
+
# "2023-12-31" => ["Entry 1 description", ...]
|
14
|
+
# }
|
15
|
+
class ImporterService
|
16
|
+
include Support::Fileable
|
17
|
+
|
18
|
+
def initialize(import_entry_groups:, options: {})
|
19
|
+
raise ArgumentError, 'Argument import_entry_groups is blank' if import_entry_groups.blank?
|
20
|
+
|
21
|
+
@import_entry_groups = import_entry_groups
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def call
|
26
|
+
import!
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :import_entry_groups, :options
|
32
|
+
|
33
|
+
def import!
|
34
|
+
import_entry_groups.each_pair do |entry_group_date, entry_descriptions|
|
35
|
+
entry_group_for(entry_group_date).tap do |entry_group|
|
36
|
+
entry_descriptions.each do |entry_description|
|
37
|
+
add_entry_group_entry_if(entry_group: entry_group, entry_description: entry_description)
|
38
|
+
end
|
39
|
+
|
40
|
+
import_messages[entry_group.time_yyyy_mm_dd] = []
|
41
|
+
|
42
|
+
unless entry_group.save
|
43
|
+
entry_group.errors.full_messages.each do |error|
|
44
|
+
import_messages[entry_group.time_yyyy_mm_dd] << error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
import_messages
|
51
|
+
end
|
52
|
+
|
53
|
+
def entry_group_for(entry_group_date)
|
54
|
+
time = Time.parse(entry_group_date).in_time_zone
|
55
|
+
if merge?
|
56
|
+
Models::EntryGroup.find_or_initialize(time: time)
|
57
|
+
else
|
58
|
+
Models::EntryGroup.new(time: time, options: options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_entry_group_entry_if(entry_group:, entry_description:)
|
63
|
+
entry = Models::Entry.new(description: entry_description)
|
64
|
+
return entry_group.entries << entry if replace?
|
65
|
+
return if entry_group.entries.include?(entry)
|
66
|
+
|
67
|
+
entry_group.entries << entry
|
68
|
+
end
|
69
|
+
|
70
|
+
def merge?
|
71
|
+
options.fetch(:merge, true)
|
72
|
+
end
|
73
|
+
|
74
|
+
def replace?
|
75
|
+
!merge?
|
76
|
+
end
|
77
|
+
|
78
|
+
def import_messages
|
79
|
+
@import_messages ||= {}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../presenters/export/all_presenter'
|
4
|
+
require_relative '../presenters/export/dates_presenter'
|
3
5
|
require_relative '../support/command_options/dsu_times'
|
4
6
|
require_relative '../support/command_options/time_mnemonic'
|
5
7
|
require_relative '../support/time_formatable'
|
@@ -39,7 +41,6 @@ module Dsu
|
|
39
41
|
return
|
40
42
|
end
|
41
43
|
|
42
|
-
times = times_sort(times: times, entries_display_order: options[:entries_display_order])
|
43
44
|
Views::Export.new(presenter: dates_presenter_for(from: times.min, to: times.max, options: options)).render
|
44
45
|
rescue ArgumentError => e
|
45
46
|
Views::Shared::Error.new(messages: e.message).render
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../presenters/import/all_presenter'
|
4
|
+
require_relative '../presenters/import/dates_presenter'
|
5
|
+
require_relative '../support/command_options/dsu_times'
|
6
|
+
require_relative '../support/command_options/time_mnemonic'
|
7
|
+
require_relative '../support/time_formatable'
|
8
|
+
require_relative '../views/import'
|
9
|
+
require_relative '../views/shared/error'
|
10
|
+
require_relative 'base_subcommand'
|
11
|
+
|
12
|
+
module Dsu
|
13
|
+
module Subcommands
|
14
|
+
class Import < BaseSubcommand
|
15
|
+
include Support::CommandOptions::TimeMnemonic
|
16
|
+
include Support::TimeFormatable
|
17
|
+
|
18
|
+
# TODO: I18n.
|
19
|
+
map %w[a] => :all
|
20
|
+
map %w[dd] => :dates
|
21
|
+
|
22
|
+
desc I18n.t('subcommands.import.all.desc'), I18n.t('subcommands.import.all.usage')
|
23
|
+
long_desc I18n.t('subcommands.import.all.long_desc')
|
24
|
+
option :import_file, type: :string, required: true, aliases: '-i', banner: 'IMPORT_CVS_FILE'
|
25
|
+
option :merge, type: :boolean, default: true, aliases: '-m'
|
26
|
+
option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
|
27
|
+
def all
|
28
|
+
Views::Import.new(presenter: all_presenter(import_file_path: options[:import_file],
|
29
|
+
options: options)).render
|
30
|
+
end
|
31
|
+
|
32
|
+
desc I18n.t('subcommands.import.dates.desc'), I18n.t('subcommands.import.dates.usage')
|
33
|
+
long_desc I18n.t('subcommands.import.dates.long_desc',
|
34
|
+
date_option_description: date_option_description,
|
35
|
+
mnemonic_option_description: mnemonic_option_description)
|
36
|
+
option :from, type: :string, required: true, aliases: '-f', banner: 'DATE|MNEMONIC'
|
37
|
+
option :to, type: :string, required: true, aliases: '-t', banner: 'DATE|MNEMONIC'
|
38
|
+
option :import_file, type: :string, required: true, aliases: '-i', banner: 'IMPORT_CVS_FILE'
|
39
|
+
option :merge, type: :boolean, default: true, aliases: '-m'
|
40
|
+
option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
|
41
|
+
def dates
|
42
|
+
options = configuration.to_h.merge(self.options).with_indifferent_access
|
43
|
+
times, errors = Support::CommandOptions::DsuTimes.dsu_times_for(from_option: options[:from], to_option: options[:to]) # rubocop:disable Layout/LineLength
|
44
|
+
if errors.any?
|
45
|
+
Views::Shared::Error.new(messages: errors).render
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
Views::Import.new(presenter: dates_presenter_for(from: times.min,
|
50
|
+
to: times.max,
|
51
|
+
import_file_path: options[:import_file],
|
52
|
+
options: options)).render
|
53
|
+
rescue ArgumentError => e
|
54
|
+
Views::Shared::Error.new(messages: e.message).render
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def all_presenter(import_file_path:, options:)
|
60
|
+
Presenters::Import::AllPresenter.new(import_file_path: import_file_path, options: options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def dates_presenter_for(from:, to:, import_file_path:, options:)
|
64
|
+
Presenters::Import::DatesPresenter.new(from: from, to: to, import_file_path: import_file_path, options: options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/dsu/support/ask.rb
CHANGED
@@ -13,7 +13,10 @@ module Dsu
|
|
13
13
|
def yes?(prompt, options: {})
|
14
14
|
auto_prompt = auto_prompt(prompt, options)
|
15
15
|
|
16
|
-
|
16
|
+
unless auto_prompt.nil?
|
17
|
+
puts prompt
|
18
|
+
return auto_prompt
|
19
|
+
end
|
17
20
|
|
18
21
|
Thor::Base.shell.new.yes?(prompt)
|
19
22
|
end
|
@@ -21,9 +24,10 @@ module Dsu
|
|
21
24
|
private
|
22
25
|
|
23
26
|
def auto_prompt(prompt, options)
|
27
|
+
options = options.with_indifferent_access
|
24
28
|
prompt = Utils.strip_escapes(prompt)
|
25
29
|
@auto_prompt ||= begin
|
26
|
-
value = options.dig(
|
30
|
+
value = options.dig(:prompts, prompt) || options.dig(:prompts, :any)
|
27
31
|
value = (value == 'true' unless value.nil?)
|
28
32
|
value
|
29
33
|
end
|
@@ -21,7 +21,7 @@ module Dsu
|
|
21
21
|
errors << I18n.t('errors.to_option_invalid', to_option: to_option) if to_time.nil?
|
22
22
|
return [[], errors] if errors.any?
|
23
23
|
|
24
|
-
min_time, max_time = [from_time, to_time].
|
24
|
+
min_time, max_time = [from_time, to_time].minmax
|
25
25
|
[(min_time.to_date..max_time.to_date).map(&:to_time), []]
|
26
26
|
end
|
27
27
|
|
@@ -12,6 +12,7 @@ module Dsu
|
|
12
12
|
end
|
13
13
|
|
14
14
|
unless description.is_a?(String)
|
15
|
+
# TODO: I18n.
|
15
16
|
record.errors.add(:description, 'is the wrong object type. ' \
|
16
17
|
"\"String\" was expected, but \"#{description.class}\" was received.")
|
17
18
|
return
|
@@ -25,16 +26,26 @@ module Dsu
|
|
25
26
|
def validate_description(record)
|
26
27
|
description = record.description
|
27
28
|
|
28
|
-
return if description.length.between?(
|
29
|
+
return if description.length.between?(min_description_length(record), max_description_length(record))
|
29
30
|
|
30
|
-
if description.length <
|
31
|
+
if description.length < min_description_length(record)
|
31
32
|
# TODO: I18n.
|
32
|
-
record.errors.add(:description, "is too short: \"#{record.short_description}\"
|
33
|
-
|
33
|
+
record.errors.add(:description, "is too short: \"#{record.short_description}\" " \
|
34
|
+
"(minimum is #{min_description_length(record)} characters).")
|
35
|
+
elsif description.length > max_description_length(record)
|
34
36
|
# TODO: I18n.
|
35
|
-
record.errors.add(:description, "is too long: \"#{record.short_description}\"
|
37
|
+
record.errors.add(:description, "is too long: \"#{record.short_description}\" " \
|
38
|
+
"(maximum is #{max_description_length(record)} characters).")
|
36
39
|
end
|
37
40
|
end
|
41
|
+
|
42
|
+
def min_description_length(record)
|
43
|
+
record.class::MIN_DESCRIPTION_LENGTH
|
44
|
+
end
|
45
|
+
|
46
|
+
def max_description_length(record)
|
47
|
+
record.class::MAX_DESCRIPTION_LENGTH
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|
data/lib/dsu/version.rb
CHANGED
data/lib/dsu/views/export.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../models/color_theme'
|
4
|
+
require_relative '../models/configuration'
|
5
|
+
require_relative '../support/color_themable'
|
6
|
+
|
7
|
+
module Dsu
|
8
|
+
module Views
|
9
|
+
class Import
|
10
|
+
include Support::ColorThemable
|
11
|
+
|
12
|
+
def initialize(presenter:)
|
13
|
+
@presenter = presenter
|
14
|
+
end
|
15
|
+
|
16
|
+
def render
|
17
|
+
return presenter.display_import_file_not_exist_message unless presenter.import_file_path_exist?
|
18
|
+
return presenter.display_nothing_to_import_message if presenter.nothing_to_import?
|
19
|
+
|
20
|
+
response = presenter.display_import_prompt
|
21
|
+
presenter.render response: response
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :presenter
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/locales/en/commands.yml
CHANGED
@@ -69,6 +69,10 @@ en:
|
|
69
69
|
key_mappings: x
|
70
70
|
desc: export|x SUBCOMMAND
|
71
71
|
usage: Export DSU entries for the given SUBCOMMAND
|
72
|
+
import:
|
73
|
+
key_mappings: m
|
74
|
+
desc: import|m SUBCOMMAND
|
75
|
+
usage: Imports DSU entries for the given SUBCOMMAND
|
72
76
|
info:
|
73
77
|
key_mappings: i
|
74
78
|
desc: info|i
|
@@ -280,6 +280,7 @@ en:
|
|
280
280
|
messages:
|
281
281
|
exported: Export successful.
|
282
282
|
exported_to: Entry groups exported to %{file_path}.
|
283
|
+
nothing_to_export: No entry groups to export.
|
283
284
|
cancelled: Cancelled.
|
284
285
|
prompts:
|
285
286
|
export_all_confirm: Export all the entries (%{count} entry groups)?
|
@@ -287,6 +288,98 @@ en:
|
|
287
288
|
options:
|
288
289
|
- y
|
289
290
|
- N
|
291
|
+
import:
|
292
|
+
dates:
|
293
|
+
desc: dates|dd OPTIONS
|
294
|
+
usage: Imports the DSU entries given the OPTIONS provided
|
295
|
+
long_desc: |
|
296
|
+
Imports the DSU entries for the given OPTIONS provided.
|
297
|
+
|
298
|
+
$ dsu import dates OPTIONS
|
299
|
+
|
300
|
+
$ dsu m dd OPTIONS
|
301
|
+
|
302
|
+
OPTIONS:
|
303
|
+
|
304
|
+
-i|--import-file IMPORT_CVS_FILE: The IMPORT_CVS_FILE file to import. IMPORT_CVS_FILE should be a fully qualified path to a file that was previously created as a result of running `dsu export`. see `dsu help export`.
|
305
|
+
|
306
|
+
-m|--merge true|false (default: true): If true, imported entries will be added to the entry group if the entry group already exists. If false, the imported entries will replace all existing entries for the entry group if the entry group already exists. If the entry group does not exist, it will be created using the imported entries.
|
307
|
+
|
308
|
+
-f|--from DATE|MNEMONIC: The DATE or MNEMONIC that represents the start of the range of DSU dates to import. If a relative mnemonic is used (+/-n, e.g +1, -1, etc.), the date calculated will be relative to the current date (e.g. `<MNEMONIC>.to_i.days.from_now(Time.now)`).
|
309
|
+
|
310
|
+
-t|--to DATE|MNEMONIC: The DATE or MNEMONIC that represents the end of the range of DSU dates to import. If a relative mnemonic is used (+/-n, e.g +1, -1, etc.), the date calculated will be relative to the date that resulting from the `--from` option date calculation.
|
311
|
+
|
312
|
+
%{date_option_description}
|
313
|
+
|
314
|
+
%{mnemonic_option_description}
|
315
|
+
|
316
|
+
EXAMPLES:
|
317
|
+
|
318
|
+
NOTE: All examples can substitute their respective short form options (e.g. `-f`, `-t`, etc. for `--from`, `--to`, etc.).
|
319
|
+
|
320
|
+
The below will import the DSU entries for the range of dates from 1/1 to 1/4 for the current year, from the import file, and replace all the entries for the respective entry groups imported:
|
321
|
+
|
322
|
+
$ dsu import dates --from 1/1 --to +3 -i /path/to/import.csv -m false
|
323
|
+
|
324
|
+
This will import the DSU entries for the range of dates from 1/2 to 1/5 for the year 2022, from the import file, and merge all the entries for the respective entry groups imported:
|
325
|
+
|
326
|
+
$ dsu m dd --from 1/5/2022 --to -3 -i /path/to/import.csv
|
327
|
+
|
328
|
+
This (assuming "today" is 1/10) will import the DSU entries for the last week 1/10 to 1/3 of the current year, from the import file, and merge all the entries for the respective entry groups imported:
|
329
|
+
|
330
|
+
$ dsu import dates --from today --to -7 -i /path/to/import.csv -m true
|
331
|
+
|
332
|
+
This (assuming "today" is 5/23) will import the DSU entries for the last week 5/16 to 5/22.
|
333
|
+
This example simply illustrates the fact that you can use relative mnemonics for
|
334
|
+
both `--from` and `--to` options; this doesn't mean you should do so...
|
335
|
+
|
336
|
+
While you can use relative mnemonics for both `--from` and `--to` options,
|
337
|
+
there is always a more intuitive way. The below example basically imports one week of DSU entries back 1 week from yesterday's date, from the import file, and merge all the entries for the respective entry groups imported:
|
338
|
+
|
339
|
+
$ dsu import dates --from -7 --to +6 -i /path/to/import.csv
|
340
|
+
|
341
|
+
The above can be accomplished MUCH easier by simply using the `yesterday` mnemonic...
|
342
|
+
|
343
|
+
This (assuming "today" is 5/23) will import the DSU entries back 1 week from yesterday's date 5/16 to 5/22, from the import file, and merge all the entries for the respective entry groups imported:
|
344
|
+
|
345
|
+
$ dsu m dd --from yesterday --to -6 -i /path/to/import.csv
|
346
|
+
all:
|
347
|
+
desc: all|a OPTIONS
|
348
|
+
usage: Imports all DSU entries from a given DSU export .csv file
|
349
|
+
long_desc: |
|
350
|
+
Imports all DSU entries from a given DSU export .csv file.
|
351
|
+
|
352
|
+
$ dsu import all OPTIONS
|
353
|
+
|
354
|
+
$ dsu m a OPTIONS
|
355
|
+
|
356
|
+
OPTIONS:
|
357
|
+
|
358
|
+
-i|--import-file IMPORT_CVS_FILE: The IMPORT_CVS_FILE file to import. IMPORT_CVS_FILE should be a fully qualified path to a file that was previously created as a result of running `dsu export`. see `dsu help export`.
|
359
|
+
|
360
|
+
-m|--merge true|false (default: true): If true, imported entries will be added to the entry group if the entry group already exists. If false, the imported entries will replace all existing entries for the entry group if the entry group already exists. If the entry group does not exist, it will be created using the imported entries.
|
361
|
+
|
362
|
+
EXAMPLES:
|
363
|
+
|
364
|
+
This will import all the DSU entries from the import file, and replace all the entries for the respective entry groups imported:
|
365
|
+
|
366
|
+
$ dsu import all -i /path/to/import.csv -m false
|
367
|
+
|
368
|
+
This will import all the DSU entries from the import file, and merge all the entries for the respective entry groups imported:
|
369
|
+
|
370
|
+
$ dsu import all -i /path/to/import.csv
|
371
|
+
messages:
|
372
|
+
import_success: Entry group for %{date} imported successfully.
|
373
|
+
import_error: "Entry group for %{date} imported with an error: %{error}."
|
374
|
+
nothing_to_import: No entry groups to import.
|
375
|
+
cancelled: Cancelled.
|
376
|
+
file_not_exist: Import file %{file_path} does not exist.
|
377
|
+
prompts:
|
378
|
+
import_all_confirm: Import all entry groups (%{count} entry groups)?
|
379
|
+
import_dates_confirm: Import all the entry groups for %{from} thru %{to} (%{count} entry groups)?
|
380
|
+
options:
|
381
|
+
- y
|
382
|
+
- N
|
290
383
|
list:
|
291
384
|
date:
|
292
385
|
desc: date|d DATE|MNEMONIC
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dsu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gene M. Angelo, Jr.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -188,7 +188,13 @@ files:
|
|
188
188
|
- lib/dsu/presenters/export/all_presenter.rb
|
189
189
|
- lib/dsu/presenters/export/dates_presenter.rb
|
190
190
|
- lib/dsu/presenters/export/messages.rb
|
191
|
+
- lib/dsu/presenters/export/nothing_to_export.rb
|
191
192
|
- lib/dsu/presenters/export/service_callable.rb
|
193
|
+
- lib/dsu/presenters/import/all_presenter.rb
|
194
|
+
- lib/dsu/presenters/import/dates_presenter.rb
|
195
|
+
- lib/dsu/presenters/import/import_file.rb
|
196
|
+
- lib/dsu/presenters/import/messages.rb
|
197
|
+
- lib/dsu/presenters/import/service_callable.rb
|
192
198
|
- lib/dsu/services/color_theme/hydrator_service.rb
|
193
199
|
- lib/dsu/services/configuration/hydrator_service.rb
|
194
200
|
- lib/dsu/services/entry/hydrator_service.rb
|
@@ -198,6 +204,7 @@ files:
|
|
198
204
|
- lib/dsu/services/entry_group/editor_service.rb
|
199
205
|
- lib/dsu/services/entry_group/exporter_service.rb
|
200
206
|
- lib/dsu/services/entry_group/hydrator_service.rb
|
207
|
+
- lib/dsu/services/entry_group/importer_service.rb
|
201
208
|
- lib/dsu/services/migration_version/hydrator_service.rb
|
202
209
|
- lib/dsu/services/stderr_redirector_service.rb
|
203
210
|
- lib/dsu/services/stdout_redirector_service.rb
|
@@ -209,6 +216,7 @@ files:
|
|
209
216
|
- lib/dsu/subcommands/delete.rb
|
210
217
|
- lib/dsu/subcommands/edit.rb
|
211
218
|
- lib/dsu/subcommands/export.rb
|
219
|
+
- lib/dsu/subcommands/import.rb
|
212
220
|
- lib/dsu/subcommands/list.rb
|
213
221
|
- lib/dsu/subcommands/theme.rb
|
214
222
|
- lib/dsu/support/ask.rb
|
@@ -245,6 +253,7 @@ files:
|
|
245
253
|
- lib/dsu/views/entry_group/shared/no_entries_to_display_for_year_of.rb
|
246
254
|
- lib/dsu/views/entry_group/show.rb
|
247
255
|
- lib/dsu/views/export.rb
|
256
|
+
- lib/dsu/views/import.rb
|
248
257
|
- lib/dsu/views/shared/error.rb
|
249
258
|
- lib/dsu/views/shared/info.rb
|
250
259
|
- lib/dsu/views/shared/message.rb
|
@@ -281,28 +290,25 @@ post_install_message: |
|
|
281
290
|
View the dsu README.md here: https://github.com/gangelo/dsu
|
282
291
|
View the dsu CHANGELOG.md: https://github.com/gangelo/dsu/blob/main/CHANGELOG.md
|
283
292
|
|
293
|
+
Dsu now has a import command! Try it out by running `dsu import help`.
|
284
294
|
Dsu now has a export command! Try it out by running `dsu export help`.
|
285
295
|
Dsu now has a browse command! Try it out by running `dsu browse help`.
|
286
|
-
|
287
|
-
Dsu now has a festive "christmas" theme! Try it out by running `dsu theme use christmas`.
|
288
296
|
Dsu now has a "light" theme for light background terminals! Try it out by running `dsu theme use light`.
|
289
|
-
|
290
297
|
Dsu now has a delete command! Try it out by running `dsu delete help`.
|
291
298
|
|
292
299
|
Try a dsu theme by running `dsu theme list` and then `dsu theme use THEME_NAME` where THEME_NAME is the name of the theme you want to try.
|
293
300
|
|
294
301
|
:)
|
295
302
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|||
|
303
|
+
Happy New Year 2024 from dsu!
|
304
|
+
|
305
|
+
* * *
|
306
|
+
* * . * . *
|
307
|
+
* * * * * *
|
308
|
+
* * * * *
|
309
|
+
* * *
|
310
|
+
|
311
|
+
May your year be filled with sparks of joy and innovation!
|
306
312
|
rdoc_options: []
|
307
313
|
require_paths:
|
308
314
|
- lib
|