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
14 import Gargantext.Core.Types (ListId, NodeId(..))
15 import Gargantext.Prelude
16 import System.Directory (renameFile, createDirectoryIfMissing, doesFileExist, removeFile)
17 import System.IO (FilePath, hClose)
18 import System.IO.Temp (withTempFile)
19 import qualified Data.ByteString.Lazy as DBL
20 import qualified Data.List as List
21 import qualified Data.Map.Strict as Map
22 import qualified Gargantext.Database.Query.Table.Ngrams as TableNgrams
25 getRepo :: HasNodeStory env err m
26 => [ListId] -> m NodeListStory
29 v <- liftBase $ f listIds
30 v' <- liftBase $ readMVar v
33 getNodeListStory :: HasNodeStory env err m
34 => m ([NodeId] -> IO (MVar NodeListStory))
36 env <- view hasNodeStory
37 pure $ view nse_getter env
41 readNodeStoryEnv :: NodeStoryDir -> IO NodeStoryEnv
42 readNodeStoryEnv nsd = do
43 mvar <- nodeStoryVar nsd Nothing []
44 saver <- mkNodeStorySaver nsd mvar
45 pure $ NodeStoryEnv { _nse_var = mvar
47 , _nse_getter = nodeStoryVar nsd (Just mvar) }
49 ------------------------------------------------------------------------
50 mkNodeStorySaver :: NodeStoryDir -> MVar NodeListStory -> IO (IO ())
51 mkNodeStorySaver nsd mvns = mkDebounce settings
53 settings = defaultDebounceSettings
54 { debounceAction = withMVar mvns (writeNodeStories nsd)
55 , debounceFreq = 1 * minute
56 -- , debounceEdge = trailingEdge -- Trigger on the trailing edge
59 second = 10^(6 :: Int)
61 nodeStoryVar :: NodeStoryDir
62 -> Maybe (MVar NodeListStory)
64 -> IO (MVar NodeListStory)
65 nodeStoryVar nsd Nothing ni = nodeStoryIncs nsd Nothing ni >>= newMVar
66 nodeStoryVar nsd (Just mv) ni = do
67 _ <- modifyMVar_ mv $ \mv' -> (nodeStoryIncs nsd (Just mv') ni)
71 nodeStoryInc :: NodeStoryDir -> Maybe NodeListStory -> NodeId -> IO NodeListStory
72 nodeStoryInc nsd (Just ns@(NodeStory nls)) ni = do
73 case Map.lookup ni nls of
75 (NodeStory nls') <- nodeStoryRead nsd ni
76 pure $ NodeStory $ Map.union nls nls'
78 nodeStoryInc nsd Nothing ni = nodeStoryRead nsd ni
81 nodeStoryIncs :: NodeStoryDir
82 -> Maybe NodeListStory
85 nodeStoryIncs _ Nothing [] = pure $ NodeStory $ Map.empty
86 nodeStoryIncs nsd (Just nls) ns = foldM (\m n -> nodeStoryInc nsd (Just m) n) nls ns
87 nodeStoryIncs nsd Nothing (ni:ns) = do
88 m <- nodeStoryRead nsd ni
89 nodeStoryIncs nsd (Just m) ns
92 nodeStoryDec :: NodeStoryDir
96 nodeStoryDec nsd ns@(NodeStory nls) ni = do
97 case Map.lookup ni nls of
99 -- we make sure the corresponding file repo is really removed
100 _ <- nodeStoryRemove nsd ni
103 let ns' = Map.filterWithKey (\k _v -> k /= ni) nls
104 _ <- nodeStoryRemove nsd ni
108 nodeStoryRead :: NodeStoryDir -> NodeId -> IO NodeListStory
109 nodeStoryRead nsd ni = do
110 _repoDir <- createDirectoryIfMissing True nsd
111 let nsp = nodeStoryPath nsd ni
112 exists <- doesFileExist nsp
114 then deserialise <$> DBL.readFile nsp
115 else pure (initNodeStory ni)
117 nodeStoryRemove :: NodeStoryDir -> NodeId -> IO ()
118 nodeStoryRemove nsd ni = do
119 let nsp = nodeStoryPath nsd ni
120 exists <- doesFileExist nsp
127 nodeStoryRead_test :: NodeStoryDir -> NodeId -> IO (Maybe [ TableNgrams.NgramsType ])
128 nodeStoryRead_test nsd ni = nodeStoryRead nsd ni >>= \n -> pure
134 ------------------------------------------------------------------------
135 type NodeStoryDir = FilePath
137 writeNodeStories :: NodeStoryDir -> NodeListStory -> IO ()
138 writeNodeStories fp nls = do
139 _done <- mapM (writeNodeStory fp) $ splitByNode nls
140 -- printDebug "[writeNodeStories]" done
143 writeNodeStory :: NodeStoryDir -> (NodeId, NodeListStory) -> IO ()
144 writeNodeStory rdfp (n, ns) = saverAction' rdfp n ns
146 splitByNode :: NodeListStory -> [(NodeId, NodeListStory)]
147 splitByNode (NodeStory m) =
148 List.map (\(n,a) -> (n, NodeStory $ Map.singleton n a)) $ Map.toList m
151 saverAction' :: Serialise a => NodeStoryDir -> NodeId -> a -> IO ()
152 saverAction' repoDir nId a = do
153 withTempFile repoDir ((cs $ show nId) <> "-tmp-repo.cbor") $ \fp h -> do
154 -- printDebug "[repoSaverAction]" fp
155 DBL.hPut h $ serialise a
157 renameFile fp (nodeStoryPath repoDir nId)
159 nodeStoryPath :: NodeStoryDir -> NodeId -> FilePath
160 nodeStoryPath repoDir nId = repoDir <> "/" <> filename
162 filename = "repo" <> "-" <> (cs $ show nId) <> ".cbor"
165 ------------------------------------------------------------------------
166 -- TODO : repo Migration TODO TESTS
168 repoMigration :: NodeStoryDir -> NgramsRepo -> IO ()
169 repoMigration fp r = writeNodeStories fp (repoToNodeListStory r)
171 repoToNodeListStory :: NgramsRepo -> NodeListStory
172 repoToNodeListStory (Repo _v s h) = NodeStory $ Map.fromList ns
174 s' = ngramsState_migration s
175 h' = ngramsStatePatch_migration h
176 ns = List.map (\(n,ns')
177 -> (n, let hs = fromMaybe [] (Map.lookup n h') in
178 Archive { _a_version = List.length hs
184 ngramsState_migration :: NgramsState
185 -> Map NodeId NgramsState'
186 ngramsState_migration ns =
187 Map.fromListWith (Map.union) $
190 -> map (\(nid, table)
191 -> (nid, Map.singleton nt table)
192 ) $ Map.toList nTable
196 ngramsStatePatch_migration :: [NgramsStatePatch]
197 -> Map NodeId [NgramsStatePatch']
198 ngramsStatePatch_migration np' = Map.fromListWith (<>)
202 toPatch :: NgramsStatePatch -> [(NodeId, [NgramsStatePatch'])]
206 -> map (\(nid, table)
207 -> (nid, [fst $ Patch.singleton nt table])
208 ) $ Patch.toList nTable