reading 1.3.0 → 1.4.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/bin/reading +1 -1
- data/lib/reading/filter.rb +1 -1
- data/lib/reading/parsing/attributes/notes.rb +10 -0
- data/lib/reading/parsing/rows/regular_columns/notes.rb +15 -3
- data/lib/reading/stats/filter.rb +17 -7
- data/lib/reading/stats/operation.rb +18 -1
- data/lib/reading/stats/query.rb +1 -1
- data/lib/reading/stats/result_formatters.rb +2 -1
- data/lib/reading/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdad0247655e1e44060d3f939edc7ca5d9357d0eae4c845ad171a3320e8e2dea
|
|
4
|
+
data.tar.gz: 0a3545801b0dc6a5852cd4e48c77c3a70f980cebd8bbf25ee56127bc3881fbf1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 573bccb558e477adec37c63d489975d076e9006590eda7ccaae1a5e733c096df6a152a799b67bbf1119409a4a4b0e19ba71a6b4bea4f3879a8edab331e67db37
|
|
7
|
+
data.tar.gz: f182929326660d98d72d56b46b290d8da12f216c77ba5eb9b373d9adb19e20693bb15d41bacfd70e4feef9ff12d700c615bf7f09ca3f06c92f71681c57cb724d
|
data/bin/reading
CHANGED
|
@@ -65,7 +65,7 @@ def run_query(file_path:, query:, enabled_columns_str:)
|
|
|
65
65
|
|
|
66
66
|
items = Reading.parse(path: file_path, item_view: false, error_handler:)
|
|
67
67
|
results = Reading.stats(
|
|
68
|
-
|
|
68
|
+
query,
|
|
69
69
|
items:,
|
|
70
70
|
result_formatters: Reading::Stats::ResultFormatters::TERMINAL,
|
|
71
71
|
)
|
data/lib/reading/filter.rb
CHANGED
|
@@ -72,7 +72,7 @@ module Reading
|
|
|
72
72
|
return items unless minimum_previous_rating
|
|
73
73
|
|
|
74
74
|
items.select! do |item|
|
|
75
|
-
max_previous_rating = item.notes.map { it.rating_change.first }.compact.max
|
|
75
|
+
max_previous_rating = item.notes.reject(&:private?).map { it.rating_change.first }.compact.max
|
|
76
76
|
max_previous_rating && max_previous_rating >= minimum_previous_rating &&
|
|
77
77
|
item.rating && item.rating < minimum_previous_rating
|
|
78
78
|
end
|
|
@@ -17,6 +17,7 @@ module Reading
|
|
|
17
17
|
content: note[:content],
|
|
18
18
|
}
|
|
19
19
|
}.then { distribute_dates_to_rating_changes!(it, parsed_row) }
|
|
20
|
+
.then { capitalize_content_of_rating_changes!(it, parsed_row) }
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
@@ -48,6 +49,15 @@ module Reading
|
|
|
48
49
|
note[:date] = last_experience[:spans].last[:dates]&.end || last_experience[:spans].first[:dates]&.begin
|
|
49
50
|
end
|
|
50
51
|
end
|
|
52
|
+
|
|
53
|
+
def capitalize_content_of_rating_changes!(transformed_notes, parsed_row)
|
|
54
|
+
return unless transformed_notes
|
|
55
|
+
|
|
56
|
+
transformed_notes.each do |note|
|
|
57
|
+
next if note[:rating_change].empty? || note[:content].nil?
|
|
58
|
+
note[:content].capitalize!
|
|
59
|
+
end
|
|
60
|
+
end
|
|
51
61
|
end
|
|
52
62
|
end
|
|
53
63
|
end
|
|
@@ -5,19 +5,31 @@ module Reading
|
|
|
5
5
|
# See https://github.com/fpsvogel/reading/blob/main/doc/csv-format.md#notes-column
|
|
6
6
|
# and https://github.com/fpsvogel/reading/blob/main/doc/csv-format.md#notes-column-special-notes
|
|
7
7
|
class Notes < Column
|
|
8
|
+
RATING_CHANGED_WORDS = %w[
|
|
9
|
+
changed
|
|
10
|
+
updated
|
|
11
|
+
revised
|
|
12
|
+
downgraded
|
|
13
|
+
demoted
|
|
14
|
+
lowered
|
|
15
|
+
upgraded
|
|
16
|
+
promoted
|
|
17
|
+
raised
|
|
18
|
+
]
|
|
19
|
+
|
|
8
20
|
def self.segment_separator
|
|
9
21
|
/\s*--\s*/
|
|
10
22
|
end
|
|
11
23
|
|
|
12
24
|
def self.regexes(segment_index)
|
|
13
25
|
[%r{\A
|
|
14
|
-
(?:(?<blurb>💬)|(?<private>🔒))
|
|
26
|
+
(?:(?<blurb>💬)|(?<private>🔒))?\s*
|
|
15
27
|
(?<date>\d{4}/\d\d?/\d\d?)?:?\s*
|
|
16
28
|
(?:
|
|
17
29
|
(?:
|
|
18
|
-
(
|
|
30
|
+
(?:#{RATING_CHANGED_WORDS.join("|")})?\s*rating
|
|
19
31
|
|
|
|
20
|
-
rating\s*(
|
|
32
|
+
rating\s*(?:#{RATING_CHANGED_WORDS.join("|")})?
|
|
21
33
|
)?
|
|
22
34
|
(?:\s+from\s+)?
|
|
23
35
|
(?:
|
data/lib/reading/stats/filter.rb
CHANGED
|
@@ -113,7 +113,7 @@ module Reading
|
|
|
113
113
|
positive_operator = operator == :"!=" ? :== : operator
|
|
114
114
|
|
|
115
115
|
matches = items.select { |item|
|
|
116
|
-
actual_previous_ratings = item.notes.map { it.rating_change.first }.compact
|
|
116
|
+
actual_previous_ratings = item.notes.reject(&:private?).map { it.rating_change.first }.compact
|
|
117
117
|
|
|
118
118
|
previous_ratings.any? { |previous_rating|
|
|
119
119
|
if !actual_previous_ratings.empty? || %i[== !=].include?(operator)
|
|
@@ -140,24 +140,34 @@ module Reading
|
|
|
140
140
|
(raise InputError, "Progress must be a percentage")
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
# Also implemented in Stats::Operation::ACTIONS[:average_progress]
|
|
144
|
+
calculate_progress = lambda { |item, experience|
|
|
145
|
+
amount = experience.spans.sum { |span|
|
|
146
|
+
span.amount * (span.progress || 0.0)
|
|
147
|
+
}.to_i
|
|
148
|
+
length = item.variants[experience.variant_index].length
|
|
149
|
+
return nil unless length
|
|
150
|
+
amount.to_f / length.to_i
|
|
151
|
+
}
|
|
152
|
+
|
|
143
153
|
filtered_items = items.map { |item|
|
|
144
154
|
# Ensure multiple values after a negative operator have an "and"
|
|
145
155
|
# relation: "not(x and y)", rather than "not(x or y)".
|
|
146
156
|
if operator == :"!="
|
|
147
157
|
item_progresses = item.experiences.map { |experience|
|
|
148
|
-
|
|
149
|
-
}
|
|
158
|
+
calculate_progress.call(item, experience)
|
|
159
|
+
}.compact
|
|
150
160
|
|
|
151
161
|
next if (item_progresses - progresses).empty?
|
|
152
162
|
end
|
|
153
163
|
|
|
154
164
|
# Filter out non-matching experiences.
|
|
155
|
-
# TODO: Make this more accurate by calculating the average progress
|
|
156
|
-
# across spans? Currently it checks just the last span's progress.
|
|
157
165
|
filtered_experiences = item.experiences.select { |experience|
|
|
158
166
|
progresses.any? { |progress|
|
|
159
|
-
experience.spans.
|
|
160
|
-
|
|
167
|
+
next if experience.spans.empty?
|
|
168
|
+
item_progress = calculate_progress.call(item, experience)
|
|
169
|
+
next unless item_progress
|
|
170
|
+
item_progress.send(operator, progress)
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
|
|
@@ -107,6 +107,22 @@ module Reading
|
|
|
107
107
|
amounts_by_date.values.sum / amounts_by_date.count
|
|
108
108
|
end
|
|
109
109
|
},
|
|
110
|
+
average_progress: proc { |items|
|
|
111
|
+
experiences_with_progress = 0
|
|
112
|
+
total_progress = items.sum { |item|
|
|
113
|
+
item.experiences.sum { |experience|
|
|
114
|
+
amount = experience.spans.sum { |span|
|
|
115
|
+
span.amount * (span.progress || 0.0)
|
|
116
|
+
}.to_i
|
|
117
|
+
length = item.variants[experience.variant_index].length
|
|
118
|
+
next 0 unless length
|
|
119
|
+
experiences_with_progress += 1
|
|
120
|
+
amount.to_f / length.to_i
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
total_progress / experiences_with_progress
|
|
125
|
+
},
|
|
110
126
|
list_item: proc { |items|
|
|
111
127
|
items.map { |item| author_and_title(item) }
|
|
112
128
|
},
|
|
@@ -269,8 +285,9 @@ module Reading
|
|
|
269
285
|
ALIASES = {
|
|
270
286
|
average_rating: %w[ar],
|
|
271
287
|
average_length: %w[al],
|
|
272
|
-
average_amount: %w[
|
|
288
|
+
average_amount: %w[aa],
|
|
273
289
|
:"average_daily-amount" => %w[ada ad],
|
|
290
|
+
average_progress: %w[ap],
|
|
274
291
|
list_item: %w[li list],
|
|
275
292
|
total_item: %w[item count],
|
|
276
293
|
total_amount: %w[amount],
|
data/lib/reading/stats/query.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Reading
|
|
|
15
15
|
# @param result_formatters [Boolean, Hash{Symbol => Proc}] to alter the
|
|
16
16
|
# appearance of results; keys should be from among the keys of
|
|
17
17
|
# Operation::ACTIONS. Pre-made formatters are in result_formatters.rb.
|
|
18
|
-
def initialize(input
|
|
18
|
+
def initialize(input, items:, result_formatters: Reading::Stats::ResultFormatters::TRUNCATED_TITLES)
|
|
19
19
|
@input = input
|
|
20
20
|
@items = items
|
|
21
21
|
@result_formatters = result_formatters
|
|
@@ -18,6 +18,7 @@ module Reading
|
|
|
18
18
|
average_length: ->(result) { length_to_s(result) },
|
|
19
19
|
average_amount: ->(result) { length_to_s(result) },
|
|
20
20
|
:"average_daily-amount" => ->(result) { "#{length_to_s(result)} per day" },
|
|
21
|
+
average_progress: ->(result) { "#{(result * 100).round(1)}%" },
|
|
21
22
|
total_item: ->(result) {
|
|
22
23
|
if result.zero?
|
|
23
24
|
"none"
|
|
@@ -106,4 +107,4 @@ module Reading
|
|
|
106
107
|
end
|
|
107
108
|
end
|
|
108
109
|
end
|
|
109
|
-
end
|
|
110
|
+
end
|
data/lib/reading/version.rb
CHANGED