dresdner-verkehrsbetriebe 0.1.1 → 1.0.0
raw patch · 5 files changed
+199/−27 lines, 5 filesdep +textdep ~HTTPdep ~aesondep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: text
Dependency ranges changed: HTTP, aeson, base, time
API changes (from Hackage documentation)
- Network.Transportation.Germany.DVB: instance Show City
- Network.Transportation.Germany.DVB: instance Show Location
- Network.Transportation.Germany.DVB: instance Show TimeType
- Network.Transportation.Germany.DVB.Route: instance Show Error
- Network.Transportation.Germany.DVB.Route: instance Show Leg
- Network.Transportation.Germany.DVB.Route: instance Show Route
- Network.Transportation.Germany.DVB.Route: instance Show RouteRequest
- Network.Transportation.Germany.DVB.Route: instance Show Stop
- Network.Transportation.Germany.DVB.Route: instance Show StopArrival
- Network.Transportation.Germany.DVB.Route: instance Show StopDeparture
- Network.Transportation.Germany.DVB.Route: instance Show Trip
- Network.Transportation.Germany.DVB.Route: legDesc :: Leg -> String
- Network.Transportation.Germany.DVB.Route: legNumber :: Leg -> String
- Network.Transportation.Germany.DVB.Route: legStops :: Leg -> [Stop]
- Network.Transportation.Germany.DVB.Route: routeReqCityDestination :: RouteRequest -> City
- Network.Transportation.Germany.DVB.Route: routeReqCityOrigin :: RouteRequest -> City
- Network.Transportation.Germany.DVB.Route: routeReqDestination :: RouteRequest -> Location
- Network.Transportation.Germany.DVB.Route: routeReqOrigin :: RouteRequest -> Location
- Network.Transportation.Germany.DVB.Route: routeReqTime :: RouteRequest -> LocalTime
- Network.Transportation.Germany.DVB.Route: routeReqTimeType :: RouteRequest -> TimeType
- Network.Transportation.Germany.DVB.Route: routeTrips :: Route -> [Trip]
- Network.Transportation.Germany.DVB.Route: stopArrival :: Stop -> Maybe StopArrival
- Network.Transportation.Germany.DVB.Route: stopArrivalDelayMins :: StopArrival -> Integer
- Network.Transportation.Germany.DVB.Route: stopArrivalTime :: StopArrival -> LocalTime
- Network.Transportation.Germany.DVB.Route: stopDeparture :: Stop -> Maybe StopDeparture
- Network.Transportation.Germany.DVB.Route: stopDepartureDelayMins :: StopDeparture -> Integer
- Network.Transportation.Germany.DVB.Route: stopDepartureTime :: StopDeparture -> LocalTime
- Network.Transportation.Germany.DVB.Route: stopName :: Stop -> String
- Network.Transportation.Germany.DVB.Route: stopPlatformName :: Stop -> String
- Network.Transportation.Germany.DVB.Route: tripDuration :: Trip -> String
- Network.Transportation.Germany.DVB.Route: tripLegs :: Trip -> [Leg]
+ Network.Transportation.Germany.DVB: instance GHC.Show.Show Network.Transportation.Germany.DVB.City
+ Network.Transportation.Germany.DVB: instance GHC.Show.Show Network.Transportation.Germany.DVB.Location
+ Network.Transportation.Germany.DVB: instance GHC.Show.Show Network.Transportation.Germany.DVB.TimeType
+ Network.Transportation.Germany.DVB.Monitor: HttpClosedError :: Error
+ Network.Transportation.Germany.DVB.Monitor: HttpMiscError :: String -> Error
+ Network.Transportation.Germany.DVB.Monitor: HttpParseError :: String -> Error
+ Network.Transportation.Germany.DVB.Monitor: HttpResetError :: Error
+ Network.Transportation.Germany.DVB.Monitor: JsonParseError :: String -> Error
+ Network.Transportation.Germany.DVB.Monitor: MonitorRequest :: City -> Location -> Integer -> Integer -> MonitorRequest
+ Network.Transportation.Germany.DVB.Monitor: TransitConnection :: String -> String -> Integer -> TransitConnection
+ Network.Transportation.Germany.DVB.Monitor: [monitorReqCity] :: MonitorRequest -> City
+ Network.Transportation.Germany.DVB.Monitor: [monitorReqLimit] :: MonitorRequest -> Integer
+ Network.Transportation.Germany.DVB.Monitor: [monitorReqOffset] :: MonitorRequest -> Integer
+ Network.Transportation.Germany.DVB.Monitor: [monitorReqStop] :: MonitorRequest -> Location
+ Network.Transportation.Germany.DVB.Monitor: [transConnArrivalMinutes] :: TransitConnection -> Integer
+ Network.Transportation.Germany.DVB.Monitor: [transConnDesc] :: TransitConnection -> String
+ Network.Transportation.Germany.DVB.Monitor: [transConnNumber] :: TransitConnection -> String
+ Network.Transportation.Germany.DVB.Monitor: data Error
+ Network.Transportation.Germany.DVB.Monitor: data MonitorRequest
+ Network.Transportation.Germany.DVB.Monitor: data TransitConnection
+ Network.Transportation.Germany.DVB.Monitor: instance GHC.Show.Show Network.Transportation.Germany.DVB.Monitor.Error
+ Network.Transportation.Germany.DVB.Monitor: instance GHC.Show.Show Network.Transportation.Germany.DVB.Monitor.MonitorRequest
+ Network.Transportation.Germany.DVB.Monitor: instance GHC.Show.Show Network.Transportation.Germany.DVB.Monitor.TransitConnection
+ Network.Transportation.Germany.DVB.Monitor: monitor :: MonitorRequest -> IO MonitorResult
+ Network.Transportation.Germany.DVB.Monitor: type MonitorResult = Either Error [TransitConnection]
+ Network.Transportation.Germany.DVB.Route: [legDesc] :: Leg -> String
+ Network.Transportation.Germany.DVB.Route: [legNumber] :: Leg -> String
+ Network.Transportation.Germany.DVB.Route: [legStops] :: Leg -> [Stop]
+ Network.Transportation.Germany.DVB.Route: [routeReqCityDestination] :: RouteRequest -> City
+ Network.Transportation.Germany.DVB.Route: [routeReqCityOrigin] :: RouteRequest -> City
+ Network.Transportation.Germany.DVB.Route: [routeReqDestination] :: RouteRequest -> Location
+ Network.Transportation.Germany.DVB.Route: [routeReqOrigin] :: RouteRequest -> Location
+ Network.Transportation.Germany.DVB.Route: [routeReqTimeType] :: RouteRequest -> TimeType
+ Network.Transportation.Germany.DVB.Route: [routeReqTime] :: RouteRequest -> LocalTime
+ Network.Transportation.Germany.DVB.Route: [routeTrips] :: Route -> [Trip]
+ Network.Transportation.Germany.DVB.Route: [stopArrivalDelayMins] :: StopArrival -> Integer
+ Network.Transportation.Germany.DVB.Route: [stopArrivalTime] :: StopArrival -> LocalTime
+ Network.Transportation.Germany.DVB.Route: [stopArrival] :: Stop -> Maybe StopArrival
+ Network.Transportation.Germany.DVB.Route: [stopDepartureDelayMins] :: StopDeparture -> Integer
+ Network.Transportation.Germany.DVB.Route: [stopDepartureTime] :: StopDeparture -> LocalTime
+ Network.Transportation.Germany.DVB.Route: [stopDeparture] :: Stop -> Maybe StopDeparture
+ Network.Transportation.Germany.DVB.Route: [stopName] :: Stop -> String
+ Network.Transportation.Germany.DVB.Route: [stopPlatformName] :: Stop -> String
+ Network.Transportation.Germany.DVB.Route: [tripDuration] :: Trip -> String
+ Network.Transportation.Germany.DVB.Route: [tripLegs] :: Trip -> [Leg]
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.Error
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.Leg
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.Route
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.RouteRequest
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.Stop
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.StopArrival
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.StopDeparture
+ Network.Transportation.Germany.DVB.Route: instance GHC.Show.Show Network.Transportation.Germany.DVB.Route.Trip
Files
- dresdner-verkehrsbetriebe.cabal +14/−9
- src/Main.hs +61/−17
- src/Network/Transportation/Germany/DVB/Monitor.hs +87/−0
- src/Network/Transportation/Germany/DVB/Monitor/JSON.hs +36/−0
- src/Network/Transportation/Germany/DVB/Route.hs +1/−1
dresdner-verkehrsbetriebe.cabal view
@@ -1,5 +1,5 @@ name: dresdner-verkehrsbetriebe-version: 0.1.1+version: 1.0.0 synopsis: Library and program for querying DVB (Dresdner Verkehrsbetriebe AG) license: MIT license-file: LICENSE@@ -8,19 +8,23 @@ category: Network build-type: Simple cabal-version: >= 1.10+Tested-With: GHC == 7.8.4, GHC == 7.10.2, GHC == 8.0.1 library exposed-modules: Network.Transportation.Germany.DVB , Network.Transportation.Germany.DVB.Route+ , Network.Transportation.Germany.DVB.Monitor other-modules: Network.Transportation.Germany.DVB.Route.JSON+ , Network.Transportation.Germany.DVB.Monitor.JSON build-depends:- base >= 4.7 && < 4.8- , HTTP >= 4000.2 && < 4000.3+ base >= 4 && < 5+ , HTTP >= 4000.2 && < 4000.4 , old-locale >= 1.0.0 && < 1.0.1- , time >= 1.4 && < 1.5- , aeson >= 0.10.0 && < 0.11.0+ , time >= 1.5 && < 1.7+ , aeson >= 0.10.0 && < 1.0.0+ , text >= 1.1.1.0 , vector >= 0.11.0 && < 0.12.0 , bytestring >= 0.10.0 && < 0.11.0 , unordered-containers >= 0.2.0 && < 0.3.0@@ -30,11 +34,12 @@ executable dresdner-verkehrsbetriebe main-is: Main.hs build-depends:- base >= 4.7 && < 4.8- , HTTP >= 4000.2 && < 4000.3+ base >= 4 && < 5+ , HTTP >= 4000.2 && < 4000.4 , old-locale >= 1.0.0 && < 1.0.1- , time >= 1.4 && < 1.5- , aeson >= 0.10.0 && < 0.11.0+ , time >= 1.5 && < 1.7+ , aeson >= 0.10.0 && < 1.0.0+ , text >= 1.1.1.0 , vector >= 0.11.0 && < 0.12.0 , bytestring >= 0.10.0 && < 0.11.0 , unordered-containers >= 0.2.0 && < 0.3.0
src/Main.hs view
@@ -8,23 +8,24 @@ module Main where -import Control.Applicative import Data.Time.Clock import Data.Time.Format import Data.Time.LocalTime import Network.Transportation.Germany.DVB import Network.Transportation.Germany.DVB.Route+import Network.Transportation.Germany.DVB.Monitor import Options.Applicative-import System.Locale -- |Data structure containing all program options. data ProgramOptions = ProgramOptions { poOrigin :: Location- , poDestination :: Location+ , poDestination :: Maybe Location , poOriginCity :: City , poDestinationCity :: City , poTime :: Maybe LocalTime , poTimeType :: Maybe TimeType+ , poOffset :: Integer+ , poLimit :: Integer } -- |Program options parser.@@ -34,9 +35,11 @@ (Location <$> strOption (long "origin" <> help "Location in the city from which to travel.")) <*>- (Location <$>+ (strToMaybeLocation <$> strOption (long "destination" <>- help "Location in the city to which to travel.")) <*>+ value "" <>+ help ("Location in the city to which to travel. Omit to " +++ "monitor the origin stop."))) <*> (City <$> strOption (long "origin-city" <> value (show defaultCity) <>@@ -52,8 +55,21 @@ (strToTimeType <$> strOption (long "time-type" <> value (show DepartureTime) <>- help "Departure (dep) or arrival (arr) time."))+ help "Departure (dep) or arrival (arr) time.")) <*>+ (read <$>+ strOption (long "offset" <>+ value "0" <>+ help "Offset of entries when monitoring the origin stop.")) <*>+ (read <$>+ strOption (long "limit" <>+ value "10" <>+ help "Limit of entries when monitoring the origin stop.")) where+ -- |Given a string that either contains text or is blank, return either a+ -- location or nothing respectively.+ strToMaybeLocation :: String -> Maybe Location+ strToMaybeLocation "" = Nothing+ strToMaybeLocation str = Just $ Location str -- |Given the current time and a time string, return a time if possible. strToTime :: LocalTime -> String -> Maybe LocalTime strToTime now "now" = Just now@@ -80,25 +96,41 @@ optMain :: ProgramOptions -> IO () optMain opts = case programOptionsToRequest opts of- Just request -> do+ Just (Left request) -> do result <- route request case result of Left err -> error ("The result is invalid: " ++ show err) Right route' -> putStrLn $ prettyRoute route'+ Just (Right request) -> do+ result <- monitor request+ case result of+ Left err -> error ("The result is invalid: " ++ show err)+ Right connections -> putStrLn $ prettyMonitor connections Nothing -> error "There are problems with the request." --- |Generate a route request from program options.-programOptionsToRequest :: ProgramOptions -> Maybe RouteRequest+-- |Generate a route or monitor request from program options.+programOptionsToRequest :: ProgramOptions ->+ Maybe (Either RouteRequest MonitorRequest) programOptionsToRequest opts = case (poTime opts, poTimeType opts) of- (Just time, Just timeType) -> Just $ RouteRequest {- routeReqOrigin = poOrigin opts,- routeReqDestination = poDestination opts,- routeReqCityOrigin = poOriginCity opts,- routeReqCityDestination = poDestinationCity opts,- routeReqTime = time,- routeReqTimeType = timeType- }+ (Just time, Just timeType) ->+ case poDestination opts of+ Just destination ->+ return $ Left $ RouteRequest {+ routeReqOrigin = poOrigin opts,+ routeReqDestination = destination,+ routeReqCityOrigin = poOriginCity opts,+ routeReqCityDestination = poDestinationCity opts,+ routeReqTime = time,+ routeReqTimeType = timeType+ }+ Nothing ->+ return $ Right $ MonitorRequest {+ monitorReqCity = poOriginCity opts,+ monitorReqStop = poOrigin opts,+ monitorReqOffset = poOffset opts,+ monitorReqLimit = poLimit opts+ } (_, _) -> Nothing -- |Print a route to be displayed to the user.@@ -139,3 +171,15 @@ l1 = " ~ Haltestelle: " ++ stopName stop ++ platform ++ arrival ++ departure ++ "\n" in l1++-- |Print transit connections to be displayed to the user.+prettyMonitor :: [TransitConnection] -> String+prettyMonitor connections = concat $ map prettyConnection connections+ where+ prettyConnection connection =+ let l1 = "===== Verbindung: ==========================================="+ l2 = " " ++ transConnDesc connection +++ " (Linie: " ++ transConnNumber connection ++ ")"+ l3 = " Kommt in: " ++ show (transConnArrivalMinutes connection) +++ " Minuten"+ in l1 ++ "\n" ++ l2 ++ "\n" ++ l3 ++ "\n"
+ src/Network/Transportation/Germany/DVB/Monitor.hs view
@@ -0,0 +1,87 @@+-- |+-- Module: Network.Transportation.Germany.DVB.Monitor+-- Copyright: (C) 2016 Braden Walters+-- License: MIT (see LICENSE file)+-- Maintainer: Braden Walters <vc@braden-walters.info>+-- Stability: experimental+-- Portability: ghc++module Network.Transportation.Germany.DVB.Monitor+( MonitorRequest(..)+, MonitorResult(..)+, TransitConnection(..)+, Error(..)+, monitor+) where++import Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as BS8+import Network.HTTP+import Network.Stream+import Network.Transportation.Germany.DVB+import qualified Network.Transportation.Germany.DVB.Monitor.JSON as JSON++-- |All data sent to DVB to monitor a stop.+data MonitorRequest = MonitorRequest+ { monitorReqCity :: City+ , monitorReqStop :: Location+ , monitorReqOffset :: Integer+ , monitorReqLimit :: Integer+ } deriving (Show)++-- |Either monitor data or an error.+type MonitorResult = Either Error [TransitConnection]++-- |An arriving vehicle at the stop.+data TransitConnection = TransitConnection+ { transConnNumber :: String+ , transConnDesc :: String+ , transConnArrivalMinutes :: Integer+ } deriving (Show)++-- |All possible errors which could occur while fetching data, including HTTP+-- errors and JSON parsing errors.+data Error = HttpResetError | HttpClosedError | HttpParseError String |+ HttpMiscError String | JsonParseError String deriving (Show)++-- |Given information about a stop, query and return data from DVB about+-- vehicles stopping there.+monitor :: MonitorRequest -> IO MonitorResult+monitor req = do+ let (City city) = monitorReqCity req+ (Location stop) = monitorReqStop req+ params = [("ort", city),+ ("hst", stop),+ ("vz", show $ monitorReqOffset req),+ ("lim", show $ monitorReqLimit req)]+ result <- simpleHTTP (getRequest (monitorUrl ++ "?" ++ urlEncodeVars params))+ case result of+ Left connError -> return $ Left $ connErrToResultErr connError+ Right response' ->+ let body = BS8.pack $ rspBody response'+ in case eitherDecode body of+ Left err ->+ return $ Left $ JsonParseError err+ Right transConns ->+ return $ Right (map fromJsonTransitConnection transConns)++-- |The HTTP URL to query for DVB monitor data.+monitorUrl :: String+monitorUrl = "http://widgets.vvo-online.de/abfahrtsmonitor/Abfahrten.do"++-- |Take an HTTP connection error and convert it into an error which can be+-- returned in a RouteResult.+connErrToResultErr :: ConnError -> Error+connErrToResultErr ErrorReset = HttpResetError+connErrToResultErr ErrorClosed = HttpClosedError+connErrToResultErr (ErrorParse msg) = HttpParseError msg+connErrToResultErr (ErrorMisc msg) = HttpMiscError msg++fromJsonTransitConnection :: JSON.TransitConnection -> TransitConnection+fromJsonTransitConnection transConn =+ TransitConnection {+ transConnNumber = JSON.transConnNumber transConn,+ transConnDesc = JSON.transConnDesc transConn,+ -- TODO: Use a total function instead of read.+ transConnArrivalMinutes = read $ JSON.transConnArrivalMinutes transConn+ }
+ src/Network/Transportation/Germany/DVB/Monitor/JSON.hs view
@@ -0,0 +1,36 @@+-- |+-- Module: Network.Transportation.Germany.DVB.Monitor.JSON+-- Copyright: (C) 2016 Braden Walters+-- License: MIT (see LICENSE file)+-- Maintainer: Braden Walters <vc@braden-walters.info>+-- Stability: experimental+-- Portability: ghc++{-# LANGUAGE OverloadedStrings #-}++module Network.Transportation.Germany.DVB.Monitor.JSON+( TransitConnection(..)+) where++import Data.Aeson+import Control.Monad+import qualified Data.Text as T+import qualified Data.Vector as V++-- |An arriving vehicle at the stop.+data TransitConnection = TransitConnection+ { transConnNumber :: String+ , transConnDesc :: String+ , transConnArrivalMinutes :: String+ } deriving (Show)++instance FromJSON TransitConnection where+ parseJSON (Array arr) =+ if V.length arr == 3 then+ case (arr V.! 0, arr V.! 1, arr V.! 2) of+ (String number, String desc, String arrivalMinutes) ->+ return $ TransitConnection (T.unpack number) (T.unpack desc)+ (T.unpack arrivalMinutes)+ _ -> mzero+ else mzero+ parseJSON _ = mzero
src/Network/Transportation/Germany/DVB/Route.hs view
@@ -29,7 +29,7 @@ import Network.Stream import Network.Transportation.Germany.DVB import qualified Network.Transportation.Germany.DVB.Route.JSON as JSON-import System.Locale+import System.Locale hiding (defaultTimeLocale) -- |All data sent to DVB to query routes. data RouteRequest = RouteRequest