reading 1.1.2 → 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: 7c5e7713901523000e0218a07a0295c5190089ed4d890df7b08469f224d3e019
4
- data.tar.gz: 1e77b55fc62c48f00c0c0ac284955ab231212f9a509cca75e00af76cf70f3644
3
+ metadata.gz: 5a081fbab3e05154fcd493884223c2eedcc0c60dd4be99d56f2373ef5689d337
4
+ data.tar.gz: e7f4dfef59413ce4ade78d2452193bec233482700cecee699655a1ea72a5e499
5
5
  SHA512:
6
- metadata.gz: 103b4c3467c6ca5a8672f8b3e89767b89bd643114f114dd2e02e23c9432f980016f16cb11326b250efd308de9517f4986f95c4b1f1ca5a22e6aab6ac11d9abf4
7
- data.tar.gz: 0a6dbeb09a6e1a44d314d4825a63452f1c6d2be3b3b147c70016ecc28d849371302ea32ec026e7b70e13d31ee5d40dfc2b7fccc53c39d4f8c89495ff56bac684
6
+ metadata.gz: 45e98e729dd0422b5e0e2abe014f8bab41190b5bd4d3df6969097a60c5738dace9c190bea8e82476c8de880a5d1c34772caf0d9957a975ce417c663190128b11
7
+ data.tar.gz: f5a9e4645ccc2b6b9a076197c1df92efb6f2219f51de1c7b8d746d08c98b4457aef143702cfabdf0b86eac4ac80bab56324814badae9bf739e56bcf4d49cd64f
@@ -105,6 +105,7 @@ module Reading
105
105
  course: "🏫",
106
106
  piece: "✏️",
107
107
  website: "🌐",
108
+ game: "🎲",
108
109
  },
109
110
  source_names_from_urls:
110
111
  {
@@ -135,6 +136,7 @@ module Reading
135
136
  piece: { emoji: "✏️" },
136
137
  video: { emoji: "🎞️" },
137
138
  audio: { emoji: "🎤" },
139
+ game: { emoji: "🎲" },
138
140
  },
139
141
  default_type: :book,
140
142
  },
@@ -191,6 +193,8 @@ module Reading
191
193
  [{
192
194
  blurb?: false,
193
195
  private?: false,
196
+ date: nil,
197
+ rating_change: [],
194
198
  content: nil,
195
199
  }],
196
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
@@ -58,7 +58,7 @@ module Reading
58
58
  # planned or dates
59
59
  (
60
60
  (
61
- (?<planned>\?\?)
61
+ (?<planned>\?\?|TODO)
62
62
  |
63
63
  (#{START_END_DATES_REGEX})
64
64
  )
@@ -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
@@ -19,7 +19,7 @@ module Reading
19
19
  (
20
20
  (?<date>\d{4}/\d\d?/\d\d?)
21
21
  |
22
- (?<planned>\?\?)
22
+ (?<planned>\?\?|TODO)
23
23
  )
24
24
  (\s+|\z)
25
25
  )?
@@ -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.1.2"
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.1.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Vogel