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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a081fbab3e05154fcd493884223c2eedcc0c60dd4be99d56f2373ef5689d337
4
- data.tar.gz: e7f4dfef59413ce4ade78d2452193bec233482700cecee699655a1ea72a5e499
3
+ metadata.gz: cdad0247655e1e44060d3f939edc7ca5d9357d0eae4c845ad171a3320e8e2dea
4
+ data.tar.gz: 0a3545801b0dc6a5852cd4e48c77c3a70f980cebd8bbf25ee56127bc3881fbf1
5
5
  SHA512:
6
- metadata.gz: 45e98e729dd0422b5e0e2abe014f8bab41190b5bd4d3df6969097a60c5738dace9c190bea8e82476c8de880a5d1c34772caf0d9957a975ce417c663190128b11
7
- data.tar.gz: f5a9e4645ccc2b6b9a076197c1df92efb6f2219f51de1c7b8d746d08c98b4457aef143702cfabdf0b86eac4ac80bab56324814badae9bf739e56bcf4d49cd64f
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
- input: query,
68
+ query,
69
69
  items:,
70
70
  result_formatters: Reading::Stats::ResultFormatters::TERMINAL,
71
71
  )
@@ -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
- (?:downgraded|upgraded|updated|changed)?\s*rating
30
+ (?:#{RATING_CHANGED_WORDS.join("|")})?\s*rating
19
31
  |
20
- rating\s*(?:downgraded|upgraded|updated|changed)?
32
+ rating\s*(?:#{RATING_CHANGED_WORDS.join("|")})?
21
33
  )?
22
34
  (?:\s+from\s+)?
23
35
  (?:
@@ -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
- experience.spans.last.progress || 0.0
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.any? &&
160
- (experience.spans.last.progress || 0.0).send(operator, progress)
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[aia ai],
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],
@@ -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:, items:, result_formatters: Reading::Stats::ResultFormatters::TRUNCATED_TITLES)
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
@@ -1,3 +1,3 @@
1
1
  module Reading
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reading
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Vogel