DE: Distance + Volume

Summary: Pull Request resolved: https://github.com/facebook/duckling/pull/311

Reviewed By: patapizza

Differential Revision: D13683766

Pulled By: chinmay87

fbshipit-source-id: a18c5ab656d26eb2b83d9340f307baae89da56f6
This commit is contained in:
Martin Ring 2019-01-28 11:05:06 -08:00 committed by Facebook Github Bot
parent 8b344b98cf
commit fc7f2c77dd
11 changed files with 570 additions and 8 deletions

View File

@ -14,8 +14,10 @@ import Duckling.Dimensions.Types
allDimensions :: [Some Dimension]
allDimensions =
[ This Duration
[ This Distance
, This Duration
, This Numeral
, This Ordinal
, This Time
, This Volume
]

View File

@ -0,0 +1,75 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Distance.DE.Corpus
( corpus
) where
import Data.String
import Prelude
import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
corpus :: Corpus
corpus = (testContext {locale = makeLocale DE Nothing}, testOptions, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (simple Kilometre 3)
[ "3 kilometer"
, "3 km"
, "3km"
, "3,0 km"
]
, examples (simple Mile 8)
[ "acht meilen"
, "8 meilen"
]
, examples (simple Metre 9)
[ "9m"
]
, examples (simple Centimetre 2)
[ "2cm"
, "2 zentimeter"
]
, examples (simple Inch 5)
[ "5''"
, "fünf zoll"
, "5\""
]
, examples (simple Metre 1.87)
[ "1,87 meter"
]
, examples (between Kilometre (3, 5))
[ "zwischen 3 und 5 kilometern"
, "von 3km bis 5km"
, "um die 3-5 kilometer"
, "etwa 3km-5km"
, "3-5 kilometer"
]
, examples (under Mile 3.5)
[ "unter 3,5 meilen"
, "weniger als 3,5meilen"
--, "niedriger als dreikommafünf meilen"
]
, examples (above Inch 5)
[ "mehr als fünf zoll"
, "mindestens 5''"
, "über 5\""
]
, examples (between Millimetre (5, 6))
[ "zwischen 5 und sechs Millimetern"
, "zwischen 5 und sechs millimeter"
, "5-6 mm"
]
]

View File

@ -0,0 +1,173 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Distance.DE.Rules (rules) where
import Data.String
import Data.Text (Text)
import Prelude
import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Distance.Types (DistanceData(..))
import qualified Duckling.Distance.Types as TDistance
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Types
distances :: [(Text, String, TDistance.Unit)]
distances =
[ -- Imperial
("miles", "meilen?", TDistance.Mile)
, ("inch", "(\"|''|zoll)", TDistance.Inch)
-- Metric
, ("km", "k(ilo)?m(etern?)?", TDistance.Kilometre)
, ("meters", "m(etern?)?", TDistance.Metre)
, ("centimeters", "(cm|[zc]entimetern?)", TDistance.Centimetre)
, ("millimeters", "(mm|millimetern?)", TDistance.Millimetre)
]
rulePrecision :: Rule
rulePrecision = Rule
{ name = "about|exactly <dist>"
, pattern =
[ regex "genau|exakt|präzise|ungefähr|(in )?etwa|nahe?( an)?|um( die)?|fast|rund|gut"
, dimension Distance
]
, prod = \case
(_:token:_) -> Just token
_ -> Nothing
}
ruleDistances :: [Rule]
ruleDistances = map go distances
where
go :: (Text, String, TDistance.Unit) -> Rule
go (name, regexPattern, u) = Rule
{ name = name
, pattern = [ dimension Distance, regex regexPattern ]
, prod = \case
(Token Distance dd:_) -> Just . Token Distance $ withUnit u dd
_ -> Nothing
}
ruleIntervalBetweenNumeral :: Rule
ruleIntervalBetweenNumeral = Rule
{ name = "between|from <numeral> to|and <dist>"
, pattern =
[ regex "zwischen|von"
, Predicate isPositive
, regex "bis|und"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Numeral NumeralData{TNumeral.value = from}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) | from < to ->
Just . Token Distance . withInterval (from, to) $ unitOnly u
_ -> Nothing
}
ruleIntervalBetween :: Rule
ruleIntervalBetween = Rule
{ name = "between|from <dist> to|and <dist>"
, pattern =
[ regex "zwischen|von"
, Predicate isSimpleDistance
, regex "und|bis"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
_) | from < to && u1 == u2 ->
Just . Token Distance . withInterval (from, to) $ unitOnly u1
_ -> Nothing
}
ruleIntervalNumeralDash :: Rule
ruleIntervalNumeralDash = Rule
{ name = "<numeral> - <dist>"
, pattern =
[ Predicate isPositive
, regex "-"
, Predicate isSimpleDistance
]
, prod = \case
(Token Numeral NumeralData{TNumeral.value = from}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) | from < to ->
Just . Token Distance . withInterval (from, to) $ unitOnly u
_ -> Nothing
}
ruleIntervalDash :: Rule
ruleIntervalDash = Rule
{ name = "<dist> - <dist>"
, pattern =
[ Predicate isSimpleDistance
, regex "-"
, Predicate isSimpleDistance
]
, prod = \case
(Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
_) | from < to && u1 == u2 ->
Just . Token Distance . withInterval (from, to) $ unitOnly u1
_ -> Nothing
}
ruleIntervalMax :: Rule
ruleIntervalMax = Rule
{ name = "under/less/lower/no more than <dist>"
, pattern =
[ regex "unter|höchstens|maximal|(weniger|nicht mehr) als"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) -> Just . Token Distance . withMax to $ unitOnly u
_ -> Nothing
}
ruleIntervalMin :: Rule
ruleIntervalMin = Rule
{ name = "over/above/at least/more than <dist>"
, pattern =
[ regex "über|(mehr|nicht weniger) als|mindestens|wenigstens|minimal"
, Predicate isSimpleDistance
]
, prod = \case
(_:
Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
_) -> Just . Token Distance . withMin to $ unitOnly u
_ -> Nothing
}
rules :: [Rule]
rules =
[ ruleIntervalBetweenNumeral
, ruleIntervalBetween
, ruleIntervalMax
, ruleIntervalMin
, ruleIntervalNumeralDash
, ruleIntervalDash
, rulePrecision
]
++ ruleDistances

View File

@ -7,8 +7,6 @@
{-# LANGUAGE GADTs #-}
module Duckling.Rules.DE
( defaultRules
, langRules
@ -16,14 +14,16 @@ module Duckling.Rules.DE
) where
import Duckling.Dimensions.Types
import Duckling.Locale
import Duckling.Types
import qualified Duckling.Distance.DE.Rules as Distance
import qualified Duckling.Duration.DE.Rules as Duration
import qualified Duckling.Email.DE.Rules as Email
import qualified Duckling.Ordinal.DE.Rules as Ordinal
import Duckling.Locale
import qualified Duckling.Numeral.DE.Rules as Numeral
import qualified Duckling.Ordinal.DE.Rules as Ordinal
import qualified Duckling.Time.DE.Rules as Time
import qualified Duckling.TimeGrain.DE.Rules as TimeGrain
import Duckling.Types
import qualified Duckling.Volume.DE.Rules as Volume
defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules
@ -35,7 +35,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = Email.rules
langRules (This Numeral) = Numeral.rules
@ -47,5 +47,5 @@ langRules (This Temperature) = []
langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules DE dim

View File

@ -0,0 +1,86 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Volume.DE.Corpus
( corpus ) where
import Data.String
import Prelude
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.Volume.Types
corpus :: Corpus
corpus = (testContext {locale = makeLocale DE Nothing}, testOptions, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (simple Litre 1)
[ "1 liter"
--, "ein liter"
]
, examples (simple Litre 2)
[ "2 liter"
, "2l"
]
, examples (simple Litre 1000)
[ "1000 liter"
, "tausend liter"
]
, examples (simple Litre 0.5)
[ "halber liter"
, "ein halber liter"
]
, examples (simple Litre 0.25)
[ "viertel liter"
, "ein viertel liter"
]
, examples (simple Millilitre 1)
[ "ein milliliter"
, "ein ml"
, "1ml"
]
, examples (simple Millilitre 250)
[ "250 milliliter"
, "250ml"
, "250 ml"
]
, examples (simple Hectolitre 3)
[ "3 hektoliter"
]
, examples (between Litre (100,1000))
[ "zwischen 100 und 1000 litern"
, "100-1000 liter"
, "von 100 bis 1000 l"
, "100 - 1000 l"
]
, examples (between Litre (2,7))
[ "etwa 2 -7 l"
, "~2-7 liter"
, "von 2 bis 7 l"
, "zwischen 2,0 l und ungefähr 7,0 l"
, "zwischen 2l und etwa 7l"
, "2 - ~7 liter"
]
, examples (under Hectolitre 2)
[ "nicht mehr als 2 hektoliter"
, "höchstens zwei hektoliter"
, "unter 2 hektolitern"
, "weniger als 2 hektoliter"
]
, examples (above Millilitre 4)
[ "mehr als 4 ml"
, "wenigstens 4,0 ml"
, "über vier milliliter"
, "mindestens vier ml"
]
]

166
Duckling/Volume/DE/Rules.hs Normal file
View File

@ -0,0 +1,166 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Volume.DE.Rules
( rules ) where
import Data.Text (Text)
import Prelude
import Data.String
import Duckling.Dimensions.Types
import Duckling.Types
import Duckling.Regex.Types
import Duckling.Volume.Helpers
import Duckling.Numeral.Helpers (isPositive)
import qualified Duckling.Volume.Types as TVolume
import qualified Duckling.Numeral.Types as TNumeral
volumes :: [(Text, String, TVolume.Unit)]
volumes = [ ("<latent vol> ml", "m(l|illiliter[ns]?)", TVolume.Millilitre)
, ("<vol> hectoliters", "hektoliter[ns]?", TVolume.Hectolitre)
, ("<vol> liters", "l(iter[ns]?)?", TVolume.Litre)
]
rulesVolumes :: [Rule]
rulesVolumes = map go volumes
where
go :: (Text, String, TVolume.Unit) -> Rule
go (name, regexPattern, u) = Rule
{ name = name
, pattern =
[ regex regexPattern
]
, prod = \_ -> Just . Token Volume $ unitOnly u
}
fractions :: [(Text, String, Double)]
fractions = [ ("one","ein(e[ns])?", 1)
, ("half", "(ein(e[ns])? )?halb(e[rn])? ", 1/2)
, ("third", "(ein(e[ns])? )?dritttel ", 1/3)
, ("fourth", "(ein(e[ns])? )?viertel ", 1/4)
, ("fifth", "(ein(e[ns])? )?fünftel ", 1/5)
, ("tenth", "(ein(e[ns])? )?zehntel ", 1/10)
]
rulesFractionalVolume :: [Rule]
rulesFractionalVolume = map go fractions
where
go :: (Text, String, Double) -> Rule
go (name, regexPattern, f) = Rule
{ name = name
, pattern =
[ regex regexPattern
, Predicate isUnitOnly
]
, prod = \case
(_:
Token Volume TVolume.VolumeData{TVolume.unit = Just u}:
_) ->
Just . Token Volume $ volume u f
_ -> Nothing
}
rulePrecision :: Rule
rulePrecision = Rule
{ name = "about <volume>"
, pattern =
[ regex "\\~|(ganz )?genau|präzise|(in )?etwa|ungefähr|um( die)?|fast"
, dimension Volume
]
, prod = \case
(_:token:_) -> Just token
_ -> Nothing
}
ruleIntervalBetweenNumeral :: Rule
ruleIntervalBetweenNumeral = Rule
{ name = "between|from <numeral> and|to <volume>"
, pattern =
[ regex "zwischen|von"
, Predicate isPositive
, regex "und|bis( zu)?"
, Predicate isSimpleVolume
]
, prod = \case
(_:
Token Numeral TNumeral.NumeralData{TNumeral.value = from}:
_:
Token Volume TVolume.VolumeData{TVolume.value = Just to
, TVolume.unit = Just u}:
_) | from < to ->
Just . Token Volume . withInterval (from, to) $ unitOnly u
_ -> Nothing
}
ruleIntervalBetween :: Rule
ruleIntervalBetween = Rule
{ name = "between|from <volume> to|and <volume>"
, pattern =
[ regex "zwischen|von"
, Predicate isSimpleVolume
, regex "bis( zu)?|und"
, Predicate isSimpleVolume
]
, prod = \case
(_:
Token Volume TVolume.VolumeData{TVolume.value = Just from
, TVolume.unit = Just u1}:
_:
Token Volume TVolume.VolumeData{TVolume.value = Just to
, TVolume.unit = Just u2}:
_) | from < to && u1 == u2 ->
Just . Token Volume . withInterval (from, to) $ unitOnly u1
_ -> Nothing
}
ruleIntervalMax :: Rule
ruleIntervalMax = Rule
{ name = "at most <volume>"
, pattern =
[ regex "unter|weniger( als)?|höchstens|nicht mehr als"
, Predicate isSimpleVolume
]
, prod = \case
(_:
Token Volume TVolume.VolumeData{TVolume.value = Just to
, TVolume.unit = Just u}:
_) ->
Just . Token Volume . withMax to $ unitOnly u
_ -> Nothing
}
ruleIntervalMin :: Rule
ruleIntervalMin = Rule
{ name = "more than <volume>"
, pattern =
[ regex "über|mindestens|wenigstens|mehr als|größer( als)?"
, Predicate isSimpleVolume
]
, prod = \case
(_:
Token Volume TVolume.VolumeData{TVolume.value = Just from
, TVolume.unit = Just u}:
_) ->
Just . Token Volume . withMin from $ unitOnly u
_ -> Nothing
}
rules :: [Rule]
rules = [ rulePrecision
, ruleIntervalBetweenNumeral
, ruleIntervalBetween
, ruleIntervalMax
, ruleIntervalMin
]
++ rulesVolumes
++ rulesFractionalVolume

View File

@ -272,6 +272,8 @@ library
, Duckling.Distance.BG.Rules
, Duckling.Distance.CS.Corpus
, Duckling.Distance.CS.Rules
, Duckling.Distance.DE.Corpus
, Duckling.Distance.DE.Rules
, Duckling.Distance.EN.Corpus
, Duckling.Distance.EN.Rules
, Duckling.Distance.ES.Corpus
@ -741,6 +743,8 @@ library
-- Volume
, Duckling.Volume.AR.Corpus
, Duckling.Volume.AR.Rules
, Duckling.Volume.DE.Corpus
, Duckling.Volume.DE.Rules
, Duckling.Volume.EN.Corpus
, Duckling.Volume.EN.Rules
, Duckling.Volume.ES.Corpus
@ -846,6 +850,7 @@ test-suite duckling-test
-- Distance
, Duckling.Distance.BG.Tests
, Duckling.Distance.CS.Tests
, Duckling.Distance.DE.Tests
, Duckling.Distance.EN.Tests
, Duckling.Distance.ES.Tests
, Duckling.Distance.FR.Tests
@ -890,6 +895,8 @@ test-suite duckling-test
-- Email
, Duckling.Email.EN.Tests
, Duckling.Email.DE.Tests
, Duckling.Email.IS.Tests
, Duckling.Email.FR.Tests
, Duckling.Email.IT.Tests
, Duckling.Email.Tests
@ -1045,6 +1052,7 @@ test-suite duckling-test
-- Volume
, Duckling.Volume.AR.Tests
, Duckling.Volume.DE.Tests
, Duckling.Volume.EN.Tests
, Duckling.Volume.ES.Tests
, Duckling.Volume.FR.Tests

View File

@ -0,0 +1,24 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
module Duckling.Distance.DE.Tests
( tests
) where
import Data.String
import Prelude
import Test.Tasty
import Duckling.Dimensions.Types
import Duckling.Distance.DE.Corpus
import Duckling.Testing.Asserts
tests :: TestTree
tests = testGroup "DE Tests"
[ makeCorpusTest [This Distance] corpus
]

View File

@ -16,6 +16,7 @@ import Test.Tasty
import qualified Duckling.Distance.BG.Tests as BG
import qualified Duckling.Distance.CS.Tests as CS
import qualified Duckling.Distance.DE.Tests as DE
import qualified Duckling.Distance.EN.Tests as EN
import qualified Duckling.Distance.ES.Tests as ES
import qualified Duckling.Distance.FR.Tests as FR
@ -37,6 +38,7 @@ tests :: TestTree
tests = testGroup "Distance Tests"
[ BG.tests
, CS.tests
, DE.tests
, EN.tests
, ES.tests
, FR.tests

View File

@ -0,0 +1,24 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
module Duckling.Volume.DE.Tests
( tests
) where
import Prelude
import Data.String
import Test.Tasty
import Duckling.Dimensions.Types
import Duckling.Testing.Asserts
import Duckling.Volume.DE.Corpus
tests :: TestTree
tests = testGroup "DE Tests"
[ makeCorpusTest [This Volume] corpus
]

View File

@ -15,6 +15,7 @@ import Prelude
import Test.Tasty
import qualified Duckling.Volume.AR.Tests as AR
import qualified Duckling.Volume.DE.Tests as DE
import qualified Duckling.Volume.EN.Tests as EN
import qualified Duckling.Volume.ES.Tests as ES
import qualified Duckling.Volume.FR.Tests as FR
@ -33,6 +34,7 @@ import qualified Duckling.Volume.TR.Tests as TR
tests :: TestTree
tests = testGroup "Volume Tests"
[ AR.tests
, DE.tests
, EN.tests
, ES.tests
, FR.tests