Skip to content

Commit

Permalink
feat: ordinal weekday recurrence (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
scroskey-clinc authored Jun 29, 2021
1 parent 4c04a18 commit 1709151
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 56 deletions.
55 changes: 52 additions & 3 deletions Duckling/Recurrence/EN/Corpus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,18 @@ examples :: ToJSON a => (Context -> a) -> [Text] -> [Example]
examples f = examplesCustom (check f)

recurrence :: Int -> Int -> Grain -> Context -> RecurrenceValue
recurrence v t g ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = Nothing}
recurrence v t g ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = Nothing, TRecurrence.rInnerGrain = Nothing, TRecurrence.rInnerDay = Nothing, TRecurrence.rInnerInstance = Nothing}

anchoredRecurrence :: Int -> Int -> Grain -> Datetime -> Grain -> Context -> RecurrenceValue
anchoredRecurrence v t g dt dtg ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = a}
anchoredRecurrence v t g dt dtg ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = a, TRecurrence.rInnerGrain = Nothing, TRecurrence.rInnerDay = Nothing, TRecurrence.rInnerInstance = Nothing}
where
a = Just $ datetime dt dtg ctx

instancedRecurrence :: Maybe Int -> Maybe Int -> Grain -> Maybe Grain -> Context -> RecurrenceValue
instancedRecurrence n d g ig ctx = RecurrenceValue{TRecurrence.rValue = 1, TRecurrence.rTimes = 1, TRecurrence.rGrain = g, TRecurrence.rAnchor = Nothing, TRecurrence.rInnerGrain = ig, TRecurrence.rInnerDay = d, TRecurrence.rInnerInstance = n}

anchoredRecurrenceHoliday :: Int -> Int -> Grain -> Datetime -> Grain -> Text -> Context -> RecurrenceValue
anchoredRecurrenceHoliday v t g dt dtg h ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = a}
anchoredRecurrenceHoliday v t g dt dtg h ctx = RecurrenceValue{TRecurrence.rValue = v, TRecurrence.rTimes = t, TRecurrence.rGrain = g, TRecurrence.rAnchor = a, TRecurrence.rInnerGrain = Nothing, TRecurrence.rInnerDay = Nothing, TRecurrence.rInnerInstance = Nothing}
where
a = Just $ datetimeHoliday dt dtg h ctx

Expand Down Expand Up @@ -174,4 +177,50 @@ allExamples = concat
, "biquarterly"
, "every two quarters"
]
, examples (instancedRecurrence (Just 3) (Just 6) Month (Just Week))
[ "3rd saturday of every month"
, "third saturday each month"
, "3rd saturday each month"
]
, examples (instancedRecurrence (Just 1) (Just 7) Year (Just Week))
[ "1st sunday of each year"
, "first sunday every year"
, "1st sunday per year"
]
, examples (instancedRecurrence (Just 2) (Just 1) Month (Just Week))
[ "second monday of every month"
, "2nd monday each month"
]
, examples (instancedRecurrence (Just 3) Nothing Year (Just Week))
[ "third week of every year"
, "3rd week each year"
]
, examples (instancedRecurrence (Just 5) Nothing Year (Just Month))
[ "fifth month each year"
, "5th month per year"
]
, examples (instancedRecurrence Nothing (Just 15) Month (Just Month))
[ "15th of every month"
]
, examples (instancedRecurrence (Just 15) Nothing Month (Just Day))
[ "15th day of every month"
]
, examples (instancedRecurrence (Just (- 1)) Nothing Month (Just Day))
[ "last day of every month"
]
, examples (instancedRecurrence (Just (- 1)) (Just 5) Month (Just Week))
[ "last friday of every month"
]
, examples (instancedRecurrence Nothing (Just 7) Week (Just Week))
[ "7th of every week"
]
, examples (instancedRecurrence Nothing (Just 31) Month (Just Month))
[ "31st of every month"
]
, examples (instancedRecurrence (Just 12) Nothing Year (Just Month))
[ "12th month of every year"
]
, examples (instancedRecurrence Nothing (Just 31) Year (Just Month))
[ "31st of every year"
]
]
88 changes: 86 additions & 2 deletions Duckling/Recurrence/EN/Rules.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ import Duckling.Recurrence.Helpers
recurrence,
recurrentDimension,
timedRecurrence,
dowRecurrence,
instancedRecurrence,
tr )
import Duckling.Duration.Types (DurationData (..))
import Duckling.Duration.Types (DurationData(..))
import Duckling.Recurrence.Types (RecurrenceData(..))
import Duckling.Numeral.Helpers (parseInt, parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Time.Helpers (getIntValue)
import Duckling.Ordinal.Types (OrdinalData(..))
import Duckling.Time.Helpers (getIntValue, isADayOfWeek)
import Duckling.Time.Types (TimeData(..))
import Duckling.Regex.Types ( GroupMatch(GroupMatch) )
import Duckling.Types
Expand All @@ -44,6 +47,7 @@ import Duckling.Types
Token(Token) )
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.Ordinal.Types as TOrdinal
import qualified Duckling.TimeGrain.Types as TG

