1 {- NOTE This is legacy code. It keeps node stories in a directory
2 repo. We now have migrated to the DB. However this code is needed to
3 make the migration (see Gargantext.API.Ngrams.Tools) -}
5 module Gargantext.Core.NodeStoryFile where
7 import Control.Lens (view)
8 import Control.Monad (foldM)
9 import Codec.Serialise (serialise, deserialise)
10 import Codec.Serialise.Class
11 import Control.Concurrent (MVar(), modifyMVar_, newMVar, readMVar, withMVar)
12 import Control.Debounce (mkDebounce, defaultDebounceSettings, debounceFreq, debounceAction)
13 import Gargantext.Core.NodeStory hiding (readNodeStoryEnv)
14 import Gargantext.Core.Types (ListId, NodeId(..))
15 import Gargantext.Database.Prelude (CmdM, hasConfig)
16 import Gargantext.Prelude
17 import Gargantext.Prelude.Config (gc_repofilepath)
18 import System.Directory (renameFile, createDirectoryIfMissing, doesFileExist, removeFile)
19 import System.IO (FilePath, hClose)
20 import System.IO.Temp (withTempFile)
21 import qualified Data.ByteString.Lazy as DBL
22 import qualified Data.List as List
23 import qualified Data.Map.Strict as Map
24 import qualified Gargantext.Database.Query.Table.Ngrams as TableNgrams
27 getRepo :: HasNodeStory env err m
28 => [ListId] -> m NodeListStory
34 -- v <- liftBase $ f listIds
35 -- v' <- liftBase $ readMVar v
38 getRepoReadConfig :: (CmdM env err m)
39 => [ListId] -> m NodeListStory
40 getRepoReadConfig listIds = do
41 repoFP <- view $ hasConfig . gc_repofilepath
42 env <- liftBase $ readNodeStoryEnv repoFP
43 let g = view nse_getter env
48 getNodeListStory :: HasNodeStory env err m
49 => m ([NodeId] -> IO (MVar NodeListStory))
51 env <- view hasNodeStory
52 pure $ view nse_getter env
56 readNodeStoryEnv :: NodeStoryDir -> IO NodeStoryEnv
57 readNodeStoryEnv nsd = do
58 mvar <- nodeStoryVar nsd Nothing []
59 saver <- mkNodeStorySaver nsd mvar
60 pure $ NodeStoryEnv { _nse_var = mvar
62 , _nse_getter = nodeStoryVar nsd (Just mvar) }
64 ------------------------------------------------------------------------
65 mkNodeStorySaver :: NodeStoryDir -> MVar NodeListStory -> IO (IO ())
66 mkNodeStorySaver nsd mvns = mkDebounce settings
68 settings = defaultDebounceSettings
69 { debounceAction = withMVar mvns (writeNodeStories nsd)
70 , debounceFreq = 1 * minute
71 -- , debounceEdge = trailingEdge -- Trigger on the trailing edge
74 second = 10^(6 :: Int)
76 nodeStoryVar :: NodeStoryDir
77 -> Maybe (MVar NodeListStory)
79 -> IO (MVar NodeListStory)
80 nodeStoryVar nsd Nothing ni = nodeStoryIncs nsd Nothing ni >>= newMVar
81 nodeStoryVar nsd (Just mv) ni = do
82 _ <- modifyMVar_ mv $ \mv' -> (nodeStoryIncs nsd (Just mv') ni)
86 nodeStoryInc :: NodeStoryDir -> Maybe NodeListStory -> NodeId -> IO NodeListStory
87 nodeStoryInc nsd (Just ns@(NodeStory nls)) ni = do
88 case Map.lookup ni nls of
90 (NodeStory nls') <- nodeStoryRead nsd ni
91 pure $ NodeStory $ Map.union nls nls'
93 nodeStoryInc nsd Nothing ni = nodeStoryRead nsd ni
96 nodeStoryIncs :: NodeStoryDir
97 -> Maybe NodeListStory
100 nodeStoryIncs _ Nothing [] = pure $ NodeStory $ Map.empty
101 nodeStoryIncs nsd (Just nls) ns = foldM (\m n -> nodeStoryInc nsd (Just m) n) nls ns
102 nodeStoryIncs nsd Nothing (ni:ns) = do
103 m <- nodeStoryRead nsd ni
104 nodeStoryIncs nsd (Just m) ns
107 nodeStoryDec :: NodeStoryDir
111 nodeStoryDec nsd ns@(NodeStory nls) ni = do
112 case Map.lookup ni nls of
114 -- we make sure the corresponding file repo is really removed
115 _ <- nodeStoryRemove nsd ni
118 let ns' = Map.filterWithKey (\k _v -> k /= ni) nls
119 _ <- nodeStoryRemove nsd ni
123 nodeStoryRead :: NodeStoryDir -> NodeId -> IO NodeListStory
124 nodeStoryRead nsd ni = do
125 _repoDir <- createDirectoryIfMissing True nsd
126 let nsp = nodeStoryPath nsd ni
127 exists <- doesFileExist nsp
129 then deserialise <$> DBL.readFile nsp
130 else pure (initNodeStory ni)
132 nodeStoryRemove :: NodeStoryDir -> NodeId -> IO ()
133 nodeStoryRemove nsd ni = do
134 let nsp = nodeStoryPath nsd ni
135 exists <- doesFileExist nsp
142 nodeStoryRead_test :: NodeStoryDir -> NodeId -> IO (Maybe [ TableNgrams.NgramsType ])
143 nodeStoryRead_test nsd ni = nodeStoryRead nsd ni >>= \n -> pure
149 ------------------------------------------------------------------------
150 type NodeStoryDir = FilePath
152 writeNodeStories :: NodeStoryDir -> NodeListStory -> IO ()
153 writeNodeStories fp nls = do
154 _done <- mapM (writeNodeStory fp) $ splitByNode nls
155 -- printDebug "[writeNodeStories]" done
158 writeNodeStory :: NodeStoryDir -> (NodeId, NodeListStory) -> IO ()
159 writeNodeStory rdfp (n, ns) = saverAction' rdfp n ns
161 splitByNode :: NodeListStory -> [(NodeId, NodeListStory)]
162 splitByNode (NodeStory m) =
163 List.map (\(n,a) -> (n, NodeStory $ Map.singleton n a)) $ Map.toList m
166 saverAction' :: Serialise a => NodeStoryDir -> NodeId -> a -> IO ()
167 saverAction' repoDir nId a = do
168 withTempFile repoDir ((cs $ show nId) <> "-tmp-repo.cbor") $ \fp h -> do
169 -- printDebug "[repoSaverAction]" fp
170 DBL.hPut h $ serialise a
172 renameFile fp (nodeStoryPath repoDir nId)
174 nodeStoryPath :: NodeStoryDir -> NodeId -> FilePath
175 nodeStoryPath repoDir nId = repoDir <> "/" <> filename
177 filename = "repo" <> "-" <> (cs $ show nId) <> ".cbor"
180 ------------------------------------------------------------------------
181 -- TODO : repo Migration TODO TESTS
183 repoMigration :: NodeStoryDir -> NgramsRepo -> IO ()
184 repoMigration fp r = writeNodeStories fp (repoToNodeListStory r)
186 repoToNodeListStory :: NgramsRepo -> NodeListStory
187 repoToNodeListStory (Repo _v s h) = NodeStory $ Map.fromList ns
189 s' = ngramsState_migration s
190 h' = ngramsStatePatch_migration h
191 ns = List.map (\(n,ns')
192 -> (n, let hs = fromMaybe [] (Map.lookup n h') in
193 Archive { _a_version = List.length hs
199 ngramsState_migration :: NgramsState
200 -> Map NodeId NgramsState'
201 ngramsState_migration ns =
202 Map.fromListWith (Map.union) $
205 -> map (\(nid, table)
206 -> (nid, Map.singleton nt table)
207 ) $ Map.toList nTable
211 ngramsStatePatch_migration :: [NgramsStatePatch]
212 -> Map NodeId [NgramsStatePatch']
213 ngramsStatePatch_migration np' = Map.fromListWith (<>)
217 toPatch :: NgramsStatePatch -> [(NodeId, [NgramsStatePatch'])]
221 -> map (\(nid, table)
222 -> (nid, [fst $ Patch.singleton nt table])
223 ) $ Patch.toList nTable