reading 0.8.0 → 0.9.1
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/bin/reading +95 -10
- data/lib/reading/config.rb +27 -5
- data/lib/reading/errors.rb +4 -1
- data/lib/reading/item/time_length.rb +60 -23
- data/lib/reading/item/view.rb +14 -19
- data/lib/reading/item.rb +324 -54
- data/lib/reading/parsing/attributes/attribute.rb +0 -7
- data/lib/reading/parsing/attributes/experiences/dates_and_head_transformer.rb +17 -13
- data/lib/reading/parsing/attributes/experiences/history_transformer.rb +172 -60
- data/lib/reading/parsing/attributes/experiences/spans_validator.rb +19 -20
- data/lib/reading/parsing/attributes/experiences.rb +5 -5
- data/lib/reading/parsing/attributes/shared.rb +17 -7
- data/lib/reading/parsing/attributes/variants.rb +9 -6
- data/lib/reading/parsing/csv.rb +38 -35
- data/lib/reading/parsing/parser.rb +23 -24
- data/lib/reading/parsing/rows/blank.rb +23 -0
- data/lib/reading/parsing/rows/comment.rb +6 -7
- data/lib/reading/parsing/rows/compact_planned.rb +9 -9
- data/lib/reading/parsing/rows/compact_planned_columns/head.rb +2 -2
- data/lib/reading/parsing/rows/custom_config.rb +42 -0
- data/lib/reading/parsing/rows/regular.rb +15 -14
- data/lib/reading/parsing/rows/regular_columns/length.rb +8 -8
- data/lib/reading/parsing/rows/regular_columns/sources.rb +16 -10
- data/lib/reading/parsing/rows/regular_columns/start_dates.rb +5 -1
- data/lib/reading/parsing/transformer.rb +13 -17
- data/lib/reading/stats/filter.rb +738 -0
- data/lib/reading/stats/grouping.rb +257 -0
- data/lib/reading/stats/operation.rb +345 -0
- data/lib/reading/stats/query.rb +37 -0
- data/lib/reading/stats/terminal_result_formatters.rb +91 -0
- data/lib/reading/util/exclude.rb +12 -0
- data/lib/reading/util/hash_array_deep_fetch.rb +1 -23
- data/lib/reading/util/hash_to_data.rb +2 -2
- data/lib/reading/version.rb +1 -1
- data/lib/reading.rb +36 -21
- metadata +28 -24
- data/bin/readingfile +0 -31
- data/lib/reading/util/string_remove.rb +0 -28
- data/lib/reading/util/string_truncate.rb +0 -22
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
|
3
|
+
module Reading
|
4
|
+
module Stats
|
5
|
+
module ResultFormatters
|
6
|
+
TERMINAL = {
|
7
|
+
average_length: ->(result) { length_to_s(result) },
|
8
|
+
average_amount: ->(result) { length_to_s(result) },
|
9
|
+
:'average_daily-amount' => ->(result) { "#{length_to_s(result)} per day" },
|
10
|
+
total_item: ->(result) {
|
11
|
+
if result.zero?
|
12
|
+
PASTEL.bright_black("none")
|
13
|
+
else
|
14
|
+
color("#{result} #{result == 1 ? "item" : "items"}")
|
15
|
+
end
|
16
|
+
},
|
17
|
+
total_amount: ->(result) { length_to_s(result) },
|
18
|
+
top_length: ->(result) { top_or_bottom_lengths(result) },
|
19
|
+
top_amount: ->(result) { top_or_bottom_lengths(result) },
|
20
|
+
top_speed: ->(result) { top_or_bottom_speeds(result) },
|
21
|
+
bottom_length: ->(result) { top_or_bottom_lengths(result) },
|
22
|
+
botom_amount: ->(result) { top_or_bottom_lengths(result) },
|
23
|
+
bottom_speed: ->(result) { top_or_bottom_speeds(result) },
|
24
|
+
}
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
PASTEL = Pastel.new
|
29
|
+
|
30
|
+
# Applies a terminal color.
|
31
|
+
# @param string [String]
|
32
|
+
# @return [String]
|
33
|
+
private_class_method def self.color(string)
|
34
|
+
PASTEL.bright_blue(string)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Converts a length/amount (pages or time) into a string.
|
38
|
+
# @param length [Numeric, Reading::Item::TimeLength]
|
39
|
+
# @param color [Boolean] whether a terminal color should be applied.
|
40
|
+
# @return [String]
|
41
|
+
private_class_method def self.length_to_s(length, color: true)
|
42
|
+
if length.is_a?(Numeric)
|
43
|
+
length_string = "#{length.round} pages"
|
44
|
+
else
|
45
|
+
length_string = length.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
color ? color(length_string) : length_string
|
49
|
+
end
|
50
|
+
|
51
|
+
# Formats a list of top/bottom length results as a string.
|
52
|
+
# @param result [Array]
|
53
|
+
# @return [String]
|
54
|
+
private_class_method def self.top_or_bottom_lengths(result)
|
55
|
+
offset = result.count.digits.count
|
56
|
+
|
57
|
+
result
|
58
|
+
.map.with_index { |(title, length), index|
|
59
|
+
pad = ' ' * (offset - (index + 1).digits.count)
|
60
|
+
|
61
|
+
title_line = "#{index + 1}. #{pad}#{title}"
|
62
|
+
indent = " #{' ' * offset}"
|
63
|
+
|
64
|
+
"#{title_line}\n#{indent}#{length_to_s(length)}"
|
65
|
+
}
|
66
|
+
.join("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Formats a list of top/bottom speed results as a string.
|
70
|
+
# @param result [Array]
|
71
|
+
# @return [String]
|
72
|
+
private_class_method def self.top_or_bottom_speeds(result)
|
73
|
+
offset = result.count.digits.count
|
74
|
+
|
75
|
+
result
|
76
|
+
.map.with_index { |(title, hash), index|
|
77
|
+
amount = length_to_s(hash[:amount], color: false)
|
78
|
+
days = "#{hash[:days]} #{hash[:days] == 1 ? "day" : "days"}"
|
79
|
+
pad = ' ' * (offset - (index + 1).digits.count)
|
80
|
+
|
81
|
+
title_line = "#{index + 1}. #{pad}#{title}"
|
82
|
+
indent = " #{' ' * offset}"
|
83
|
+
colored_speed = color("#{amount} in #{days}")
|
84
|
+
|
85
|
+
"#{title_line}\n#{indent}#{colored_speed}"
|
86
|
+
}
|
87
|
+
.join("\n")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,31 +1,9 @@
|
|
1
1
|
module Reading
|
2
2
|
module Util
|
3
|
-
class FetchDepthExceededError < StandardError
|
4
|
-
end
|
5
|
-
|
6
3
|
# Similar to Array#dig and Hash#dig but raises an error for not found elements.
|
7
|
-
#
|
8
|
-
# More flexible but slightly slower alternative:
|
9
|
-
# keys.reduce(self) { |a, e| a.fetch(e) }
|
10
|
-
#
|
11
|
-
# See performance comparisons:
|
12
|
-
# https://fpsvogel.com/posts/2022/ruby-hash-dot-syntax-deep-fetch
|
13
4
|
module HashArrayDeepFetch
|
14
5
|
def deep_fetch(*keys)
|
15
|
-
|
16
|
-
when 1
|
17
|
-
fetch(keys[0])
|
18
|
-
when 2
|
19
|
-
fetch(keys[0]).fetch(keys[1])
|
20
|
-
when 3
|
21
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2])
|
22
|
-
when 4
|
23
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2]).fetch(keys[3])
|
24
|
-
when 5
|
25
|
-
fetch(keys[0]).fetch(keys[1]).fetch(keys[2]).fetch(keys[3]).fetch(keys[4])
|
26
|
-
else
|
27
|
-
raise FetchDepthExceededError, "#deep_fetch can't fetch that deep!"
|
28
|
-
end
|
6
|
+
keys.reduce(self) { |a, e| a.fetch(e) }
|
29
7
|
end
|
30
8
|
|
31
9
|
refine Hash do
|
data/lib/reading/version.rb
CHANGED
data/lib/reading.rb
CHANGED
@@ -1,26 +1,36 @@
|
|
1
|
-
|
2
|
-
require_relative
|
3
|
-
require_relative
|
4
|
-
require_relative
|
1
|
+
Dir[File.join(__dir__, 'reading', 'util', '*.rb')].each { |file| require file }
|
2
|
+
require_relative 'reading/errors'
|
3
|
+
require_relative 'reading/config'
|
4
|
+
require_relative 'reading/parsing/csv'
|
5
|
+
require_relative 'reading/filter'
|
6
|
+
require_relative 'reading/stats/query'
|
7
|
+
require_relative 'reading/item/time_length.rb'
|
5
8
|
|
6
9
|
# The gem's public API. See https://github.com/fpsvogel/reading#usage
|
7
10
|
#
|
8
11
|
# Architectural overview:
|
9
12
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
13
|
+
# (filtered (stats input*
|
14
|
+
# (CSV input) (Items) Items) and Items) (results)
|
15
|
+
# | Λ | Λ | Λ
|
16
|
+
# V | V | V |
|
17
|
+
# ::parse | ::filter | ::stats |
|
18
|
+
# | | | | | |
|
19
|
+
# | | | | | |
|
20
|
+
# | | | | | |
|
21
|
+
# Parsing::CSV ---------> Item Filter Stats::Query
|
22
|
+
# / \ / \ / | \
|
23
|
+
# / \ Item::View Item::TimeLength / | \
|
24
|
+
# / \ / | \
|
25
|
+
# Parsing::Parser Parsing::Transformer Stats::Filter | Stats::Operation
|
26
|
+
# | | Stats::Grouping
|
27
|
+
# parsing/rows/* parsing/attributes/*
|
28
|
+
# * Stats input is either from the
|
29
|
+
# command line (via the `reading`
|
30
|
+
# command) or provided via Ruby
|
31
|
+
# code that uses this gem.
|
32
|
+
# Results likewise go either to
|
33
|
+
# stdout or to the gem user.
|
24
34
|
#
|
25
35
|
module Reading
|
26
36
|
# Parses a CSV file or string. See Parsing::CSV#initialize and #parse for details.
|
@@ -34,10 +44,15 @@ module Reading
|
|
34
44
|
Filter.by(...)
|
35
45
|
end
|
36
46
|
|
37
|
-
#
|
47
|
+
# Returns statistics on Items. See Stats::Query#initialize and #result for details.
|
48
|
+
def self.stats(...)
|
49
|
+
query = Stats::Query.new(...)
|
50
|
+
query.result
|
51
|
+
end
|
52
|
+
|
38
53
|
# @return [Hash]
|
39
|
-
def self.
|
40
|
-
Config.
|
54
|
+
def self.config
|
55
|
+
Config.hash
|
41
56
|
end
|
42
57
|
|
43
58
|
# A shortcut for getting a time from a string.
|
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.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felipe Vogel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pastel
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: amazing_print
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: debug
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,20 +108,6 @@ dependencies:
|
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '1.0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: amazing_print
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '1.4'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '1.4'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rubycritic
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -127,12 +127,10 @@ email:
|
|
127
127
|
- fps.vogel@gmail.com
|
128
128
|
executables:
|
129
129
|
- reading
|
130
|
-
- readingfile
|
131
130
|
extensions: []
|
132
131
|
extra_rdoc_files: []
|
133
132
|
files:
|
134
133
|
- bin/reading
|
135
|
-
- bin/readingfile
|
136
134
|
- lib/reading.rb
|
137
135
|
- lib/reading/config.rb
|
138
136
|
- lib/reading/errors.rb
|
@@ -154,10 +152,12 @@ files:
|
|
154
152
|
- lib/reading/parsing/attributes/variants.rb
|
155
153
|
- lib/reading/parsing/csv.rb
|
156
154
|
- lib/reading/parsing/parser.rb
|
155
|
+
- lib/reading/parsing/rows/blank.rb
|
157
156
|
- lib/reading/parsing/rows/column.rb
|
158
157
|
- lib/reading/parsing/rows/comment.rb
|
159
158
|
- lib/reading/parsing/rows/compact_planned.rb
|
160
159
|
- lib/reading/parsing/rows/compact_planned_columns/head.rb
|
160
|
+
- lib/reading/parsing/rows/custom_config.rb
|
161
161
|
- lib/reading/parsing/rows/regular.rb
|
162
162
|
- lib/reading/parsing/rows/regular_columns/end_dates.rb
|
163
163
|
- lib/reading/parsing/rows/regular_columns/genres.rb
|
@@ -169,14 +169,18 @@ files:
|
|
169
169
|
- lib/reading/parsing/rows/regular_columns/sources.rb
|
170
170
|
- lib/reading/parsing/rows/regular_columns/start_dates.rb
|
171
171
|
- lib/reading/parsing/transformer.rb
|
172
|
+
- lib/reading/stats/filter.rb
|
173
|
+
- lib/reading/stats/grouping.rb
|
174
|
+
- lib/reading/stats/operation.rb
|
175
|
+
- lib/reading/stats/query.rb
|
176
|
+
- lib/reading/stats/terminal_result_formatters.rb
|
172
177
|
- lib/reading/util/blank.rb
|
178
|
+
- lib/reading/util/exclude.rb
|
173
179
|
- lib/reading/util/hash_array_deep_fetch.rb
|
174
180
|
- lib/reading/util/hash_compact_by_template.rb
|
175
181
|
- lib/reading/util/hash_deep_merge.rb
|
176
182
|
- lib/reading/util/hash_to_data.rb
|
177
183
|
- lib/reading/util/numeric_to_i_if_whole.rb
|
178
|
-
- lib/reading/util/string_remove.rb
|
179
|
-
- lib/reading/util/string_truncate.rb
|
180
184
|
- lib/reading/version.rb
|
181
185
|
homepage: https://github.com/fpsvogel/reading
|
182
186
|
licenses:
|
@@ -185,23 +189,23 @@ metadata:
|
|
185
189
|
allowed_push_host: https://rubygems.org
|
186
190
|
homepage_uri: https://github.com/fpsvogel/reading
|
187
191
|
source_code_uri: https://github.com/fpsvogel/reading
|
188
|
-
changelog_uri: https://github.com/fpsvogel/reading/blob/
|
192
|
+
changelog_uri: https://github.com/fpsvogel/reading/blob/main/CHANGELOG.md
|
189
193
|
post_install_message:
|
190
194
|
rdoc_options: []
|
191
195
|
require_paths:
|
192
196
|
- lib
|
193
197
|
required_ruby_version: !ruby/object:Gem::Requirement
|
194
198
|
requirements:
|
195
|
-
- - "
|
199
|
+
- - "~>"
|
196
200
|
- !ruby/object:Gem::Version
|
197
|
-
version: 3.
|
201
|
+
version: 3.3.0
|
198
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
203
|
requirements:
|
200
204
|
- - ">="
|
201
205
|
- !ruby/object:Gem::Version
|
202
206
|
version: '0'
|
203
207
|
requirements: []
|
204
|
-
rubygems_version: 3.
|
208
|
+
rubygems_version: 3.5.15
|
205
209
|
signing_key:
|
206
210
|
specification_version: 4
|
207
211
|
summary: Parses a CSV reading log.
|
data/bin/readingfile
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# A script that provides a quick way to see the output of a CSV file.
|
4
|
-
#
|
5
|
-
# Usage:
|
6
|
-
# Run on the command line:
|
7
|
-
# reading "<file path>" "<optional comma-separated names of enabled columns>"
|
8
|
-
#
|
9
|
-
# Examples:
|
10
|
-
# reading '/home/felipe/reading.csv'
|
11
|
-
# reading '/home/felipe/reading.csv' 'head, sources'
|
12
|
-
|
13
|
-
|
14
|
-
require_relative "../lib/reading"
|
15
|
-
require "amazing_print"
|
16
|
-
require "debug"
|
17
|
-
|
18
|
-
path = ARGV[0]
|
19
|
-
unless path
|
20
|
-
raise ArgumentError, "CSV path argument required, such as '/home/felipe/reading.csv'"
|
21
|
-
end
|
22
|
-
|
23
|
-
config = {}
|
24
|
-
if ARGV[1]
|
25
|
-
enabled_columns = ARGV[1].split(",").map(&:strip).map(&:to_sym)
|
26
|
-
config = { enabled_columns: }
|
27
|
-
end
|
28
|
-
|
29
|
-
items = Reading.parse(path, config:, hash_output: true)
|
30
|
-
|
31
|
-
ap items
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Reading
|
2
|
-
module Util
|
3
|
-
# Shortcuts for String#sub and String#gsub when replacing with an empty string.
|
4
|
-
module StringRemove
|
5
|
-
refine String do
|
6
|
-
def remove(pattern)
|
7
|
-
sub(pattern, EMPTY_STRING)
|
8
|
-
end
|
9
|
-
|
10
|
-
def remove!(pattern)
|
11
|
-
sub!(pattern, EMPTY_STRING)
|
12
|
-
end
|
13
|
-
|
14
|
-
def remove_all(pattern)
|
15
|
-
gsub(pattern, EMPTY_STRING)
|
16
|
-
end
|
17
|
-
|
18
|
-
def remove_all!(pattern)
|
19
|
-
gsub!(pattern, EMPTY_STRING)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
EMPTY_STRING = "".freeze
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Reading
|
2
|
-
module Util
|
3
|
-
# Shortens the String to a given length.
|
4
|
-
module StringTruncate
|
5
|
-
refine String do
|
6
|
-
# @param length [Integer]
|
7
|
-
# @return [String]
|
8
|
-
def truncate(length)
|
9
|
-
if length < self.length - ELLIPSIS.length
|
10
|
-
"#{self[0...length]}#{ELLIPSIS}"
|
11
|
-
else
|
12
|
-
self
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
ELLIPSIS = "...".freeze
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|