ruleEvery :: Rule
Expand Down Expand Up @@ -244,6 +248,81 @@ ruleCompositeOnceAGrain = Rule
_ -> Nothing
}

ruleOrdinalDOWEveryGrain :: Rule
ruleOrdinalDOWEveryGrain = Rule
{ name = "<ordinal> <day-of-week> of every <grain>"
, pattern =
[ dimension Ordinal
, Predicate isADayOfWeek
, regex "(of |in )?(every|per|each)"
, dimension TimeGrain
]
, prod = \tokens -> case tokens of
(Token Ordinal OrdinalData{TOrdinal.value}:Token Time td:_:Token TimeGrain grain:_) ->
tr $ dowRecurrence value td grain TG.Week
_ -> Nothing
}

ruleLastDOWEveryGrain :: Rule
ruleLastDOWEveryGrain = Rule
{ name = "<ordinal> <day-of-week> of every <grain>"
, pattern =
[ regex "last|final"
, Predicate isADayOfWeek
, regex "(of |in )?(every|per|each)"
, dimension TimeGrain
]
, prod = \tokens -> case tokens of
(_:Token Time td:_:Token TimeGrain grain:_) ->
tr $ dowRecurrence (- 1) td grain TG.Week
_ -> Nothing
}

ruleOrdinalEveryGrain :: Rule
ruleOrdinalEveryGrain = Rule
{ name = "<ordinal> of every <grain>"
, pattern =
[ dimension Ordinal
, regex "(of |in )?(every|per|each)"
, dimension TimeGrain
]
, prod = \tokens -> case tokens of
(Token Ordinal OrdinalData{TOrdinal.value}:_:Token TimeGrain grain:_) -> case grain of
TG.Week -> tr $ instancedRecurrence Nothing (Just value) grain TG.Week
_ -> tr $ instancedRecurrence Nothing (Just value) grain TG.Month
_ -> Nothing
}

ruleOrdinalGrainEveryGrain :: Rule
ruleOrdinalGrainEveryGrain = Rule
{ name = "<ordinal> <grain> of every <grain>"
, pattern =
[ dimension Ordinal
, dimension TimeGrain
, regex "(of |in )?(every|per|each)"
, dimension TimeGrain
]
, prod = \tokens -> case tokens of
(Token Ordinal OrdinalData{TOrdinal.value}:Token TimeGrain innerGrain:_:Token TimeGrain outerGrain:_) ->
tr $ instancedRecurrence (Just value) Nothing outerGrain innerGrain
_ -> Nothing
}

