reading 0.6.0 → 0.7.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/bin/reading +8 -8
  3. data/bin/readingfile +31 -0
  4. data/lib/reading/config.rb +115 -148
  5. data/lib/reading/errors.rb +11 -64
  6. data/lib/reading/item/time_length.rb +138 -0
  7. data/lib/reading/parsing/attributes/attribute.rb +26 -0
  8. data/lib/reading/parsing/attributes/author.rb +15 -0
  9. data/lib/reading/parsing/attributes/experiences/dates_and_head_transformer.rb +106 -0
  10. data/lib/reading/parsing/attributes/experiences/history_transformer.rb +452 -0
  11. data/lib/reading/parsing/attributes/experiences/spans_validator.rb +149 -0
  12. data/lib/reading/parsing/attributes/experiences.rb +27 -0
  13. data/lib/reading/parsing/attributes/genres.rb +16 -0
  14. data/lib/reading/parsing/attributes/notes.rb +22 -0
  15. data/lib/reading/parsing/attributes/rating.rb +17 -0
  16. data/lib/reading/parsing/attributes/shared.rb +62 -0
  17. data/lib/reading/parsing/attributes/title.rb +21 -0
  18. data/lib/reading/parsing/attributes/variants.rb +77 -0
  19. data/lib/reading/parsing/csv.rb +101 -0
  20. data/lib/reading/parsing/parser.rb +292 -0
  21. data/lib/reading/parsing/rows/column.rb +131 -0
  22. data/lib/reading/parsing/rows/comment.rb +26 -0
  23. data/lib/reading/parsing/rows/compact_planned.rb +30 -0
  24. data/lib/reading/parsing/rows/compact_planned_columns/head.rb +60 -0
  25. data/lib/reading/parsing/rows/regular.rb +33 -0
  26. data/lib/reading/parsing/rows/regular_columns/end_dates.rb +20 -0
  27. data/lib/reading/parsing/rows/regular_columns/genres.rb +20 -0
  28. data/lib/reading/parsing/rows/regular_columns/head.rb +45 -0
  29. data/lib/reading/parsing/rows/regular_columns/history.rb +143 -0
  30. data/lib/reading/parsing/rows/regular_columns/length.rb +35 -0
  31. data/lib/reading/parsing/rows/regular_columns/notes.rb +32 -0
  32. data/lib/reading/parsing/rows/regular_columns/rating.rb +15 -0
  33. data/lib/reading/parsing/rows/regular_columns/sources.rb +94 -0
  34. data/lib/reading/parsing/rows/regular_columns/start_dates.rb +35 -0
  35. data/lib/reading/parsing/transformer.rb +70 -0
  36. data/lib/reading/util/hash_compact_by_template.rb +1 -0
  37. data/lib/reading/util/hash_deep_merge.rb +1 -1
  38. data/lib/reading/util/hash_to_struct.rb +1 -0
  39. data/lib/reading/util/numeric_to_i_if_whole.rb +12 -0
  40. data/lib/reading/util/string_truncate.rb +13 -4
  41. data/lib/reading/version.rb +1 -1
  42. data/lib/reading.rb +18 -0
  43. metadata +58 -41
  44. data/lib/reading/attribute/all_attributes.rb +0 -83
  45. data/lib/reading/attribute/attribute.rb +0 -25
  46. data/lib/reading/attribute/experiences/dates_validator.rb +0 -94
  47. data/lib/reading/attribute/experiences/experiences_attribute.rb +0 -74
  48. data/lib/reading/attribute/experiences/progress_subattribute.rb +0 -48
  49. data/lib/reading/attribute/experiences/spans_subattribute.rb +0 -82
  50. data/lib/reading/attribute/variants/extra_info_subattribute.rb +0 -44
  51. data/lib/reading/attribute/variants/length_subattribute.rb +0 -45
  52. data/lib/reading/attribute/variants/series_subattribute.rb +0 -57
  53. data/lib/reading/attribute/variants/sources_subattribute.rb +0 -78
  54. data/lib/reading/attribute/variants/variants_attribute.rb +0 -69
  55. data/lib/reading/csv.rb +0 -67
  56. data/lib/reading/line.rb +0 -23
  57. data/lib/reading/row/blank_row.rb +0 -23
  58. data/lib/reading/row/compact_planned_row.rb +0 -130
  59. data/lib/reading/row/regular_row.rb +0 -94
  60. data/lib/reading/row/row.rb +0 -88
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reading
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Vogel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-27 00:00:00.000000000 Z
11
+ date: 2023-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pastel
@@ -28,119 +28,136 @@ dependencies:
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.0
33
+ version: '1.7'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.0
40
+ version: '1.7'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '5.18'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '5.0'
54
+ version: '5.18'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest-reporters
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.0'
61
+ version: '1.6'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.0'
68
+ version: '1.6'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pretty-diffs
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '1.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '1.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: amazing_print
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '1.4'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '1.4'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubycritic
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '4.7'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '4.7'
111
111
  description:
