souplesse/lib/Store.hs

129 lines
3.9 KiB
Haskell
Raw Normal View History

2024-11-04 23:37:48 +00:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Store (save, fetch, sessions, sessionStartTime,
sessionDuration, migrateSession, migrateTrkpt
) where
2024-11-04 23:37:48 +00:00
2024-11-07 18:59:56 +00:00
import Control.Exception
2024-11-05 23:32:01 +00:00
import Control.Monad.IO.Class (MonadIO, liftIO)
2024-11-04 23:37:48 +00:00
import Control.Monad.Trans.Reader (ReaderT)
2024-11-07 18:59:56 +00:00
import Data.Maybe (isJust)
import Data.Time.Clock (DiffTime, NominalDiffTime, UTCTime, addUTCTime)
2024-11-04 23:37:48 +00:00
import Database.Persist
2024-11-05 23:32:01 +00:00
import Database.Persist.Class
2024-11-04 23:37:48 +00:00
import Database.Persist.Postgresql
( ConnectionString,
SqlBackend,
createPostgresqlPool,
pgConnStr,
pgPoolSize,
runMigration,
runSqlPool,
rawExecute,
PgInterval
2024-11-04 23:37:48 +00:00
)
import Database.Persist.TH
import Text.Read (readMaybe)
2024-11-05 23:32:01 +00:00
import Track (Point (..), Pos (..))
2024-11-04 23:37:48 +00:00
import Track as T
import Text.RawString.QQ (r)
2024-11-04 23:37:48 +00:00
connString :: ConnectionString
connString = "host=127.0.0.1 port=5432 user=souplesse dbname=souplesse password=secret"
share
[mkPersist sqlSettings, mkMigrate "migrateSession"]
[persistLowerCase|
Session
startTime UTCTime
duration PgInterval
draft Bool default=True
notes String Maybe
|]
share
[mkPersist sqlSettings, mkMigrate "migrateTrkpt"]
2024-11-04 23:37:48 +00:00
[persistLowerCase|
Trkpt
lat Double
lon Double
ele Double Maybe
time UTCTime
cadence Int Maybe
power Int Maybe
heartRate Int Maybe
|]
2024-11-04 23:37:48 +00:00
fromPoint :: Point -> Trkpt
fromPoint p =
let Pos lat lon ele = T.pos p
in Trkpt lat lon ele (T.time p) (T.cadence p) (T.power p) (T.heartRate p)
2024-11-05 23:32:01 +00:00
toPoint :: Entity Trkpt -> Point
toPoint entity =
let tkp = (\(Entity _ tkp) -> tkp) entity
pos = Pos (trkptLat tkp) (trkptLon tkp) (trkptEle tkp)
in Point
pos
(trkptTime tkp)
2024-11-06 19:13:47 +00:00
(trkptCadence tkp)
(trkptPower tkp)
(trkptHeartRate tkp)
2024-11-05 23:32:01 +00:00
updateSessions = [r|
-- delete existing drafts as new data may extend one of them
delete from session where draft;
-- find all potential start points in the new data
insert into session(start_time, duration, draft) (select time as start_time, make_interval(0) as duration, true as draft from trkpt as p where not exists (select id from session where start_time <= p.time and (start_time + duration) > p.time) and not exists(select * from trkpt as p1 where time > p.time - '600 seconds'::interval and time < p.time));
-- reset durations
update session s1
set duration =
coalesce(
(select max(time) from trkpt t1
where t1.time < coalesce((select min(start_time) from session s2 where s2.start_time > s1.start_time), now())) - s1.start_time
, make_interval())
where draft;
|]
2024-11-07 18:59:56 +00:00
data OverlapExists = OverlapExists String deriving (Show)
instance Exception OverlapExists
sessions :: (MonadIO m) => ReaderT SqlBackend m [Session]
sessions = do
s <- selectList [SessionDraft !=. True] [Desc SessionStartTime, LimitTo 10]
return $ map (\(Entity _ x) -> x) s
2024-11-07 18:59:56 +00:00
save :: (MonadIO m) => Track -> ReaderT SqlBackend m (Either OverlapExists Track)
save track = do
let start = startTime track
finish = addUTCTime (duration track) (startTime track)
priors <- selectFirst [TrkptTime >. start, TrkptTime <. finish] []
if isJust priors
then return $ Left (OverlapExists "track overlaps with existing data")
else do
mapM_ (insert . fromPoint) track
rawExecute updateSessions []
2024-11-07 18:59:56 +00:00
return $ Right track
2024-11-05 23:32:01 +00:00
fetch :: (MonadIO m) => UTCTime -> NominalDiffTime -> ReaderT SqlBackend m [Point]
fetch start duration = do
let finish = addUTCTime duration start
trkpts <- selectList [TrkptTime >. start, TrkptTime <. finish] []
2024-11-05 23:32:01 +00:00
return $ map toPoint trkpts