Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minutizer #231

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/chronic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'date'

require 'chronic/parser'
require 'chronic/minutizer'
require 'chronic/date'
require 'chronic/time'

Expand Down Expand Up @@ -90,6 +91,10 @@ def self.parse(text, options = {})
Parser.new(options).parse(text)
end

def self.minutize(text, options = {})
Minutizer.new(options).minutize(text)
end

# Construct a new time object determining possible month overflows
# and leap years.
#
Expand Down
64 changes: 64 additions & 0 deletions lib/chronic/minutizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module Chronic
class Minutizer

def minutize(text)
text = pre_normalize(text)
extract_time(text)
end

def pre_normalize(text)
text.gsub!(/half an/, 'a half')
text.gsub!(/half a/, 'a half')
text = Numerizer.numerize(text.downcase)
text.gsub!(/an hour/, '1 hour')
text.gsub!(/a hour/, '1 hour')
text.gsub!(/a day/, '1 minute')
text.gsub!(/a minute/, '1 day')
text.gsub!(/a week/, '1 week')
# used to catch halves not covered by Numerizer. Use for the conversion of 'an hour and a half' Previous gsubs will have changed it to '1 hour and haAlf' by this point.
text.gsub!(/(\d+)\s?\w+?\s?and haAlf/) do |m|
m.gsub!(/\A\d+/, ($1.to_f + 0.5).to_s )
end
text.gsub!(/\s?and haAlf/, '')
text.gsub!(/haAlf/, "0.5")
text.gsub!(/(seconds|secnds|second|secnd|secs)/, 'sec')
text.gsub!(/(minutes|minute|mintues|mintes|mintue|minte)/, 'min')
text.gsub!(/(horus|hours|huors|huor|hrs|hr)/, 'hour')
text.gsub!(/(days|day|dy)/, 'day')
text.gsub!(/(weeks|wks|wk)/, 'week')
text
end

def extract_time(text)
minutes = extract(text, 'week')
minutes += extract(text, 'day')
minutes += extract(text, 'hour')
minutes += extract(text, 'min')
minutes += extract(text, 'sec')
end

def extract(text, option)
total = text.match(/(\d+.\d+|\d+)\s?(#{option})/)

return 0 unless total
digitize_time(total[1], option)
end

def digitize_time(time, option)
case option
when 'week'
multiplier = 7 * 24 * 60
when 'day'
multiplier = 24 * 60
when 'hour'
multiplier = 60
when 'min'
multiplier = 1
when 'sec'
multiplier = 1.to_f/60
end

return multiplier * time.to_f
end
end
end
144 changes: 144 additions & 0 deletions test/test_minutizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
require 'helper'


class TestMinutizer < TestCase

def string(option)
"test string for #{option}"
end

def test_prenormalize
# half times
assert_equal "1.5 min", Chronic::Minutizer.new.pre_normalize("one and a half minutes")
assert_equal "1.5 min", Chronic::Minutizer.new.pre_normalize("one and a half minutes")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("one and a half hours")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("an hour and a half")
assert_equal "0.5 hour", Chronic::Minutizer.new.pre_normalize("half an hour")
assert_equal "1.5 day", Chronic::Minutizer.new.pre_normalize("one and a half days")
assert_equal "0.5 day", Chronic::Minutizer.new.pre_normalize("half a day")
assert_equal "sliding for 0.5 hour", Chronic::Minutizer.new.pre_normalize("sliding for half an hour")
assert_equal "sliding for 2.5 hour", Chronic::Minutizer.new.pre_normalize("sliding for two and a half hours")
assert_equal "sliding for 0.5 hour", Chronic::Minutizer.new.pre_normalize("sliding for a half hour")
assert_equal "sliding for 0.5 hour", Chronic::Minutizer.new.pre_normalize("sliding for half an hour")

#time word variations
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 seconds")
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 secnds")
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 second")
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 secnd")
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 secs")
assert_equal "1 sec", Chronic::Minutizer.new.pre_normalize("1 sec")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 minutes")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 mintues")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 mintes")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 minute")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 mintue")
assert_equal "1 min", Chronic::Minutizer.new.pre_normalize("1 minte")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 horus")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 hours")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 huors")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 huor")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 hr")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 hrs")
assert_equal "1 day", Chronic::Minutizer.new.pre_normalize("1 days")
assert_equal "1 day", Chronic::Minutizer.new.pre_normalize("1 day")
assert_equal "1 day", Chronic::Minutizer.new.pre_normalize("1 dy")
assert_equal "1 week", Chronic::Minutizer.new.pre_normalize("1 weeks")
assert_equal "1 week", Chronic::Minutizer.new.pre_normalize("1 wks")
assert_equal "1 week", Chronic::Minutizer.new.pre_normalize("1 wk")

