reading 1.2.0 → 1.3.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: 1de3ae0f90b05ae6e01ea8e2eda7aff828214d826be309604c571e588c98a41d
4
- data.tar.gz: 633a8399e99dab04f9bdbb50bb2348cdba16b5383ce76b29210ad341aca7fcdd
3
+ metadata.gz: 5a081fbab3e05154fcd493884223c2eedcc0c60dd4be99d56f2373ef5689d337
4
+ data.tar.gz: e7f4dfef59413ce4ade78d2452193bec233482700cecee699655a1ea72a5e499
5
5
  SHA512:
6
- metadata.gz: 464ca9056f3d2ed9be69e66f5b43bc37914f5458f830cbbab8d2b3f42599688eba0ec7a5e366024e993ddb62e5b60bafd45243eb354bb33624d63dd3174c1e7d
7
- data.tar.gz: 20e50ef79ef02a74e495b8c5e2b89c61a379ec85826a28b786e305cfb3215e462fd0e6d3f759c13b0f5ef7f0d042bba339800be99b3a5488c4d30823c62a5a3c
6
+ metadata.gz: 45e98e729dd0422b5e0e2abe014f8bab41190b5bd4d3df6969097a60c5738dace9c190bea8e82476c8de880a5d1c34772caf0d9957a975ce417c663190128b11
7
+ data.tar.gz: f5a9e4645ccc2b6b9a076197c1df92efb6f2219f51de1c7b8d746d08c98b4457aef143702cfabdf0b86eac4ac80bab56324814badae9bf739e56bcf4d49cd64f
@@ -193,6 +193,8 @@ module Reading
193
193
  [{
194
194
  blurb?: false,
195
195
  private?: false,
196
+ date: nil,
197
+ rating_change: [],
196
198
  content: nil,
197
199
  }],
198
200
  },
@@ -60,9 +60,21 @@ module Reading
60
60
  return items unless minimum_rating
61
61
 
62
62
  items.select! do |item|
63
- if item.rating
64
- item.rating >= minimum_rating
65
- end
63
+ item.rating && item.rating >= minimum_rating
64
+ end
65
+ end
66
+
67
+ # Mutates the given array of Items to select only Items that were at some
68
+ # point greater than or equal to (but are now less than) the given minimum.
69
+ # @param items [Array<Item>]
70
+ # @param minimum_rating [Integer]
71
+ def by_minimum_previous_rating!(items, minimum_previous_rating)
72
+ return items unless minimum_previous_rating
73
+
74
+ items.select! do |item|
75
+ max_previous_rating = item.notes.map { it.rating_change.first }.compact.max
76
+ max_previous_rating && max_previous_rating >= minimum_previous_rating &&
77
+ item.rating && item.rating < minimum_previous_rating
66
78
  end
67
79
  end
68
80
 
@@ -12,9 +12,41 @@ module Reading
12
12
  {
13
13
  blurb?: note.has_key?(:blurb),
14
14
  private?: note.has_key?(:private),
15
+ date: date(note[:date]),
16
+ rating_change: rating_change(note[:rating_from], note[:rating_to]),
15
17
  content: note[:content],
16
18
  }
17
- }
19
+ }.then { distribute_dates_to_rating_changes!(it, parsed_row) }
20
+ end
21
+
22
+ private
23
+
24
+ def date(date_string)
25
+ return nil unless date_string
26
+
27
+ Date.parse(date_string)
28
+ rescue Date::Error
29
+ raise InvalidDateError, "Unparsable date \"#{date_string}\""
30
+ end
31
+
32
+ def rating_change(rating_from, rating_to)
33
+ return [] unless rating_from && rating_to
34
+
35
+ [Integer(rating_from), Integer(rating_to)]
36
+ end
37
+
38
+ # Rating change notes without dates are automatically given the end or
39
+ # start dates of the item's experiences in reverse order.
40
+ def distribute_dates_to_rating_changes!(transformed_notes, parsed_row)
41
+ return unless transformed_notes
42
+ experiences = Experiences.new.transform_from_parsed(parsed_row, 0)
43
+
44
+ transformed_notes.reverse_each do |note|
45
+ return if experiences.empty?
46
+ next if note[:rating_change].empty? || note[:date]
47
+ last_experience = experiences.pop
48
+ note[:date] = last_experience[:spans].last[:dates]&.end || last_experience[:spans].first[:dates]&.begin
49
+ end
18
50
  end
