From d52c2f2b9a10a346f72add814bb457703ee95374 Mon Sep 17 00:00:00 2001 From: Jayanth-Parthsarathy Date: Wed, 9 Oct 2024 16:52:04 +0530 Subject: [PATCH] refactor: restructure multimodal module --- lib/mobility-core/mobility-core.cabal | 11 +-- .../External/Maps/Google/MapsClient/Types.hs | 3 +- .../src/Kernel/External/Maps/Interface.hs | 15 ---- .../Kernel/External/Maps/Interface/Google.hs | 29 -------- .../Kernel/External/Maps/Interface/Types.hs | 18 ----- .../src/Kernel/External/MultiModal.hs | 17 +++++ .../External/MultiModal/Common/Polyline.hs | 31 -------- .../Kernel/External/MultiModal/Interface.hs | 42 +++++++++++ .../External/MultiModal/Interface/Google.hs | 41 +++++++++++ .../Interface/OpenTripPlanner.hs | 27 ++++--- .../External/MultiModal/Interface/Types.hs | 70 +++++++++++++++++++ .../OpenTripPlanner/Config.hs | 2 +- .../OpenTripPlanner/Types.hs | 2 +- .../{ => OpenTripPlanner}/query.gql | 0 .../{ => OpenTripPlanner}/schema.gql | 0 .../src/Kernel/External/MultiModal/Types.hs | 53 ++++---------- .../src/Kernel/External/MultiModal/Utils.hs | 41 +++++++++-- 17 files changed, 241 insertions(+), 161 deletions(-) create mode 100644 lib/mobility-core/src/Kernel/External/MultiModal.hs delete mode 100644 lib/mobility-core/src/Kernel/External/MultiModal/Common/Polyline.hs create mode 100644 lib/mobility-core/src/Kernel/External/MultiModal/Interface.hs create mode 100644 lib/mobility-core/src/Kernel/External/MultiModal/Interface/Google.hs rename lib/mobility-core/src/Kernel/External/{Maps => MultiModal}/Interface/OpenTripPlanner.hs (60%) create mode 100644 lib/mobility-core/src/Kernel/External/MultiModal/Interface/Types.hs rename lib/mobility-core/src/Kernel/External/{Maps => MultiModal}/OpenTripPlanner/Config.hs (62%) rename lib/mobility-core/src/Kernel/External/{Maps => MultiModal}/OpenTripPlanner/Types.hs (99%) rename lib/mobility-core/src/Kernel/External/MultiModal/{ => OpenTripPlanner}/query.gql (100%) rename lib/mobility-core/src/Kernel/External/MultiModal/{ => OpenTripPlanner}/schema.gql (100%) diff --git a/lib/mobility-core/mobility-core.cabal b/lib/mobility-core/mobility-core.cabal index 07ba1ba5..a7228461 100644 --- a/lib/mobility-core/mobility-core.cabal +++ b/lib/mobility-core/mobility-core.cabal @@ -88,7 +88,6 @@ library Kernel.External.Maps.Interface.Google Kernel.External.Maps.Interface.MMI Kernel.External.Maps.Interface.NextBillion - Kernel.External.Maps.Interface.OpenTripPlanner Kernel.External.Maps.Interface.OSRM Kernel.External.Maps.Interface.Types Kernel.External.Maps.MMI.AutoSuggest @@ -104,13 +103,17 @@ library Kernel.External.Maps.NextBillion.Config Kernel.External.Maps.NextBillion.Route Kernel.External.Maps.NextBillion.Types - Kernel.External.Maps.OpenTripPlanner.Config - Kernel.External.Maps.OpenTripPlanner.Types Kernel.External.Maps.OSRM.Config Kernel.External.Maps.OSRM.RoadsClient Kernel.External.Maps.Types Kernel.External.Maps.Utils - Kernel.External.MultiModal.Common.Polyline + Kernel.External.MultiModal + Kernel.External.MultiModal.Interface + Kernel.External.MultiModal.Interface.Google + Kernel.External.MultiModal.Interface.OpenTripPlanner + Kernel.External.MultiModal.Interface.Types + Kernel.External.MultiModal.OpenTripPlanner.Config + Kernel.External.MultiModal.OpenTripPlanner.Types Kernel.External.MultiModal.Types Kernel.External.MultiModal.Utils Kernel.External.Notification diff --git a/lib/mobility-core/src/Kernel/External/Maps/Google/MapsClient/Types.hs b/lib/mobility-core/src/Kernel/External/Maps/Google/MapsClient/Types.hs index 6e23b483..94b1482b 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/Google/MapsClient/Types.hs +++ b/lib/mobility-core/src/Kernel/External/Maps/Google/MapsClient/Types.hs @@ -197,7 +197,6 @@ data TransitDirectionsReq = TransitDirectionsReq arrivalTime :: Maybe String, -- yyyy-mm-ddThh:mm:ssZ departureTime :: Maybe String } - -----------remove null fields----------- deriving (Generic, ToJSON, FromJSON, ToSchema) data AdvancedDirectionsReq = AdvancedDirectionsReq @@ -351,7 +350,7 @@ instance ToJSON VehicleV2 where newtype Polyline = Polyline { encodedPolyline :: PolyLinePoints } - deriving (Generic, ToJSON, FromJSON, ToSchema) + deriving (Generic, ToJSON, FromJSON, ToSchema, Show) newtype EncodedPointObject = EncodedPointObject {points :: PolyLinePoints} deriving stock (Generic) diff --git a/lib/mobility-core/src/Kernel/External/Maps/Interface.hs b/lib/mobility-core/src/Kernel/External/Maps/Interface.hs index 9340d8e2..7e73aa5b 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/Interface.hs +++ b/lib/mobility-core/src/Kernel/External/Maps/Interface.hs @@ -19,7 +19,6 @@ module Kernel.External.Maps.Interface getDistances, getRoutesProvided, getRoutes, - getTransitRoutes, snapToRoadProvided, snapToRoad, snapToRoadWithFallback, @@ -39,12 +38,10 @@ import qualified Kernel.External.Maps.Interface.Google as Google import qualified Kernel.External.Maps.Interface.MMI as MMI import qualified Kernel.External.Maps.Interface.NextBillion as NextBillion import qualified Kernel.External.Maps.Interface.OSRM as OSRM -import qualified Kernel.External.Maps.Interface.OpenTripPlanner as OTP import Kernel.External.Maps.Interface.Types as Reexport import Kernel.External.Maps.MMI.Config as Reexport import Kernel.External.Maps.OSRM.Config as Reexport import Kernel.External.Maps.Types as Reexport -import Kernel.External.MultiModal.Types import Kernel.Prelude import Kernel.Storage.Hedis as Redis import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics) @@ -128,18 +125,6 @@ getRoutes isAvoidToll serviceConfig req = case serviceConfig of MMIConfig cfg -> MMI.getRoutes cfg req NextBillionConfig cfg -> NextBillion.getRoutes cfg req -getTransitRoutes :: - ( EncFlow m r, - CoreMetrics m, - Log m - ) => - TransitServiceConfig -> - GetTransitRoutesReq -> - m (Maybe MultiModalResponse) -getTransitRoutes serviceConfig req = case serviceConfig of - GoogleTransit cfg -> Google.getTransitRoutes cfg req - OTPTransit cfg -> OTP.getTransitRoutes cfg req - snapToRoadProvided :: MapsService -> Bool snapToRoadProvided = \case Google -> True diff --git a/lib/mobility-core/src/Kernel/External/Maps/Interface/Google.hs b/lib/mobility-core/src/Kernel/External/Maps/Interface/Google.hs index 2d8c53e5..b787afa9 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/Interface/Google.hs +++ b/lib/mobility-core/src/Kernel/External/Maps/Interface/Google.hs @@ -16,7 +16,6 @@ module Kernel.External.Maps.Interface.Google ( module Reexport, getDistances, getRoutes, - getTransitRoutes, snapToRoad, autoComplete, getPlaceDetails, @@ -39,8 +38,6 @@ import qualified Kernel.External.Maps.Google.RoadsClient as GoogleRoads import Kernel.External.Maps.HasCoordinates as Reexport (HasCoordinates (..)) import Kernel.External.Maps.Interface.Types as Types import Kernel.External.Maps.Types as Reexport -import qualified Kernel.External.MultiModal.Types as MultiModalTypes -import Kernel.External.MultiModal.Utils import Kernel.Prelude import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics) import Kernel.Types.Common hiding (id) @@ -186,32 +183,6 @@ getRoutes isAvoidToll cfg req = do BICYCLE -> GoogleMaps.BICYCLE MOTORCYCLE -> GoogleMaps.TWO_WHEELER -getTransitRoutes :: - ( EncFlow m r, - CoreMetrics m, - Log m - ) => - GoogleCfg -> - GetTransitRoutesReq -> - m (Maybe MultiModalTypes.MultiModalResponse) -getTransitRoutes cfg req = do - key <- decrypt cfg.googleKey - let googleMapsUrl = cfg.googleRouteConfig.url - computeAlternativeRoutes = cfg.googleRouteConfig.computeAlternativeRoutes - routePreference = cfg.googleRouteConfig.routePreference - origin = req.origin - destination = req.destination - travelMode = req.mode - arrivalTime = req.arrivalTime - departureTime = req.departureTime - transitPreferences = req.transitPreferences - result <- try @_ @SomeException $ GoogleMaps.transitDirectionsAPI googleMapsUrl key origin destination travelMode computeAlternativeRoutes routePreference transitPreferences arrivalTime departureTime - case result of - Right gRes -> do - pure $ Just $ convertGoogleToGeneric gRes - Left _ -> do - pure Nothing - mkRoute' :: (MonadFlow m) => GetRoutesReqProxy -> diff --git a/lib/mobility-core/src/Kernel/External/Maps/Interface/Types.hs b/lib/mobility-core/src/Kernel/External/Maps/Interface/Types.hs index 65a8948e..bd0a2f31 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/Interface/Types.hs +++ b/lib/mobility-core/src/Kernel/External/Maps/Interface/Types.hs @@ -41,12 +41,9 @@ import qualified Debug.Trace as T import Deriving.Aeson import EulerHS.Prelude import qualified Kernel.External.Maps.Google.Config as Google -import qualified Kernel.External.Maps.Google.MapsClient.Types as GT import qualified Kernel.External.Maps.MMI.Config as MMI import qualified Kernel.External.Maps.NextBillion.Config as NextBillion import qualified Kernel.External.Maps.OSRM.Config as OSRM -import qualified Kernel.External.Maps.OpenTripPlanner.Config as OTP -import qualified Kernel.External.Maps.OpenTripPlanner.Types as OTPTypes import Kernel.External.Maps.Types import Kernel.External.Types (Language) import Kernel.Types.Common @@ -62,10 +59,6 @@ data MapsServiceConfig = GoogleConfig Google.GoogleCfg | OSRMConfig OSRM.OSRMCfg deriving stock (Show, Eq, Generic) deriving (FromJSON, ToJSON) via CustomJSON '[SumTaggedObject "tag" "content"] MapsServiceConfig -data TransitServiceConfig = GoogleTransit Google.GoogleCfg | OTPTransit OTP.OTPCfg - deriving stock (Show, Eq, Generic) - deriving (FromJSON, ToJSON) via CustomJSON '[SumTaggedObject "tag" "content"] TransitServiceConfig - data TravelMode = CAR | MOTORCYCLE | BICYCLE | FOOT deriving (Show, Eq, Generic, ToJSON, FromJSON, ToSchema) @@ -106,17 +99,6 @@ data GetRoutesReq = GetRoutesReq } deriving (Generic, ToJSON, FromJSON, Show, ToSchema) -data GetTransitRoutesReq = GetTransitRoutesReq - { origin :: GT.WayPointV2, - destination :: GT.WayPointV2, - arrivalTime :: Maybe String, - departureTime :: Maybe String, - mode :: Maybe GT.ModeV2, - transitPreferences :: Maybe GT.TransitPreferencesV2, - transportModes :: Maybe [Maybe OTPTypes.TransportMode] - } - deriving (Generic, ToJSON, FromJSON, Show, ToSchema) - data GetRoutesReqProxy = GetRoutesReqProxy { waypoints :: [LatLong], origin :: LatLong, diff --git a/lib/mobility-core/src/Kernel/External/MultiModal.hs b/lib/mobility-core/src/Kernel/External/MultiModal.hs new file mode 100644 index 00000000..c5678c80 --- /dev/null +++ b/lib/mobility-core/src/Kernel/External/MultiModal.hs @@ -0,0 +1,17 @@ +{- + Copyright 2022-23, Juspay India Pvt Ltd + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License + + as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is + + distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero + + General Public License along with this program. If not, see . +-} + +module Kernel.External.MultiModal (module Reexport) where + +import Kernel.External.MultiModal.Interface as Reexport diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Common/Polyline.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Common/Polyline.hs deleted file mode 100644 index 6eeb0496..00000000 --- a/lib/mobility-core/src/Kernel/External/MultiModal/Common/Polyline.hs +++ /dev/null @@ -1,31 +0,0 @@ -module Kernel.External.MultiModal.Common.Polyline (encode, decode) where - -import qualified Data.Text as T -import qualified Data.Text.Encoding as TE -import Kernel.External.Maps.Google.MapsClient.Types as GT -import Kernel.External.Maps.Google.PolyLinePoints (oneCoordEnc, stringToCoords) -import Kernel.Prelude - -makePairs :: [Double] -> [GT.LatLngV2] -makePairs (d1 : d2 : ds) = GT.LatLngV2 d1 d2 : makePairs ds -makePairs [] = [] -makePairs _ = [] - -catPairs :: [GT.LatLngV2] -> [Double] -catPairs [] = [] -catPairs (GT.LatLngV2 a b : xs) = a : b : catPairs xs - -addPair :: GT.LatLngV2 -> GT.LatLngV2 -> GT.LatLngV2 -addPair (GT.LatLngV2 x1 y1) (GT.LatLngV2 x2 y2) = GT.LatLngV2 (x1 + x2) (y1 + y2) - -subPair :: GT.LatLngV2 -> GT.LatLngV2 -> GT.LatLngV2 -subPair (GT.LatLngV2 x1 y1) (GT.LatLngV2 x2 y2) = GT.LatLngV2 (x1 - x2) (y1 - y2) - -adjDiff :: [GT.LatLngV2] -> [GT.LatLngV2] -adjDiff p = zipWith subPair p (GT.LatLngV2 0 0 : p) - -decode :: T.Text -> [GT.LatLngV2] -decode = scanl1 addPair . makePairs . stringToCoords . TE.encodeUtf8 - -encode :: [GT.LatLngV2] -> T.Text -encode = T.concat . fmap oneCoordEnc . catPairs . adjDiff diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Interface.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Interface.hs new file mode 100644 index 00000000..94a867a0 --- /dev/null +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Interface.hs @@ -0,0 +1,42 @@ +module Kernel.External.MultiModal.Interface + ( module Reexport, + getTransitRoutesProvided, + getTransitRoutes, + ) +where + +import Kernel.External.Maps.Google.Config as Reexport +import qualified Kernel.External.MultiModal.Interface.Google as Google +import qualified Kernel.External.MultiModal.Interface.OpenTripPlanner as OTP +import Kernel.External.MultiModal.Interface.Types as Reexport +import Kernel.External.MultiModal.OpenTripPlanner.Config as Reexport +import Kernel.External.MultiModal.Types +import Kernel.External.MultiModal.Utils as Reexport +import Kernel.Prelude +import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics) +import Kernel.Types.Common hiding (id) + +-- To be used in future when we add another provider +-- mkNotProvidedError :: Text -> MultiModalService -> Text +-- mkNotProvidedError functionName serviceName = "Function " <> functionName <> " is not provided by service " <> show serviceName +-- +-- throwNotProvidedError :: (MonadFlow m) => Text -> MultiModalService -> m a +-- throwNotProvidedError = +-- (throwError . InternalError) ... mkNotProvidedError + +getTransitRoutesProvided :: MultiModalService -> Bool +getTransitRoutesProvided = \case + GoogleTransit -> True + OTPTransit -> True + +getTransitRoutes :: + ( EncFlow m r, + CoreMetrics m, + Log m + ) => + MultiModalServiceConfig -> + GetTransitRoutesReq -> + m (Maybe MultiModalResponse) +getTransitRoutes serviceConfig req = case serviceConfig of + GoogleTransitConfig cfg -> Google.getTransitRoutes cfg req + OTPTransitConfig cfg -> OTP.getTransitRoutes cfg req diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Google.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Google.hs new file mode 100644 index 00000000..61f871d4 --- /dev/null +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Google.hs @@ -0,0 +1,41 @@ +module Kernel.External.MultiModal.Interface.Google (getTransitRoutes) where + +import Data.Time +import Kernel.External.Encryption +import Kernel.External.Maps.Google.Config +import qualified Kernel.External.Maps.Google.MapsClient as GoogleMaps +import Kernel.External.MultiModal.Interface.Types as MultiModalTypes +import Kernel.External.MultiModal.Utils +import Kernel.Prelude +import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics) +import Kernel.Types.Common hiding (id) + +formatUtcTime :: Maybe UTCTime -> Maybe String +formatUtcTime Nothing = Nothing +formatUtcTime (Just utcTime) = Just $ formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%SZ" utcTime + +getTransitRoutes :: + ( EncFlow m r, + CoreMetrics m, + Log m + ) => + GoogleCfg -> + MultiModalTypes.GetTransitRoutesReq -> + m (Maybe MultiModalTypes.MultiModalResponse) +getTransitRoutes cfg req = do + key <- decrypt cfg.googleKey + let googleMapsUrl = cfg.googleRouteConfig.url + computeAlternativeRoutes = cfg.googleRouteConfig.computeAlternativeRoutes + routePreference = cfg.googleRouteConfig.routePreference + origin = req.origin + destination = req.destination + travelMode = req.mode + arrivalTime = formatUtcTime req.arrivalTime + departureTime = formatUtcTime req.departureTime + transitPreferences = req.transitPreferences + result <- try @_ @SomeException $ GoogleMaps.transitDirectionsAPI googleMapsUrl key origin destination travelMode computeAlternativeRoutes routePreference transitPreferences arrivalTime departureTime + case result of + Right gRes -> do + pure $ Just $ convertGoogleToGeneric gRes + Left _ -> do + pure Nothing diff --git a/lib/mobility-core/src/Kernel/External/Maps/Interface/OpenTripPlanner.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/OpenTripPlanner.hs similarity index 60% rename from lib/mobility-core/src/Kernel/External/Maps/Interface/OpenTripPlanner.hs rename to lib/mobility-core/src/Kernel/External/MultiModal/Interface/OpenTripPlanner.hs index e8243b15..c16de3a5 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/Interface/OpenTripPlanner.hs +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/OpenTripPlanner.hs @@ -1,26 +1,23 @@ -module Kernel.External.Maps.Interface.OpenTripPlanner (getTransitRoutes) where +module Kernel.External.MultiModal.Interface.OpenTripPlanner (getTransitRoutes) where import Data.Morpheus.Client ( request, single, ) -import Data.Time.Format (defaultTimeLocale, formatTime, parseTimeM) +import Data.Time.Format (defaultTimeLocale, formatTime) import EulerHS.Prelude hiding (id, product) -import Kernel.External.Encryption -import qualified Kernel.External.Maps.Interface.Types as TP -import Kernel.External.Maps.OpenTripPlanner.Config -import Kernel.External.Maps.OpenTripPlanner.Types -import Kernel.External.MultiModal.Types +import qualified Kernel.External.MultiModal.Interface.Types as TP +import Kernel.External.MultiModal.OpenTripPlanner.Config +import Kernel.External.MultiModal.OpenTripPlanner.Types import Kernel.External.MultiModal.Utils import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics) import Kernel.Utils.Common hiding (id) -convertDateTime :: String -> Maybe (String, String) -convertDateTime input = do - time <- parseTimeM True defaultTimeLocale "%Y-%m-%dT%H:%M:%SZ" input :: Maybe UTCTime - let dateStr = formatTime defaultTimeLocale "%Y-%m-%d" time - let timeStr = formatTime defaultTimeLocale "%H:%M" time - return (dateStr, timeStr) +formatUtcDateTime :: UTCTime -> (String, String) +formatUtcDateTime utcTime = (dateString, timeString) + where + dateString = formatTime defaultTimeLocale "%Y-%m-%d" utcTime + timeString = formatTime defaultTimeLocale "%H:%M" utcTime getTransitRoutes :: ( EncFlow m r, @@ -29,7 +26,7 @@ getTransitRoutes :: ) => OTPCfg -> TP.GetTransitRoutesReq -> - m (Maybe MultiModalResponse) + m (Maybe TP.MultiModalResponse) getTransitRoutes cfg req = do let origin = InputCoordinates @@ -41,7 +38,7 @@ getTransitRoutes cfg req = do { lat = req.destination.location.latLng.latitude, lon = req.destination.location.latLng.longitude } - let dateTime = req.arrivalTime >>= convertDateTime + let dateTime = req.departureTime <&> formatUtcDateTime let planClient = fromString cfg.baseUrl let transportModes' = req.transportModes resp <- diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Types.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Types.hs new file mode 100644 index 00000000..02c60d27 --- /dev/null +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Interface/Types.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module Kernel.External.MultiModal.Interface.Types where + +import Data.OpenApi hiding (name) +import Data.Time (UTCTime) +import Deriving.Aeson +import EulerHS.Prelude +import qualified Kernel.External.Maps.Google.Config as Google +import qualified Kernel.External.Maps.Google.MapsClient.Types as GT +import qualified Kernel.External.MultiModal.OpenTripPlanner.Config as OTP +import qualified Kernel.External.MultiModal.OpenTripPlanner.Types as OTPTypes + +newtype MultiModalResponse = MultiModalResponse {routes :: [MultiModalRoute]} + deriving (Show, Generic) + +data MultiModalRoute = MultiModalRoute + { distance :: Double, + duration :: Int, + legs :: [MultiModalLeg] + } + deriving (Show, Generic) + +data MultiModalStopDetails = MultiModalStopDetails + { stopCode :: Maybe String, + name :: Maybe Text + } + deriving (Show, Generic) + +data MultiModalAgency = MultiModalAgency + { gtfsId :: Maybe Text, + name :: Text + } + deriving (Show, Generic) + +data MultiModalLeg = MultiModalLeg + { distance :: Double, + duration :: Double, + polyline :: GT.Polyline, + mode :: String, + startLocation :: GT.LocationV2, + endLocation :: GT.LocationV2, + fromStopDetails :: Maybe MultiModalStopDetails, + toStopDetails :: Maybe MultiModalStopDetails, + agency :: Maybe MultiModalAgency, + fromArrivalTime :: Maybe UTCTime, + fromDepartureTime :: Maybe UTCTime, + toArrivalTime :: Maybe UTCTime, + toDepartureTime :: Maybe UTCTime + } + deriving (Show, Generic) + +data MultiModalServiceConfig = GoogleTransitConfig Google.GoogleCfg | OTPTransitConfig OTP.OTPCfg + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) via CustomJSON '[SumTaggedObject "tag" "content"] MultiModalServiceConfig + +data GetTransitRoutesReq = GetTransitRoutesReq + { origin :: GT.WayPointV2, + destination :: GT.WayPointV2, + arrivalTime :: Maybe UTCTime, + departureTime :: Maybe UTCTime, + mode :: Maybe GT.ModeV2, + transitPreferences :: Maybe GT.TransitPreferencesV2, + transportModes :: Maybe [Maybe OTPTypes.TransportMode] + } + deriving (Generic, ToJSON, FromJSON, Show, ToSchema) diff --git a/lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Config.hs b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Config.hs similarity index 62% rename from lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Config.hs rename to lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Config.hs index 0f243a33..1e3cd01f 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Config.hs +++ b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Config.hs @@ -1,4 +1,4 @@ -module Kernel.External.Maps.OpenTripPlanner.Config where +module Kernel.External.MultiModal.OpenTripPlanner.Config (OTPCfg) where import Kernel.Prelude diff --git a/lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Types.hs b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Types.hs similarity index 99% rename from lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Types.hs rename to lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Types.hs index d42c9603..a53c506c 100644 --- a/lib/mobility-core/src/Kernel/External/Maps/OpenTripPlanner/Types.hs +++ b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/Types.hs @@ -5,7 +5,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} -module Kernel.External.Maps.OpenTripPlanner.Types where +module Kernel.External.MultiModal.OpenTripPlanner.Types where import Data.Morpheus.Client.CodeGen.Internal import EulerHS.Prelude hiding (id, product) diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/query.gql b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/query.gql similarity index 100% rename from lib/mobility-core/src/Kernel/External/MultiModal/query.gql rename to lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/query.gql diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/schema.gql b/lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/schema.gql similarity index 100% rename from lib/mobility-core/src/Kernel/External/MultiModal/schema.gql rename to lib/mobility-core/src/Kernel/External/MultiModal/OpenTripPlanner/schema.gql diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Types.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Types.hs index 491570f0..8c16712d 100644 --- a/lib/mobility-core/src/Kernel/External/MultiModal/Types.hs +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Types.hs @@ -1,47 +1,24 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE TemplateHaskell #-} -module Kernel.External.MultiModal.Types where +module Kernel.External.MultiModal.Types + ( module Kernel.External.MultiModal.Types, + ) +where -import Data.Time (UTCTime) +import Data.OpenApi hiding (name) import EulerHS.Prelude -import qualified Kernel.External.Maps.Google.MapsClient.Types as GT +import Kernel.Beam.Lib.UtilsTH (mkBeamInstancesForEnumAndList) +import Kernel.Storage.Esqueleto (derivePersistField) -newtype MultiModalResponse = MultiModalResponse {routes :: [MultiModalRoute]} - deriving (Generic) +data MultiModalService = GoogleTransit | OTPTransit + deriving (Show, Read, Eq, Ord, Generic, ToJSON, FromJSON, ToSchema) -data MultiModalRoute = MultiModalRoute - { distance :: Double, - duration :: Int, - legs :: [MultiModalLeg] - } - deriving (Generic) +$(mkBeamInstancesForEnumAndList ''MultiModalService) -data MutliModalStopDetails = MultiModalStopDetails - { stopCode :: Maybe String, - name :: Maybe Text - } - deriving (Show, Generic) +availableMultiModalService :: [MultiModalService] +availableMultiModalService = [GoogleTransit, OTPTransit] -data MultiModalAgency = MultiModalAgency - { gtfsId :: Maybe Text, - name :: Text - } - deriving (Generic) - -data MultiModalLeg = MultiModalLeg - { distance :: Double, - duration :: Double, - polyline :: GT.Polyline, - mode :: String, - startLocation :: GT.LocationV2, - endLocation :: GT.LocationV2, - fromStopDetails :: Maybe MutliModalStopDetails, - toStopDetails :: Maybe MutliModalStopDetails, - agency :: Maybe MultiModalAgency, - fromArrivalTime :: Maybe UTCTime, - fromDepartureTime :: Maybe UTCTime, - toArrivalTime :: Maybe UTCTime, - toDepartureTime :: Maybe UTCTime - } - deriving (Generic) +derivePersistField "MultiModalService" diff --git a/lib/mobility-core/src/Kernel/External/MultiModal/Utils.hs b/lib/mobility-core/src/Kernel/External/MultiModal/Utils.hs index 11f418a3..620ed448 100644 --- a/lib/mobility-core/src/Kernel/External/MultiModal/Utils.hs +++ b/lib/mobility-core/src/Kernel/External/MultiModal/Utils.hs @@ -1,22 +1,49 @@ module Kernel.External.MultiModal.Utils ( convertGoogleToGeneric, convertOTPToGeneric, + decode, + encode, ) where import qualified Data.Char as Char import qualified Data.Text as T -import EulerHS.Prelude -import qualified Kernel.External.Maps.Google.MapsClient.Types as GT -import qualified Kernel.External.Maps.OpenTripPlanner.Types as OTP -import qualified Kernel.External.MultiModal.Common.Polyline as GP -import Kernel.External.MultiModal.Types -import Kernel.Prelude (read) +import qualified Data.Text.Encoding as TE +import EulerHS.Prelude (safeHead) +import Kernel.External.Maps.Google.MapsClient.Types as GT +import Kernel.External.Maps.Google.PolyLinePoints (oneCoordEnc, stringToCoords) +import Kernel.External.MultiModal.Interface.Types +import qualified Kernel.External.MultiModal.OpenTripPlanner.Types as OTP +import Kernel.Prelude import Kernel.Utils.Time (millisecondsToUTC, parseISO8601UTC) extractDuration :: T.Text -> Int extractDuration t = read (filter Char.isDigit (T.unpack t)) :: Int +makePairs :: [Double] -> [GT.LatLngV2] +makePairs (d1 : d2 : ds) = GT.LatLngV2 d1 d2 : makePairs ds +makePairs [] = [] +makePairs _ = [] + +catPairs :: [GT.LatLngV2] -> [Double] +catPairs [] = [] +catPairs (GT.LatLngV2 a b : xs) = a : b : catPairs xs + +addPair :: GT.LatLngV2 -> GT.LatLngV2 -> GT.LatLngV2 +addPair (GT.LatLngV2 x1 y1) (GT.LatLngV2 x2 y2) = GT.LatLngV2 (x1 + x2) (y1 + y2) + +subPair :: GT.LatLngV2 -> GT.LatLngV2 -> GT.LatLngV2 +subPair (GT.LatLngV2 x1 y1) (GT.LatLngV2 x2 y2) = GT.LatLngV2 (x1 - x2) (y1 - y2) + +adjDiff :: [GT.LatLngV2] -> [GT.LatLngV2] +adjDiff p = zipWith subPair p (GT.LatLngV2 0 0 : p) + +decode :: T.Text -> [GT.LatLngV2] +decode = scanl1 addPair . makePairs . stringToCoords . TE.encodeUtf8 + +encode :: [GT.LatLngV2] -> T.Text +encode = T.concat . fmap oneCoordEnc . catPairs . adjDiff + convertGoogleToGeneric :: GT.AdvancedDirectionsResp -> MultiModalResponse convertGoogleToGeneric gResponse = let gRoutes = gResponse.routes @@ -127,7 +154,7 @@ convertGoogleToGeneric gResponse = let leg1Start = leg1.startLocation leg2Start = leg2.startLocation leg2End = leg2.endLocation - encodedPolylineText = GP.encode [leg1Start.latLng, leg2Start.latLng, leg2End.latLng] + encodedPolylineText = encode [leg1Start.latLng, leg2Start.latLng, leg2End.latLng] in MultiModalLeg { distance = leg1.distance + leg2.distance, duration = leg1.duration + leg2.duration,