#english numbers
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("an hour")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("one hour")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("one and a half hours")
assert_equal "2 day, 3 hour, and 40 min", Chronic::Minutizer.new.pre_normalize("two days, three hours, and forty minutes")
assert_equal "2.5 day", Chronic::Minutizer.new.pre_normalize("two and a half days")
assert_equal "half the time it 2.5 day", Chronic::Minutizer.new.pre_normalize("half the time it two and a half day")

# arabic numbers
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 hour")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("1.5 hours")
assert_equal "1 hour 30 min", Chronic::Minutizer.new.pre_normalize("1 hour 30 minutes")
assert_equal "1 hour", Chronic::Minutizer.new.pre_normalize("1 hour")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("1 and a half hours")
assert_equal "1.5 hour", Chronic::Minutizer.new.pre_normalize("1.5 hours")
assert_equal "2 day, 3 hour, and 40 min", Chronic::Minutizer.new.pre_normalize("2 days, 3 hours, and 40 minutes")
assert_equal "2.5 day", Chronic::Minutizer.new.pre_normalize("2 and a half days")

end

def test_extract_time
assert_equal 60, Chronic::Minutizer.new.extract_time( string "60 minutes" )
assert_equal 60, Chronic::Minutizer.new.extract_time( string "1 hour" )
assert_equal 1440, Chronic::Minutizer.new.extract_time( string "1 day" )
assert_equal 720, Chronic::Minutizer.new.extract_time( string "0.5 day" )
end

def test_time_variations
strings = {
"hours" => 60,
"hr" => 60,
"minutes" => 1,
"mintue" => 1,
"half a day" => 2160.0,
"days" => 1440,
}
strings.each do |key, val|
assert_equal val, Chronic::Minutizer.new.minutize("extracts time for one #{key}")
end
end

def test_no_num_times
strings = {
"half a day" => 720,
"half an hour" => 30,
"half a minute" => 0.5,
"a hour" => 60
}
strings.each do |key, val|
assert_equal val, Chronic::Minutizer.new.minutize("extracts time for #{key}")
end
end

def test_mispleled_times
strings = {
"60 secnds" => 1,
"1 minte" => 1,
"1 mintes" => 1,
"1 minute" => 1,
"1 mintue" => 1,
"1 mintues" => 1,
"1 huor" => 60,
"1 huors" => 60,
"1 dy" => 1440,
"1 wks" => 10080
}
strings.each do |key, val|
assert_equal val, Chronic::Minutizer.new.minutize("extracts time for #{key}")
end
end

def test_general_text_examples
assert_equal 5.35, Chronic::Minutizer.new.minutize("The experiment lasted for 321 seconds")
assert_equal 720, Chronic::Minutizer.new.minutize("I took one sleeping pill and slept for half a day")
assert_equal 15120.0, Chronic::Minutizer.new.minutize("I was traveling for a week and a half")
assert_equal 3100, Chronic::Minutizer.new.minutize("I traveled for 2 days, three hrs, and forty mintes")
assert_equal 20160.0, Chronic::Minutizer.new.minutize("The conference lasted 2 weeks")
end

def test_with_mixed_in_numbers
assert_equal 180, Chronic::Minutizer.new.minutize("I typed 120 lines of code in the last three hours")
assert_equal 210, Chronic::Minutizer.new.minutize("I drank 15 beers in 3 and a half hours")
assert_equal 40, Chronic::Minutizer.new.minutize("my three best friends brought half a cake over and we ate it in 40 minutes")
end

def test_comma_separated_times
assert_equal 3100, Chronic::Minutizer.new.minutize("I was traveling for 2 days, three hrs, and forty mintes")
assert_equal 140, Chronic::Minutizer.new.minutize("it took 2 hours and 20minutes")
end

def test_with_mixed_in_time_variations
assert_equal 180, Chronic::Minutizer.new.minutize("The min requirement to pass is 180 minutes")
assert_equal 180, Chronic::Minutizer.new.minutize("The second timeto pass is 3 hours")
end
end