112
112
  email:
113
113
  - fps.vogel@gmail.com
114
114
  executables:
115
115
  - reading
116
+ - readingfile
116
117
  extensions: []
117
118
  extra_rdoc_files: []
118
119
  files:
119
120
  - bin/reading
120
- - lib/reading/attribute/all_attributes.rb
121
- - lib/reading/attribute/attribute.rb
122
- - lib/reading/attribute/experiences/dates_validator.rb
123
- - lib/reading/attribute/experiences/experiences_attribute.rb
124
- - lib/reading/attribute/experiences/progress_subattribute.rb
125
- - lib/reading/attribute/experiences/spans_subattribute.rb
126
- - lib/reading/attribute/variants/extra_info_subattribute.rb
127
- - lib/reading/attribute/variants/length_subattribute.rb
128
- - lib/reading/attribute/variants/series_subattribute.rb
129
- - lib/reading/attribute/variants/sources_subattribute.rb
130
- - lib/reading/attribute/variants/variants_attribute.rb
121
+ - bin/readingfile
122
+ - lib/reading.rb
131
123
  - lib/reading/config.rb
132
- - lib/reading/csv.rb
133
124
  - lib/reading/errors.rb
134
- - lib/reading/line.rb
135
- - lib/reading/row/blank_row.rb
136
- - lib/reading/row/compact_planned_row.rb
137
- - lib/reading/row/regular_row.rb
138
- - lib/reading/row/row.rb
125
+ - lib/reading/item/time_length.rb
126
+ - lib/reading/parsing/attributes/attribute.rb
127
+ - lib/reading/parsing/attributes/author.rb
128
+ - lib/reading/parsing/attributes/experiences.rb
129
+ - lib/reading/parsing/attributes/experiences/dates_and_head_transformer.rb
130
+ - lib/reading/parsing/attributes/experiences/history_transformer.rb
131
+ - lib/reading/parsing/attributes/experiences/spans_validator.rb
132
+ - lib/reading/parsing/attributes/genres.rb
133
+ - lib/reading/parsing/attributes/notes.rb
134
+ - lib/reading/parsing/attributes/rating.rb
135
+ - lib/reading/parsing/attributes/shared.rb
136
+ - lib/reading/parsing/attributes/title.rb
137
+ - lib/reading/parsing/attributes/variants.rb
138
+ - lib/reading/parsing/csv.rb
139
+ - lib/reading/parsing/parser.rb
140
+ - lib/reading/parsing/rows/column.rb
141
+ - lib/reading/parsing/rows/comment.rb
142
+ - lib/reading/parsing/rows/compact_planned.rb
143
+ - lib/reading/parsing/rows/compact_planned_columns/head.rb
144
+ - lib/reading/parsing/rows/regular.rb
145
+ - lib/reading/parsing/rows/regular_columns/end_dates.rb
146
+ - lib/reading/parsing/rows/regular_columns/genres.rb
147
+ - lib/reading/parsing/rows/regular_columns/head.rb
148
+ - lib/reading/parsing/rows/regular_columns/history.rb
149
+ - lib/reading/parsing/rows/regular_columns/length.rb
150
+ - lib/reading/parsing/rows/regular_columns/notes.rb
151
+ - lib/reading/parsing/rows/regular_columns/rating.rb
152
+ - lib/reading/parsing/rows/regular_columns/sources.rb
153
+ - lib/reading/parsing/rows/regular_columns/start_dates.rb
154
+ - lib/reading/parsing/transformer.rb
139
155
  - lib/reading/util/blank.rb