ruleLastGrainEveryGrain :: Rule
ruleLastGrainEveryGrain = Rule
{ name = "last <grain> of every <grain>"
, pattern =
[ regex "last|final"
, dimension TimeGrain
, regex "(of |in )?(every|per|each)"
, dimension TimeGrain
]
, prod = \tokens -> case tokens of
(_:Token TimeGrain innerGrain:_:Token TimeGrain outerGrain:_) ->
tr $ instancedRecurrence (Just (- 1)) Nothing outerGrain innerGrain
_ -> Nothing
}

rules :: [Rule]
rules =
[ ruleEvery
Expand All @@ -258,4 +337,9 @@ rules =
, ruleCompositeOnce
, ruleCompositeTimesAGrain
, ruleCompositeOnceAGrain
, ruleOrdinalDOWEveryGrain
, ruleLastDOWEveryGrain
, ruleOrdinalEveryGrain
, ruleOrdinalGrainEveryGrain
, ruleLastGrainEveryGrain
]
16 changes: 12 additions & 4 deletions Duckling/Recurrence/Helpers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module Duckling.Recurrence.Helpers
, recurrentDimension
, isBasicRecurrence
, mkComposite
, dowRecurrence
, instancedRecurrence
, tr
) where

Expand Down Expand Up @@ -58,13 +60,19 @@ tr :: RecurrenceData -> Maybe Token
tr = Just . Token Recurrence

recurrence :: TG.Grain -> Int -> RecurrenceData
recurrence g v = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Nothing, TRecurrence.times = 1, TRecurrence.composite = False}
recurrence g v = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Nothing, TRecurrence.times = 1, TRecurrence.composite = False, TRecurrence.innerGrain = Nothing, TRecurrence.innerDay = Nothing,TRecurrence.innerInstance = Nothing}

timedRecurrence :: TG.Grain -> Int -> Int -> RecurrenceData
timedRecurrence g v t = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Nothing, TRecurrence.times = t, TRecurrence.composite = False}
timedRecurrence g v t = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Nothing, TRecurrence.times = t, TRecurrence.composite = False, TRecurrence.innerGrain = Nothing, TRecurrence.innerDay = Nothing,TRecurrence.innerInstance = Nothing}

anchoredRecurrence :: TG.Grain -> Int -> TimeData -> RecurrenceData
anchoredRecurrence g v a = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Just a, TRecurrence.times = 1, TRecurrence.composite = False}
anchoredRecurrence g v a = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = Just a, TRecurrence.times = 1, TRecurrence.composite = False, TRecurrence.innerGrain = Nothing, TRecurrence.innerDay = Nothing, TRecurrence.innerInstance = Nothing}

mkComposite :: RecurrenceData -> Int -> RecurrenceData
mkComposite RecurrenceData { TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = a, TRecurrence.times = t } t' = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = a, TRecurrence.times = t * t', TRecurrence.composite = True}
mkComposite RecurrenceData { TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = a, TRecurrence.times = t, TRecurrence.innerGrain = w, TRecurrence.innerInstance = wn} t' = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = v, TRecurrence.anchor = a, TRecurrence.times = t * t', TRecurrence.composite = True, TRecurrence.innerGrain = w, TRecurrence.innerDay = Nothing, TRecurrence.innerInstance = wn}

dowRecurrence :: Int -> TimeData -> TG.Grain -> TG.Grain -> RecurrenceData
dowRecurrence wn wd g ig = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = 1, TRecurrence.anchor = Just wd, TRecurrence.times = 1, TRecurrence.composite = False, TRecurrence.innerGrain = Just ig, TRecurrence.innerDay = Nothing, TRecurrence.innerInstance = Just wn}

instancedRecurrence :: Maybe Int -> Maybe Int -> TG.Grain -> TG.Grain -> RecurrenceData
instancedRecurrence it is g ig = RecurrenceData {TRecurrence.grain = g, TRecurrence.value = 1, TRecurrence.anchor = Nothing, TRecurrence.times = 1, TRecurrence.composite = False, TRecurrence.innerGrain = Just ig, TRecurrence.innerDay = is, TRecurrence.innerInstance = it}
Loading

0 comments on commit 1709151

Please sign in to comment.