todo_time_patterns 0.0.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.
- data/lib/todo_time_patterns.rb +36 -0
- data/lib/todo_time_patterns/helper_modules.rb +7 -0
- data/lib/todo_time_patterns/parser.rb +47 -0
- data/lib/todo_time_patterns/parser_helpers.rb +17 -0
- data/lib/todo_time_patterns/time_patterns.rb +148 -0
- data/lib/todo_time_patterns/time_patterns_finder.rb +39 -0
- data/lib/todo_time_patterns/tokens.rb +266 -0
- data/tests/console_test.rb +5 -0
- data/tests/parser_tests.rb +18 -0
- data/tests/time_patterns_finder_tests.rb +39 -0
- data/tests/time_patterns_tests.rb +200 -0
- metadata +60 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'todo_time_patterns/parser'
|
2
|
+
require_relative 'todo_time_patterns/time_patterns_finder'
|
3
|
+
|
4
|
+
class TodoTimePatterns
|
5
|
+
def self.parse(input)
|
6
|
+
parser = TimePatterns::Parser.new
|
7
|
+
tokens = parser.parse input
|
8
|
+
|
9
|
+
time_patterns_finder = TimePatterns::TimePatternsFinder.new
|
10
|
+
tokens_with_time_patterns = time_patterns_finder.find_and_update tokens
|
11
|
+
|
12
|
+
result = generate_result(tokens_with_time_patterns, input)
|
13
|
+
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.generate_result(tokens, input)
|
18
|
+
time_token = tokens.find {|token| token.to_s == "{ending_form}"}
|
19
|
+
|
20
|
+
unless time_token.nil?
|
21
|
+
time_token = tokens.find {|token| token.to_s == "{ending_form}"}
|
22
|
+
to_remove = input.slice(time_token.start_index, time_token.end_index - time_token.start_index + 1)
|
23
|
+
|
24
|
+
{
|
25
|
+
hours: time_token.hours,
|
26
|
+
minutes: time_token.minutes,
|
27
|
+
interval: time_token.interval,
|
28
|
+
result_string: input.sub(to_remove, "").sub(" ", " ").strip
|
29
|
+
}
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private_class_method :generate_result
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'parser_helpers'
|
2
|
+
require_relative 'tokens'
|
3
|
+
require_relative 'helper_modules'
|
4
|
+
|
5
|
+
module TimePatterns
|
6
|
+
class Parser
|
7
|
+
def initialize
|
8
|
+
@parser_checks = {
|
9
|
+
letter?: WordToken,
|
10
|
+
number?: NumberToken,
|
11
|
+
symbol?: SymbToken,
|
12
|
+
unknown?: UnknownToken
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(input)
|
17
|
+
|
18
|
+
i = 0
|
19
|
+
buffer = ""
|
20
|
+
tokens = []
|
21
|
+
|
22
|
+
while input[i]
|
23
|
+
break if input[i] == nil
|
24
|
+
|
25
|
+
while input[i] == " "
|
26
|
+
i += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
@parser_checks.each do |check_method, check_class|
|
30
|
+
token_start_index = i
|
31
|
+
while input[i] != nil and input[i] != " " and input[i].send check_method
|
32
|
+
buffer << input[i]
|
33
|
+
i += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
unless buffer.empty?
|
37
|
+
tokens << (check_class.new buffer, token_start_index)
|
38
|
+
buffer = ""
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
tokens.extend(TokensStringRepresentation)
|
44
|
+
tokens
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class String
|
2
|
+
def letter?
|
3
|
+
('a'..'z').include?(self) or ('A'..'Z').include?(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
def number?
|
7
|
+
true if Integer(self) rescue false
|
8
|
+
end
|
9
|
+
|
10
|
+
def symbol?
|
11
|
+
[':', '#'].include?(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def unknown?
|
15
|
+
not letter? and not number?
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require_relative 'helper_modules'
|
2
|
+
|
3
|
+
module TimePatterns
|
4
|
+
class TimePattern
|
5
|
+
def initialize(pattern_array, token_class)
|
6
|
+
@pattern = pattern_array.join
|
7
|
+
@number_of_pattern_tokens = pattern_array.length
|
8
|
+
@token_class = token_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_and_update(tokens)
|
12
|
+
return tokens unless tokens.to_s.include? @pattern
|
13
|
+
|
14
|
+
tokens_clone = tokens.clone
|
15
|
+
tokens.each_cons(@number_of_pattern_tokens) do |tokens_set|
|
16
|
+
break unless tokens_set.length == @number_of_pattern_tokens
|
17
|
+
|
18
|
+
tokens_set.extend(TokensStringRepresentation)
|
19
|
+
|
20
|
+
if tokens_set.to_s == @pattern
|
21
|
+
next unless valid? tokens_set
|
22
|
+
|
23
|
+
time_token = @token_class.new tokens_set
|
24
|
+
substitute_tokens tokens_set, time_token, tokens_clone
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
tokens_clone
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def substitute_tokens(old, new, tokens)
|
33
|
+
index = tokens.index old[0]
|
34
|
+
tokens.delete_if {|token| old.include? token}
|
35
|
+
tokens.insert index, new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TimeWithModifierPattern < TimePattern
|
40
|
+
def initialize
|
41
|
+
super(%w[{number} {word}], TimeToken)
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid?(tokens_pair)
|
45
|
+
hour, modifier = tokens_pair.map { |token| token.value }
|
46
|
+
(0..12).include?(hour) and ["am", "pm"].include?(modifier) and not
|
47
|
+
(hour == 0 and modifier == "pm")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ClassicTimePattern < TimePattern
|
52
|
+
def initialize
|
53
|
+
super(%w[{number} {symbol} {number}], TimeToken)
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?(tokens_triplet)
|
57
|
+
hour, colon, minutes = tokens_triplet.map { |token| token.value }
|
58
|
+
(0..23).include?(hour) and (0..59).include?(minutes) and colon == ":"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ClassicTimeWithModifierPattern < TimePattern
|
63
|
+
def initialize
|
64
|
+
super(%w[{number} {symbol} {number} {word}], TimeToken)
|
65
|
+
end
|
66
|
+
|
67
|
+
def valid?(tokens_quadruple)
|
68
|
+
hour, colon, minutes, modifier = tokens_quadruple.map { |token| token.value }
|
69
|
+
(0..12).include?(hour) and (0..59).include?(minutes) and colon == ":" and ["am", "pm"].include?(modifier)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ComplexIntervalPattern < TimePattern
|
74
|
+
def initialize
|
75
|
+
super(%w[{number} {word} {number} {word}], IntervalToken)
|
76
|
+
end
|
77
|
+
|
78
|
+
def valid?(tokens_quadruple)
|
79
|
+
tokens = tokens_quadruple.each_slice(2).to_a
|
80
|
+
interval_pair_valid?(tokens[0], true) and interval_pair_valid?(tokens[1], false)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def interval_pair_valid?(tokens_pair, for_hours)
|
85
|
+
modifier = tokens_pair[1].value
|
86
|
+
|
87
|
+
if for_hours and %w[h hour hours].include? modifier
|
88
|
+
hours = tokens_pair[0].value
|
89
|
+
(0..23).include? hours
|
90
|
+
elsif not for_hours and %w[m min mins minutes].include? modifier
|
91
|
+
minutes = tokens_pair[0].value
|
92
|
+
(0..59).include? minutes
|
93
|
+
else
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class SimpleIntervalPattern < TimePattern
|
100
|
+
def initialize
|
101
|
+
super(%w[{number} {word}], IntervalToken)
|
102
|
+
end
|
103
|
+
|
104
|
+
def valid?(tokens_pair)
|
105
|
+
modifier = tokens_pair[1].value
|
106
|
+
|
107
|
+
if %w[h hour hours].include? modifier
|
108
|
+
hours = tokens_pair[0].value
|
109
|
+
(0..23).include? hours
|
110
|
+
elsif %w[m min mins minutes].include? modifier
|
111
|
+
minutes = tokens_pair[0].value
|
112
|
+
(0..59).include? minutes
|
113
|
+
else
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class TimeEndingPattern < TimePattern
|
120
|
+
def initialize
|
121
|
+
super(%w[{word} {time}], TimeEndingToken)
|
122
|
+
end
|
123
|
+
|
124
|
+
def valid?(tokens_pair)
|
125
|
+
tokens_pair.first.value == "at"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class IntervalEndingPattern < TimePattern
|
130
|
+
def initialize
|
131
|
+
super(%w[{word} {time} {word} {interval}], IntervalEndingToken)
|
132
|
+
end
|
133
|
+
|
134
|
+
def valid?(tokens_quadruple)
|
135
|
+
tokens_quadruple[0].value == "at" and tokens_quadruple[2].value == "for"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class IntervalTimeEndingPattern < TimePattern
|
140
|
+
def initialize
|
141
|
+
super(%w[{word} {time} {word} {time}], IntervalTimeEndingToken)
|
142
|
+
end
|
143
|
+
|
144
|
+
def valid?(tokens_quadruple)
|
145
|
+
tokens_quadruple[0].value == "from" and tokens_quadruple[2].value == "to"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'helper_modules'
|
2
|
+
require_relative 'tokens'
|
3
|
+
require_relative 'time_patterns'
|
4
|
+
|
5
|
+
module TimePatterns
|
6
|
+
class TimePatternsFinder
|
7
|
+
def find_and_update(tokens)
|
8
|
+
tokens_with_time = find_and_update_time tokens
|
9
|
+
tokens_with_intervals = find_and_update_intervals tokens_with_time
|
10
|
+
result = find_and_update_ending_forms tokens_with_intervals
|
11
|
+
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def find_and_update_time(tokens)
|
17
|
+
patterns = [ClassicTimeWithModifierPattern.new, ClassicTimePattern.new,
|
18
|
+
TimeWithModifierPattern.new]
|
19
|
+
patterns.each { |pattern| tokens = pattern.find_and_update tokens }
|
20
|
+
|
21
|
+
tokens
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_and_update_intervals(tokens)
|
25
|
+
patterns = [ComplexIntervalPattern.new, SimpleIntervalPattern.new]
|
26
|
+
patterns.each { |pattern| tokens = pattern.find_and_update tokens }
|
27
|
+
|
28
|
+
tokens
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_and_update_ending_forms(tokens)
|
32
|
+
patterns = [IntervalEndingPattern.new, IntervalTimeEndingPattern.new,
|
33
|
+
TimeEndingPattern.new]
|
34
|
+
patterns.each { |pattern| tokens = pattern.find_and_update tokens }
|
35
|
+
|
36
|
+
tokens
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require_relative 'helper_modules'
|
2
|
+
|
3
|
+
module TimePatterns
|
4
|
+
class WordToken
|
5
|
+
attr_reader :value
|
6
|
+
attr_reader :start_index
|
7
|
+
|
8
|
+
def initialize(value, start_index)
|
9
|
+
@value = value
|
10
|
+
@start_index = start_index
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"{word}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class NumberToken
|
19
|
+
attr_reader :value
|
20
|
+
attr_reader :start_index
|
21
|
+
|
22
|
+
def initialize(value, start_index)
|
23
|
+
@value = Integer(value)
|
24
|
+
@start_index = start_index
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"{number}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SymbToken
|
33
|
+
attr_reader :value
|
34
|
+
attr_reader :start_index
|
35
|
+
|
36
|
+
def initialize(value, start_index)
|
37
|
+
@value = value
|
38
|
+
@start_index = start_index
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"{symbol}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class UnknownToken
|
47
|
+
attr_reader :value
|
48
|
+
attr_reader :start_index
|
49
|
+
|
50
|
+
def initialize(value, start_index)
|
51
|
+
@value = value
|
52
|
+
@start_index = start_index
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
"{unknown}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class TimeToken
|
61
|
+
attr_reader :start_index
|
62
|
+
attr_reader :end_index
|
63
|
+
|
64
|
+
attr_reader :hours
|
65
|
+
attr_reader :minutes
|
66
|
+
|
67
|
+
def initialize(values)
|
68
|
+
@values = values
|
69
|
+
@values.extend(TokensStringRepresentation)
|
70
|
+
|
71
|
+
set_start_and_end_indexes values
|
72
|
+
set_hours_and_minutes values
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
"{time}"
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def set_start_and_end_indexes(values)
|
81
|
+
@start_index = values.first.start_index
|
82
|
+
@end_index = values.last.start_index + values.last.value.to_s.length - 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_hours_and_minutes(values)
|
86
|
+
pattern = values.join
|
87
|
+
|
88
|
+
case pattern
|
89
|
+
when "{number}{word}" then set_time_values(values[0].value, 0, values[1].value)
|
90
|
+
when "{number}{symbol}{number}" then set_time_values(values[0].value, values[2].value)
|
91
|
+
when "{number}{symbol}{number}{word}" then set_time_values(values[0].value, values[2].value, values[3].value)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_time_values(hours, minutes, modifier = "am")
|
96
|
+
#3pm
|
97
|
+
#17:00
|
98
|
+
#13:00pm
|
99
|
+
|
100
|
+
if modifier == "am"
|
101
|
+
if hours == 12
|
102
|
+
@hours = 0
|
103
|
+
else
|
104
|
+
@hours = hours
|
105
|
+
end
|
106
|
+
elsif modifier == "pm"
|
107
|
+
if hours == 12
|
108
|
+
@hours = 12
|
109
|
+
else
|
110
|
+
@hours = hours + 12
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@minutes = minutes
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class IntervalToken
|
119
|
+
attr_reader :start_index
|
120
|
+
attr_reader :end_index
|
121
|
+
attr_reader :minutes
|
122
|
+
|
123
|
+
def initialize(values)
|
124
|
+
@values = values
|
125
|
+
@values.extend(TokensStringRepresentation)
|
126
|
+
|
127
|
+
set_start_and_end_indexes values
|
128
|
+
set_minutes values
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_s
|
132
|
+
"{interval}"
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
def set_start_and_end_indexes(values)
|
137
|
+
@start_index = values.first.start_index
|
138
|
+
@end_index = values.last.start_index + values.last.value.to_s.length - 1
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_minutes(values)
|
142
|
+
#1h 30mins
|
143
|
+
#20mins
|
144
|
+
#1 hour
|
145
|
+
|
146
|
+
pattern = values.join
|
147
|
+
|
148
|
+
@minutes = 0
|
149
|
+
if pattern == "{number}{word}"
|
150
|
+
if %w[h hour hours].include? values[1].value
|
151
|
+
@minutes = values[0].value * 60
|
152
|
+
else
|
153
|
+
@minutes = values[0].value
|
154
|
+
end
|
155
|
+
elsif pattern == "{number}{word}{number}{word}"
|
156
|
+
hours = values[0].value
|
157
|
+
@minutes = values[2].value + hours * 60
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class TimeEndingToken
|
163
|
+
attr_reader :start_index
|
164
|
+
attr_reader :end_index
|
165
|
+
attr_reader :hours
|
166
|
+
attr_reader :minutes
|
167
|
+
attr_reader :interval
|
168
|
+
|
169
|
+
def initialize(values)
|
170
|
+
@interval = 0
|
171
|
+
@values = values
|
172
|
+
@values.extend(TokensStringRepresentation)
|
173
|
+
|
174
|
+
set_start_and_end_indexes values
|
175
|
+
set_minutes values
|
176
|
+
end
|
177
|
+
|
178
|
+
def to_s
|
179
|
+
"{ending_form}"
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
def set_start_and_end_indexes(values)
|
184
|
+
@start_index = values.first.start_index
|
185
|
+
@end_index = values.last.end_index
|
186
|
+
end
|
187
|
+
|
188
|
+
def set_minutes(values)
|
189
|
+
time = values[1]
|
190
|
+
@minutes = time.minutes
|
191
|
+
@hours = time.hours
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class IntervalEndingToken
|
196
|
+
attr_reader :start_index
|
197
|
+
attr_reader :end_index
|
198
|
+
attr_reader :minutes
|
199
|
+
attr_reader :hours
|
200
|
+
attr_reader :interval
|
201
|
+
|
202
|
+
def initialize(values)
|
203
|
+
@values = values
|
204
|
+
@values.extend(TokensStringRepresentation)
|
205
|
+
|
206
|
+
set_start_and_end_indexes values
|
207
|
+
set_time_and_interval values
|
208
|
+
end
|
209
|
+
|
210
|
+
def set_start_and_end_indexes(values)
|
211
|
+
@start_index = values.first.start_index
|
212
|
+
@end_index = values.last.end_index
|
213
|
+
end
|
214
|
+
|
215
|
+
def set_time_and_interval(values)
|
216
|
+
time = values[1]
|
217
|
+
@minutes = time.minutes
|
218
|
+
@hours = time.hours
|
219
|
+
|
220
|
+
@interval = values[3].minutes
|
221
|
+
end
|
222
|
+
|
223
|
+
def to_s
|
224
|
+
"{ending_form}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class IntervalTimeEndingToken
|
229
|
+
attr_reader :start_index
|
230
|
+
attr_reader :end_index
|
231
|
+
attr_reader :minutes
|
232
|
+
attr_reader :hours
|
233
|
+
attr_reader :interval
|
234
|
+
|
235
|
+
def initialize(values)
|
236
|
+
@values = values
|
237
|
+
@values.extend(TokensStringRepresentation)
|
238
|
+
|
239
|
+
set_start_and_end_indexes values
|
240
|
+
set_time_and_interval values
|
241
|
+
end
|
242
|
+
|
243
|
+
def set_start_and_end_indexes(values)
|
244
|
+
@start_index = values.first.start_index
|
245
|
+
@end_index = values.last.end_index
|
246
|
+
end
|
247
|
+
|
248
|
+
def set_time_and_interval(values)
|
249
|
+
first_time = values[1]
|
250
|
+
second_time = values[3]
|
251
|
+
|
252
|
+
first_time_minutes = first_time.hours * 60 + first_time.minutes
|
253
|
+
second_time_minutes = second_time.hours * 60 + second_time.minutes
|
254
|
+
|
255
|
+
raise "Second time in interval is less than the first one" if first_time_minutes > second_time_minutes
|
256
|
+
|
257
|
+
@hours = first_time.hours
|
258
|
+
@minutes = first_time.minutes
|
259
|
+
@interval = second_time_minutes - first_time_minutes
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_s
|
263
|
+
"{ending_form}"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
require_relative '../lib/todo_time_patterns/parser'
|
4
|
+
|
5
|
+
class ParserTests < MiniTest::Unit::TestCase
|
6
|
+
def test_parse_input_into_tokens
|
7
|
+
input = "Buy bread at 17:00"
|
8
|
+
expected_tokens = "{word}{word}{word}{number}{symbol}{number}"
|
9
|
+
|
10
|
+
parser = TimePatterns::Parser.new
|
11
|
+
tokens = parser.parse input
|
12
|
+
|
13
|
+
indexes = tokens.map { |t| t.start_index }
|
14
|
+
|
15
|
+
assert_equal(expected_tokens, tokens.to_s)
|
16
|
+
assert_equal([0, 4, 10, 13, 15, 16], indexes)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
require_relative '../lib/todo_time_patterns/parser'
|
4
|
+
require_relative '../lib/todo_time_patterns/time_patterns_finder'
|
5
|
+
|
6
|
+
class TimePatternsFinderTests < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@parser = TimePatterns::Parser.new
|
9
|
+
@patterns_finder = TimePatterns::TimePatternsFinder.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_simple_form
|
13
|
+
input = "Meet Sergii at 4pm"
|
14
|
+
|
15
|
+
tokens = @parser.parse input
|
16
|
+
result = @patterns_finder.find_and_update tokens
|
17
|
+
|
18
|
+
assert_equal("{word}{word}{ending_form}", result.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_interval
|
22
|
+
input = "Meet Sergii at 4pm for 2 hours 30mins"
|
23
|
+
|
24
|
+
tokens = @parser.parse input
|
25
|
+
result = @patterns_finder.find_and_update tokens
|
26
|
+
|
27
|
+
assert_equal("{word}{word}{ending_form}", result.to_s)
|
28
|
+
assert_equal(150, result.last.interval)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_interval_with_time
|
32
|
+
input = "Meet Sergii from 4pm to 18:23"
|
33
|
+
|
34
|
+
tokens = @parser.parse input
|
35
|
+
result = @patterns_finder.find_and_update tokens
|
36
|
+
|
37
|
+
assert_equal("{word}{word}{ending_form}", result.to_s)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
require_relative '../lib/todo_time_patterns/parser'
|
4
|
+
require_relative '../lib/todo_time_patterns/time_patterns'
|
5
|
+
|
6
|
+
class TimePatternsTests < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@modifiers = %w[am pm]
|
9
|
+
@hour_postfixes = %w[h hour hours]
|
10
|
+
@minute_postfixes = %w[m min mins minutes]
|
11
|
+
@parser = TimePatterns::Parser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_time_with_modifier_pattern
|
15
|
+
@modifiers.each do |modifier|
|
16
|
+
input = "Buy bread at 12#{modifier}"
|
17
|
+
tokens = @parser.parse input
|
18
|
+
|
19
|
+
pattern = TimePatterns::TimeWithModifierPattern.new
|
20
|
+
result = pattern.find_and_update tokens
|
21
|
+
|
22
|
+
assert_equal("{word}{word}{word}{time}", result.to_s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_time_with_modifier_pattern_and_invalid_entry
|
27
|
+
@modifiers.each do |modifier|
|
28
|
+
input = "Buy bread at 17 #{modifier}"
|
29
|
+
|
30
|
+
tokens = @parser.parse input
|
31
|
+
|
32
|
+
pattern = TimePatterns::TimeWithModifierPattern.new
|
33
|
+
result = pattern.find_and_update tokens
|
34
|
+
|
35
|
+
assert_equal("{word}{word}{word}{number}{word}", result.to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_classic_time_pattern
|
40
|
+
input = "Buy bread at 17:00"
|
41
|
+
|
42
|
+
tokens = @parser.parse input
|
43
|
+
|
44
|
+
pattern = TimePatterns::ClassicTimePattern.new
|
45
|
+
result = pattern.find_and_update tokens
|
46
|
+
|
47
|
+
assert_equal("{word}{word}{word}{time}", result.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_classic_time_pattern_and_invalid_entry
|
51
|
+
input = "Buy bread at 25:00"
|
52
|
+
|
53
|
+
tokens = @parser.parse input
|
54
|
+
|
55
|
+
pattern = TimePatterns::ClassicTimePattern.new
|
56
|
+
result = pattern.find_and_update tokens
|
57
|
+
|
58
|
+
assert_equal("{word}{word}{word}{number}{symbol}{number}", result.to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_classic_time_with_modifier_pattern
|
62
|
+
@modifiers.each do |modifier|
|
63
|
+
input = "Buy bread at 7:10#{modifier}"
|
64
|
+
|
65
|
+
tokens = @parser.parse input
|
66
|
+
|
67
|
+
pattern = TimePatterns::ClassicTimeWithModifierPattern.new
|
68
|
+
result = pattern.find_and_update tokens
|
69
|
+
|
70
|
+
assert_equal("{word}{word}{word}{time}", result.to_s)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_classic_time_with_modifier_pattern_and_invalid_entry
|
75
|
+
@modifiers.each do |modifier|
|
76
|
+
input = "Buy bread at 17:39 #{modifier}"
|
77
|
+
|
78
|
+
tokens = @parser.parse input
|
79
|
+
|
80
|
+
pattern = TimePatterns::ClassicTimeWithModifierPattern.new
|
81
|
+
result = pattern.find_and_update tokens
|
82
|
+
|
83
|
+
assert_equal("{word}{word}{word}{number}{symbol}{number}{word}", result.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_simple_interval_pattern
|
88
|
+
@modifiers.each do |modifier|
|
89
|
+
@minute_postfixes.each do |minutes|
|
90
|
+
input = "Meeting at 1#{modifier} for 30#{minutes}"
|
91
|
+
|
92
|
+
tokens = @parser.parse input
|
93
|
+
|
94
|
+
pattern = TimePatterns::SimpleIntervalPattern.new
|
95
|
+
result = pattern.find_and_update tokens
|
96
|
+
|
97
|
+
assert_equal("{word}{word}{number}{word}{word}{interval}", result.to_s)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_simple_interval_values
|
103
|
+
input = "Meeting with Pixel at 1pm for 1 hour"
|
104
|
+
|
105
|
+
tokens = @parser.parse input
|
106
|
+
|
107
|
+
pattern = TimePatterns::SimpleIntervalPattern.new
|
108
|
+
result = pattern.find_and_update tokens
|
109
|
+
|
110
|
+
assert_equal(60, result.last.minutes)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_simple_interval_pattern_invalid_entry
|
114
|
+
input = "Meeting at 1pm for 30hours"
|
115
|
+
|
116
|
+
tokens = @parser.parse input
|
117
|
+
|
118
|
+
pattern = TimePatterns::SimpleIntervalPattern.new
|
119
|
+
result = pattern.find_and_update tokens
|
120
|
+
|
121
|
+
assert_equal("{word}{word}{number}{word}{word}{number}{word}", result.to_s)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_complex_interval_pattern
|
125
|
+
@hour_postfixes.each do |hours|
|
126
|
+
@minute_postfixes.each do |minutes|
|
127
|
+
input = "Breakfast for 1#{hours} 23#{minutes}"
|
128
|
+
|
129
|
+
tokens = @parser.parse input
|
130
|
+
|
131
|
+
pattern = TimePatterns::ComplexIntervalPattern.new
|
132
|
+
result = pattern.find_and_update tokens
|
133
|
+
|
134
|
+
assert_equal("{word}{word}{interval}", result.to_s)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_complex_interval_pattern_invalid_entry
|
140
|
+
input = "Breakfast for 1m 23m"
|
141
|
+
|
142
|
+
parser = TimePatterns::Parser.new
|
143
|
+
tokens = parser.parse input
|
144
|
+
|
145
|
+
pattern = TimePatterns::ComplexIntervalPattern.new
|
146
|
+
result = pattern.find_and_update tokens
|
147
|
+
|
148
|
+
assert_equal("{word}{word}{number}{word}{number}{word}", result.to_s)
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_simple_time_ending
|
152
|
+
input = "Breakfast at 17:00"
|
153
|
+
|
154
|
+
parser = TimePatterns::Parser.new
|
155
|
+
tokens = parser.parse input
|
156
|
+
|
157
|
+
pattern = TimePatterns::ClassicTimePattern.new
|
158
|
+
result = pattern.find_and_update tokens
|
159
|
+
|
160
|
+
ending_pattern = TimePatterns::TimeEndingPattern.new
|
161
|
+
ending_result = ending_pattern.find_and_update result
|
162
|
+
|
163
|
+
assert_equal("{word}{ending_form}", ending_result.to_s)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_interval_ending
|
167
|
+
input = "Breakfast at 4pm for 1h 30mins"
|
168
|
+
|
169
|
+
parser = TimePatterns::Parser.new
|
170
|
+
tokens = parser.parse input
|
171
|
+
|
172
|
+
pattern = TimePatterns::TimeWithModifierPattern.new
|
173
|
+
result = pattern.find_and_update tokens
|
174
|
+
interval_pattern = TimePatterns::ComplexIntervalPattern.new
|
175
|
+
result = interval_pattern.find_and_update result
|
176
|
+
|
177
|
+
ending_pattern = TimePatterns::IntervalEndingPattern.new
|
178
|
+
ending_result = ending_pattern.find_and_update result
|
179
|
+
|
180
|
+
assert_equal("{word}{ending_form}", ending_result.to_s)
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_interval_with_time_ending
|
184
|
+
input = "Breakfast from 4:00 to 9:43pm"
|
185
|
+
|
186
|
+
parser = TimePatterns::Parser.new
|
187
|
+
tokens = parser.parse input
|
188
|
+
|
189
|
+
pattern = TimePatterns::ClassicTimeWithModifierPattern.new
|
190
|
+
result = pattern.find_and_update tokens
|
191
|
+
|
192
|
+
pattern = TimePatterns::ClassicTimePattern.new
|
193
|
+
result = pattern.find_and_update result
|
194
|
+
|
195
|
+
ending_pattern = TimePatterns::IntervalTimeEndingPattern.new
|
196
|
+
ending_result = ending_pattern.find_and_update result
|
197
|
+
|
198
|
+
assert_equal("{word}{ending_form}", ending_result.to_s)
|
199
|
+
end
|
200
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: todo_time_patterns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ivan Bokii
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-22 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! " Todo time patterns is a small library that helps you to parse
|
15
|
+
different\n time patterns in strings like:\n - meet bob at 4pm\n -
|
16
|
+
meeting at 13:34\n - watch Adventure Times at 3:34pm\n - Go shopping at
|
17
|
+
2pm for 2h\n - Pay your tech. debt at 2pm for 35mins to Mafia\n - Sleep
|
18
|
+
well from 1am to 8:00\n\n etc.\n"
|
19
|
+
email: bokiyis@gmail.com
|
20
|
+
executables: []
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- lib/todo_time_patterns/helper_modules.rb
|
25
|
+
- lib/todo_time_patterns/parser.rb
|
26
|
+
- lib/todo_time_patterns/parser_helpers.rb
|
27
|
+
- lib/todo_time_patterns/time_patterns.rb
|
28
|
+
- lib/todo_time_patterns/time_patterns_finder.rb
|
29
|
+
- lib/todo_time_patterns/tokens.rb
|
30
|
+
- lib/todo_time_patterns.rb
|
31
|
+
- tests/console_test.rb
|
32
|
+
- tests/parser_tests.rb
|
33
|
+
- tests/time_patterns_finder_tests.rb
|
34
|
+
- tests/time_patterns_tests.rb
|
35
|
+
homepage: https://github.com/spkenny/todo_time_patterns
|
36
|
+
licenses: []
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.8.21
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: Library that helps you to parse date patterns in strings like 'meet Bob at
|
59
|
+
5:45pm'
|
60
|
+
test_files: []
|