140
156
  - lib/reading/util/hash_array_deep_fetch.rb
141
157
  - lib/reading/util/hash_compact_by_template.rb
142
158
  - lib/reading/util/hash_deep_merge.rb
143
159
  - lib/reading/util/hash_to_struct.rb
160
+ - lib/reading/util/numeric_to_i_if_whole.rb
144
161
  - lib/reading/util/string_remove.rb
145
162
  - lib/reading/util/string_truncate.rb
146
163
  - lib/reading/version.rb
@@ -167,8 +184,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
184
  - !ruby/object:Gem::Version
168
185
  version: '0'
169
186
  requirements: []
170
- rubygems_version: 3.4.1
187
+ rubygems_version: 3.4.9
171
188
  signing_key:
172
189
  specification_version: 4
173
- summary: reading parses a CSV reading log.
190
+ summary: Parses a CSV reading log.
174
191
  test_files: []
@@ -1,83 +0,0 @@
1
- require_relative "attribute"
2
- require_relative "variants/variants_attribute"
3
- require_relative "experiences/experiences_attribute"
4
-
5
- module Reading
6
- class Row
7
- using Util::StringRemove
8
- using Util::HashArrayDeepFetch
9
-
10
- # The simpler attributes are collected below. The more complex attributes
11
- # are separated into their own files.
12
-
13
- class RatingAttribute < Attribute
14
- def parse
15
- return nil unless columns[:rating]
16
-
17
- rating = columns[:rating].strip
18
- return nil if rating.empty?
19
-
20
- Integer(rating, exception: false) ||
21
- Float(rating, exception: false) ||
22
- (raise InvalidRatingError, "Invalid rating")
23
- end
24
- end
25
-
26
- class AuthorAttribute < Attribute
27
- def parse
28
- item_head
29
- .remove(/\A#{config.deep_fetch(:csv, :regex, :formats)}/)
30
- .match(/.+(?=#{config.deep_fetch(:csv, :short_separator)})/)
31
- &.to_s
32
- &.strip
33
- end
34
- end
35
-
36
- class TitleAttribute < Attribute
37
- def parse
38
- if item_head.end_with?(config.deep_fetch(:csv, :short_separator).rstrip)
39
- raise InvalidHeadError, "Missing title? Head column ends in a separator"
40
- end
41
-
42
- item_head
43
- .remove(/\A#{config.deep_fetch(:csv, :regex, :formats)}/)
44
- .remove(/.+#{config.deep_fetch(:csv, :short_separator)}/)
45
- .remove(/#{config.deep_fetch(:csv, :long_separator)}.+\z/)
46
- .strip
47
- .presence || (raise InvalidHeadError, "Missing title")
48
- end
49
- end
50
-
51
- class GenresAttribute < Attribute
52
- def parse
53
- return nil unless columns[:genres]
54
-
55
- columns[:genres]
56
- .split(config.deep_fetch(:csv, :separator))
57
- .map(&:strip)
58
- .map(&:downcase)
59
- .map(&:presence)
60
- .compact.presence
61
- end
62
- end
63
-
64
- class NotesAttribute < Attribute
65
- def parse
66
- return nil unless columns[:public_notes]
67
-
68
- columns[:public_notes]
69
- .presence
70
- &.chomp
71
- &.remove(/#{config.deep_fetch(:csv, :long_separator).rstrip}\s*\z/)
72
- &.split(config.deep_fetch(:csv, :long_separator))
73
- &.map { |string|
74
- {
75
- blurb?: !!string.delete!(config.deep_fetch(:csv, :blurb_emoji)),
76
- private?: !!string.delete!(config.deep_fetch(:csv, :private_emoji)),
77
- content: string.strip,
78
- }
79
- }
80
- end
81
- end
82
- end
83
- end
@@ -1,25 +0,0 @@
1
- module Reading
2
- class Row
3
- # A base class that contains behaviors common to ___Attribute classes.
4
- class Attribute
5
- private attr_reader :item_head, :columns, :config
6
-
7
- # @param item_head [String] see Row#item_heads for a definition.
8
- # @param columns [Array<String>] the CSV row split into columns.
9
- # @param config [Hash]
10
- def initialize(item_head: nil, columns: nil, config:)
11
- unless item_head || columns
12
- raise ArgumentError, "Either item_head or columns must be given to an Attribute."
13
- end
14
-
15
- @item_head = item_head
16
- @columns = columns
17
- @config = config
18
- end
19
-
20
- def parse
21
- raise NotImplementedError, "#{self.class} should have implemented #{__method__}"
22
- end
23
- end
24
- end
25
- end
@@ -1,94 +0,0 @@
1
- module Reading
2
- # Methods to validate dates. This does not cover all the ways dates can be
3
- # invalid, just the ones not covered by ExperiencesAttribute during parsing.
4
- module DatesValidator
5
- using Util::HashArrayDeepFetch
6
-
7
- class << self
8
- # Checks the dates in the given experiences hash, and raises an error at
9
- # the first invalid date found.
10
- # @param experiences [Array<Hash>]
11
- # @param config [Hash]
12
- def validate(experiences, config)
13
- validate_dates_started_are_in_order(experiences) if dates_started_column?(config)
14
- validate_dates_finished_are_in_order(experiences) if dates_finished_column?(config)
15
- validate_experiences_of_same_variant_do_not_overlap(experiences) if both_date_columns?(config)
16
- validate_spans_are_in_order_and_not_overlapping(experiences)
17
- end
18
-
19
- private
20
-
21
- def dates_started_column?(config)
22
- config.deep_fetch(:csv, :enabled_columns).include?(:dates_started)
23
- end
24
-
25
- def dates_finished_column?(config)
26
- config.deep_fetch(:csv, :enabled_columns).include?(:dates_finished)
27
- end
28
-
29
- def both_date_columns?(config)
30
- dates_started_column?(config) && dates_finished_column?(config)
31
- end
32
-
33
- def validate_dates_started_are_in_order(experiences)
34
- experiences
35
- .filter { |exp| exp[:spans].any? }
36
- .map { |exp| exp[:spans].first[:dates].begin }
37
- .each_cons(2) do |a, b|
38
- if (a.nil? && b.nil?) || (a && b && a > b )
39
- raise InvalidDateError, "Dates started are not in order"
40
- end
41
- end
42
- end
43
-
44
- def validate_dates_finished_are_in_order(experiences)
45
- experiences
46
- .filter { |exp| exp[:spans].any? }
47
- .map { |exp| exp[:spans].last[:dates].end }
48
- .each_cons(2) do |a, b|
49
- if (a.nil? && b.nil?) || (a && b && a > b )
50
- raise InvalidDateError, "Dates finished are not in order"
51
- end
52
- end
53
- end
54
-
55
- def validate_experiences_of_same_variant_do_not_overlap(experiences)
56
- experiences
57
- .group_by { |exp| exp[:variant_index] }
58
- .each do |_variant_index, exps|
59
- exps.filter { |exp| exp[:spans].any? }.each_cons(2) do |a, b|
60
- a_metaspan = a[:spans].first[:dates].begin..a[:spans].last[:dates].end
61
- b_metaspan = b[:spans].first[:dates].begin..b[:spans].last[:dates].end
62
- if a_metaspan.cover?(b_metaspan.begin || a_metaspan.begin || a_metaspan.end) ||
63
- b_metaspan.cover?(a_metaspan.begin || b_metaspan.begin || b_metaspan.end)
64
- raise InvalidDateError, "Experiences are overlapping"
65
- end
66
- end
67
- end
68
- end
69
-
70
- def validate_spans_are_in_order_and_not_overlapping(experiences)
71
- experiences
72
- .filter { |exp| exp[:spans].any? }
73
- .each do |exp|
74
- exp[:spans]
75
- .map { |span| span[:dates] }
76
- .each do |dates|
77
- if dates.begin && dates.end && dates.begin > dates.end
78
- raise InvalidDateError, "A date range is backward"
79
- end
80
- end
81
- .each_cons(2) do |a, b|
82
- if a.begin > b.begin || a.end > b.end
83
- raise InvalidDateError, "Dates are not in order"
84
- end
85
- if a.cover?(b.begin || a.begin || a.end) ||
86
- b.cover?(a.begin || b.begin || b.end)
87
- raise InvalidDateError, "Dates are overlapping"
88
- end
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,74 +0,0 @@
1
- require_relative "spans_subattribute"
2
- require_relative "progress_subattribute"
3
- require_relative "dates_validator"
4
- require "date"
5
-
6
- module Reading
7
- class Row
8
- class ExperiencesAttribute < Attribute
9
- using Util::HashArrayDeepFetch
10
- using Util::HashDeepMerge
11
-
12
- def parse
13
- started, finished = dates_split(columns)
14
-
15
- experiences_with_dates = started.map.with_index { |entry, i|
16
- variant_index = variant_index(entry)
17
- spans_attr = SpansSubattribute.new(date_entry: entry, dates_finished: finished, date_index: i, variant_index:, columns:, config:)
18
-
19
- {
20
- spans: spans_attr.parse || template.fetch(:spans),
21
- group: group(entry) || template.fetch(:group),
22
- variant_index: variant_index || template.fetch(:variant_index)
23
- }
24
- }.presence
25
-
26
- if experiences_with_dates
27
- # Raises an error if any sequence of dates does not make sense.
28
- DatesValidator.validate(experiences_with_dates, config)
29
-
30
- return experiences_with_dates
31
- else
32
- if prog = ProgressSubattribute.new(columns:, config:).parse_head
33
- return [template.deep_merge(spans: [{ progress: prog }] )]
34
- else
35
- return nil
36
- end
37
- end
38
- end
39
-
40
- private
41
-
42
- def template
43
- @template ||= config.deep_fetch(:item, :template, :experiences).first
44
- end
45
-
46
- def dates_split(columns)
47
- dates_finished = columns[:dates_finished]&.presence
48
- &.split(config.deep_fetch(:csv, :separator))&.map(&:strip) || []
49
- # Don't use #has_key? because simply checking for nil covers the
50
- # case where dates_started is the last column and omitted.
51
- started_column_exists = columns[:dates_started]&.presence
52
-
53
- dates_started =
54
- if started_column_exists
55
- columns[:dates_started]&.presence&.split(config.deep_fetch(:csv, :separator))&.map(&:strip)
56
- else
57
- [""] * dates_finished.count
58
- end
59
-
60
- [dates_started, dates_finished]
61
- end
62
-
63
- def group(entry)
64
- entry.match(config.deep_fetch(:csv, :regex, :group_experience))&.captures&.first
65
- end
66
-
67
- def variant_index(date_entry)
68
- match = date_entry.match(config.deep_fetch(:csv, :regex, :variant_index))
69
-
70
- (match&.captures&.first&.to_i || 1) - 1
71
- end
72
- end
73
- end
74
- end
@@ -1,48 +0,0 @@
1
- module Reading
2
- class Row
3
- class ProgressSubattribute
4
- using Util::HashArrayDeepFetch
5
-
6
- private attr_reader :date_entry, :variant_index, :columns, :config
7
-
8
- # @param date_entry [String] the entry in Dates Started.
9
- # @param variant_index [Integer] the variant index, for getting length for default amount.
10
- # @param columns [Array<String>]
11
- # @param config [Hash]
12
- def initialize(date_entry: nil, variant_index: nil, columns:, config:)
13
- @date_entry = date_entry
14
- @variant_index = variant_index
15
- @columns = columns
16
- @config = config
17
- end
18
-
19
- def parse
20
- progress(date_entry) || progress(columns[:head])
21
- end
22
-
23
- def parse_head
24
- progress(columns[:head])
25
- end
26
-
27
- private
28
-
29
- def progress(str)
30
- prog = str.match(config.deep_fetch(:csv, :regex, :progress))
31
-
32
- if prog
33
- if prog_percent = prog[:percent]&.to_i
34
- return prog_percent / 100.0
35
- elsif prog_time = prog[:time]
36
- return prog_time
37
- elsif prog_pages = prog[:pages]&.to_i
38
- return prog_pages
39
- end
40
- end
41
-
42
- dnf = str.match(config.deep_fetch(:csv, :regex, :dnf))&.captures&.first
43
- return 0 if dnf
44
- nil
45
- end
46
- end
47
- end
48
- end
@@ -1,82 +0,0 @@
1
- module Reading
2
- class Row
3
- class SpansSubattribute
4
- using Util::HashArrayDeepFetch
5
-
6
- private attr_reader :date_entry, :dates_finished, :date_index, :variant_index, :columns, :config
7
-
8
- # @param date_entry [String] the entry in Dates Started.
9
- # @param dates_finished [Array<String>] the entries in Dates Finished.
10
- # @param date_index [Integer] the index of the entry.
11
- # @param variant_index [Integer] the variant index, for getting length for default amount.
12
- # @param columns [Array<String>]
13
- # @param config [Hash]
14
- def initialize(date_entry:, dates_finished:, date_index:, variant_index:, columns:, config:)
15
- @date_entry = date_entry
16
- @dates_finished = dates_finished
17
- @date_index = date_index
18
- @variant_index = variant_index
19
- @columns = columns
20
- @config = config
21
- end
22
-
23
- def parse
24
- started = date_started(date_entry)
25
- finished = date_finished(dates_finished, date_index)
26
- return [] if started.nil? && finished.nil?
27
-
28
- progress_attr = ProgressSubattribute.new(date_entry:, variant_index:, columns:, config:)
29
- progress = progress_attr.parse
30
-
31
- [{
32
- dates: started..finished || template.fetch(:dates),
33
- amount: length || template.fetch(:amount),
34
- progress: progress || (1.0 if finished) || template.fetch(:progress),
35
- name: template.fetch(:name),
36
- favorite?: template.fetch(:favorite?),
37
- }]
38
- end
39
-
40
- private
41
-
42
- def template
43
- @template ||= config.deep_fetch(:item, :template, :experiences, 0, :spans).first
44
- end
45
-
46
- def date_started(date_entry)
47
- dates = date_entry.scan(config.deep_fetch(:csv, :regex, :date))
48
- raise InvalidDateError, "Conjoined dates" if dates.count > 1
49
- raise InvalidDateError, "Missing or incomplete date" if date_entry.present? && dates.empty?
50
-
51
- date_str = dates.first
52
- Date.parse(date_str) if date_str
53
- rescue Date::Error
54
- raise InvalidDateError, "Unparsable date"
55
- end
56
-
57
- def date_finished(dates_finished, date_index)
58
- return nil if dates_finished.nil?
59
-
60
- date_str = dates_finished[date_index]&.presence
61
- Date.parse(date_str) if date_str
62
- rescue Date::Error
63
- if date_str.match?(config.deep_fetch(:csv, :regex, :date))
64
- raise InvalidDateError, "Unparsable date"
65
- else
66
- raise InvalidDateError, "Missing or incomplete date"
67
- end
68
- end
69
-
70
- def length
71
- sources_str = columns[:sources]&.presence || " "
72
- bare_variant = sources_str
73
- .split(config.deep_fetch(:csv, :regex, :formats_split))
74
- .dig(variant_index)
75
- &.split(config.deep_fetch(:csv, :long_separator))
76
- &.first
77
- length_attr = LengthSubattribute.new(bare_variant:, columns:, config:)
78
- length_attr.parse
79
- end
80
- end
81
- end
82
- end
@@ -1,44 +0,0 @@
1
- module Reading
2
- class Row
3
- class ExtraInfoSubattribute
4
- using Util::HashArrayDeepFetch
5
-
6
- private attr_reader :item_head, :variant_with_extras, :config
7
-
8
- # @param item_head [String] see Row#item_heads for a definition.
9
- # @param variant_with_extras [String] the full variant string.
10
- # @param config [Hash]
11
- def initialize(item_head:, variant_with_extras: nil, config:)
12
- @item_head = item_head
13
- @variant_with_extras = variant_with_extras
14
- @config = config
15
- end
16
-
17
- def parse
18
- (
19
- Array(extra_info(item_head)) +
20
- Array(extra_info(variant_with_extras))
21
- ).presence
22
- end
23
-
24
- def parse_head
25
- extra_info(item_head)
26
- end
27
-
28
- private
29
-
30
- def template
31
- config.deep_fetch(:item, :template, :variants, 0, :series).first
32
- end
33
-
34
- def extra_info(str)
35
- separated = str.split(config.deep_fetch(:csv, :long_separator))
36
- separated.delete_at(0) # everything before the extra info
37
- separated.reject { |str|
38
- str.start_with?("#{config.deep_fetch(:csv, :series_prefix)} ") ||
39
- str.match(config.deep_fetch(:csv, :regex, :series_volume))
40
- }.presence
41
- end
42
- end
43
- end
44
- end