Merge pull request #3019 from duckduckgo/mintsoft/conversions/plurals

Conversions: Harmonise unit definitions
master
Ben Moon 2016-05-11 20:42:26 +01:00
commit 3a0e90fb62
3 changed files with 270 additions and 243 deletions

View File

@ -8,6 +8,7 @@ with 'DDG::GoodieRole::NumberStyler';
use Math::Round qw/nearest/;
use utf8;
use YAML::XS 'LoadFile';
use List::Util qw(any);
zci answer_type => 'conversions';
zci is_cached => 1;
@ -16,10 +17,15 @@ use bignum;
my @types = LoadFile(share('ratios.yml'));
my %unit_to_plural = ();
my @units = ();
my %plural_to_unit = ();
foreach my $type (@types) {
push(@units, $type->{'unit'});
push(@units, $type->{'plural'}) unless lc $type->{'unit'} eq lc $type->{'plural'};
push(@units, @{$type->{'aliases'}});
$unit_to_plural{lc $type->{'unit'}} = $type->{'plural'};
$plural_to_unit{lc $type->{'plural'}} = $type->{'unit'};
}
# build triggers based on available conversion units:
@ -35,33 +41,6 @@ my $question_prefix = qr/(?<prefix>convert|what (?:is|are|does)|how (?:much|many
my $factor_re = join('|', ('a', 'an', number_style_regex()));
my $guard = qr/^(?<question>$question_prefix)\s?(?<left_num>$factor_re*)\s?(?<left_unit>$keys)\s(?<connecting_word>in|to|into|(?:in to)|from)?\s?(?<right_num>$factor_re*)\s?(?:of\s)?(?<right_unit>$keys)[\?]?$/i;
# exceptions for pluralized forms:
my %plural_exceptions = (
'stone' => 'stone',
'foot' => 'feet',
'inch' => 'inches',
'pounds per square inch' => 'pounds per square inch',
'ton of TNT' => 'tons of TNT',
'metric horsepower' => 'metric horsepower',
'horsepower' => 'horsepower',
'electrical horsepower' => 'electrical horsepower',
'pounds force' => 'pounds force',
'坪' => '坪',
'km/h' => 'km/h',
'mph' => 'mph',
'm/s' => 'm/s',
'ft/s' => 'ft/s',
'dram avoirdupois' => 'drams avoirdupois',
'thousandth of an inch' => 'thousandths of an inch',
'century' => 'centuries',
'millennium' => 'millennia',
'mmHg' => 'mmHg',
'torr' => 'torr',
'cubic inch' => 'cubic inches',
'square foot' => 'square feet'
);
my %singular_exceptions = reverse %plural_exceptions;
# fix precision and rounding:
my $precision = 3;
my $nearest = '.' . ('0' x ($precision-1)) . '1';
@ -133,8 +112,7 @@ handle query_lc => sub {
&& "" eq $+{'right_num'}
&& $+{'question'} !~ qr/convert/i
&& !looks_plural($+{'right_unit'})
&& $+{'connecting_word'} !~ qr/to/i
&& $factor1[0] > $factor2[0]))
&& $+{'connecting_word'} !~ qr/to/i ))
{
$factor = $+{'right_num'};
@matches = ($matches[1], $matches[0]);
@ -194,31 +172,31 @@ handle query_lc => sub {
};
$factor = $styler->for_display($factor);
return $factor . " $result->{'from_unit'} = $result->{'result'} $result->{'to_unit'}",
structured_answer => {
data => {
raw_input => $styler->for_computation($factor),
raw_answer => $styler->for_computation($result->{'result'}),
left_unit => $result->{'from_unit'},
right_unit => $result->{'to_unit'},
markup_input => $styler->with_html($factor),
styled_output => $styler->with_html($result->{'result'}),
physical_quantity => $result->{'type'}
},
templates => {
group => 'text',
options => {
content => 'DDH.conversions.content'
}
}
return "$factor $result->{'from_unit'} = $result->{'result'} $result->{'to_unit'}",
structured_answer => {
data => {
raw_input => $styler->for_computation($factor),
raw_answer => $styler->for_computation($result->{'result'}),
left_unit => $result->{'from_unit'},
right_unit => $result->{'to_unit'},
markup_input => $styler->with_html($factor),
styled_output => $styler->with_html($result->{'result'}),
physical_quantity => $result->{'type'}
},
templates => {
group => 'text',
options => {
content => 'DDH.conversions.content'
}
}
};
};
sub looks_plural {
my ($unit) = @_;
my @unit_letters = split //, $unit;
return exists $singular_exceptions{$unit} || $unit_letters[-1] eq 's';
my ($input) = @_;
return defined $plural_to_unit{lc $input};
}
sub convert_temperatures {
my ($from, $to, $in_temperature) = @_;
@ -244,11 +222,10 @@ sub convert_temperatures {
}
sub get_matches {
my @input_matches = @_;
my @output_matches = ();
foreach my $match (@input_matches) {
foreach my $type (@types) {
if (lc $match eq $type->{'unit'} || grep { $_ eq lc $match } @{$type->{'aliases'}}) {
if (lc $match eq $type->{'unit'} || lc $match eq lc $type->{'plural'} || grep { $_ eq lc $match } @{$type->{'aliases'}}) {
push(@output_matches,{
type => $type->{'type'},
factor => $type->{'factor'},
@ -267,6 +244,7 @@ sub convert {
my @matches = get_matches($conversion->{'from_unit'}, $conversion->{'to_unit'});
return if $conversion->{'factor'} < 0 && !($matches[0]->{'can_be_negative'});
# matches must be of the same type (e.g., can't convert mass to length):
return if ($matches[0]->{'type'} ne $matches[1]->{'type'});
@ -286,19 +264,11 @@ sub convert {
"type" => $matches[0]->{'type'}
};
}
sub set_unit_pluralisation {
my ($unit, $count) = @_;
my $proper_unit = $unit;
my $already_plural = looks_plural($unit);
if ($already_plural && $count == 1) {
$proper_unit = $singular_exceptions{$unit} || substr($unit, 0, -1);
} elsif (!$already_plural && $count != 1) {
$proper_unit = $plural_exceptions{$unit} || $unit . 's';
}
return $proper_unit;
$unit = $unit_to_plural{lc $unit} if ($count != 1 && !looks_plural($unit));
return $unit;
}
1;

375
share/goodie/conversions/ratios.yml Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -359,6 +359,18 @@ ddg_goodie_test(
physical_quantity => 'pressure'
})
),
'0.01933677566613741911668448550544 psi in mmHg' => test_zci(
'0.01933677566613741911668448550544 pounds per square inch = 1 mmHg',
structured_answer => make_answer({
markup_input => '0.01933677566613741911668448550544',
raw_input => '0.01933677566613741911668448550544',
from_unit => 'pounds per square inch',
styled_output => '1',
raw_answer => '1',
to_unit => 'mmHg',
physical_quantity => 'pressure'
})
),
'2 thou to mm' => test_zci(
'2 thousandths of an inch = 0.051 millimeters',
structured_answer => make_answer({
@ -599,6 +611,18 @@ ddg_goodie_test(
physical_quantity => 'energy'
})
),
'1000000 kcal in tons of tnt' => test_zci(
'1,000,000 large calories = 1 ton of TNT',
structured_answer => make_answer({
markup_input => '1,000,000',
raw_input => '1000000',
from_unit => 'large calories',
styled_output => '1',
raw_answer => '1',
to_unit => 'ton of TNT',
physical_quantity => 'energy'
})
),
'90 ps in watts' => test_zci(
'90 metric horsepower = 66,194.888 watts',
structured_answer => make_answer({
@ -984,14 +1008,14 @@ ddg_goodie_test(
})
),
'ml in gallons' => test_zci(
'1 millilitre = 0.000264 us gallons',
'1 us gallon = 3,785.412 millilitres',,
structured_answer => make_answer({
markup_input => '1',
raw_input => '1',
from_unit => 'millilitre',
styled_output => '0.000264',
raw_answer => '0.000264',
to_unit => 'us gallons',
from_unit => 'us gallon',
styled_output => '3,785.412',
raw_answer => '3785.412',
to_unit => 'millilitres',
physical_quantity => 'volume'
})
),
@ -1431,14 +1455,14 @@ ddg_goodie_test(
})
),
'how many cm in metres?' => test_zci(
'1 centimeter = 0.010 meters',
'1 meter = 100 centimeters',
structured_answer => make_answer({
markup_input => '1',
raw_input => '1',
from_unit => 'centimeter',
styled_output => '0.010',
raw_answer => '0.010',
to_unit => 'meters',
from_unit => 'meter',
styled_output => '100',
raw_answer => '100',
to_unit => 'centimeters',
physical_quantity => 'length'
})
),