19
51
  end
20
52
  end
@@ -11,8 +11,24 @@ module Reading
11
11
 
12
12
  def self.regexes(segment_index)
13
13
  [%r{\A
14
- (?:(?<blurb>💬)|(?<private>🔒))?(?<content>.+)
15
- \z}x]
14
+ (?:(?<blurb>💬)|(?<private>🔒))?
15
+ (?<date>\d{4}/\d\d?/\d\d?)?:?\s*
16
+ (?:
17
+ (?:
18
+ (?:downgraded|upgraded|updated|changed)?\s*rating
19
+ |
20
+ rating\s*(?:downgraded|upgraded|updated|changed)?
21
+ )?
22
+ (?:\s+from\s+)?
23
+ (?:
24
+ (?<rating_from>\d)
25
+ \s+to\s+
26
+ (?<rating_to>\d)
27
+ )
28
+ (?:\s+because\s+|\s*[[:punct:]]\s*)?
29
+ )?
30
+ (?<content>.+)?
31
+ \z}xi]
16
32
  end
17
33
  end
18
34
  end
@@ -101,6 +101,36 @@ module Reading
101
101
 
102
102
  matches
103
103
  },
104
+ :"previous-rating" => proc { |values, operator, items|
105
+ previous_ratings = values.map { |value|
106
+ if value
107
+ Integer(value, exception: false) ||
108
+ Float(value, exception: false) ||
109
+ (raise InputError, "Previous rating must be a number")
110
+ end
111
+ }
112
+
113
+ positive_operator = operator == :"!=" ? :== : operator
114
+
115
+ matches = items.select { |item|
116
+ actual_previous_ratings = item.notes.map { it.rating_change.first }.compact
117
+
118
+ previous_ratings.any? { |previous_rating|
119
+ if !actual_previous_ratings.empty? || %i[== !=].include?(operator)
120
+ actual_previous_ratings.any? { it.send(positive_operator, previous_rating) }
121
+ end
122
+ }
123
+ }
124
+
125
+ # Instead of using it.send(operator, format) above, invert the matches
126
+ # here to ensure multiple values after a negative operator have an
127
+ # "and" relation: "not(x and y)", rather than "not(x or y)".
128
+ if operator == :"!="
129
+ matches = items - matches
130
+ end
131
+
132
+ matches
133
+ },
104
134
  progress: proc { |values, operator, items|
105
135
  progresses = values.map { |value|
106
136
  value = "0%" if value == "0"
@@ -640,6 +670,7 @@ module Reading
640
670
 
641
671
  NUMERIC_OPERATORS = {
642
672
  rating: true,
673
+ :"previous-rating" => true,
643
674
  progress: true,
644
675
  experience: true,
645
676
  date: true,
@@ -654,6 +685,7 @@ module Reading
654
685
  }
655
686
 
656
687
  PROHIBIT_NONE_VALUE = {
688
+ :"previous-rating" => true,
657
689
  progress: true,
658
690
  title: true,
659
691
  :"end-date" => true,
@@ -199,7 +199,7 @@ module Reading
199
199
  notes_word_count = item
200
200
  .notes
201
201
  .sum { |note|
202
- note.content.scan(/[\w[:punct:]]+/).count
202
+ (note.content || "").scan(/[\w[:punct:]]+/).count
203
203
  }
204
204
 
205
205
  [author_and_title(item), notes_word_count]
@@ -1,3 +1,3 @@
1
1
  module Reading
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.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.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Vogel