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 let saver_immediate = withMVar mvar (writeNodeStories nsd)
61 pure $ NodeStoryEnv { _nse_var = mvar
63 , _nse_saver_immediate = saver_immediate
64 , _nse_getter = nodeStoryVar nsd (Just mvar) }
66 ------------------------------------------------------------------------
67 mkNodeStorySaver :: NodeStoryDir -> MVar NodeListStory -> IO (IO ())
68 mkNodeStorySaver nsd mvns = mkDebounce settings
70 settings = defaultDebounceSettings
71 { debounceAction = withMVar mvns (writeNodeStories nsd)
72 , debounceFreq = 1 * minute
73 -- , debounceEdge = trailingEdge -- Trigger on the trailing edge
76 second = 10^(6 :: Int)
78 nodeStoryVar :: NodeStoryDir
79 -> Maybe (MVar NodeListStory)
81 -> IO (MVar NodeListStory)
82 nodeStoryVar nsd Nothing ni = nodeStoryIncs nsd Nothing ni >>= newMVar
83 nodeStoryVar nsd (Just mv) ni = do
84 _ <- modifyMVar_ mv $ \mv' -> (nodeStoryIncs nsd (Just mv') ni)
88 nodeStoryInc :: NodeStoryDir -> Maybe NodeListStory -> NodeId -> IO NodeListStory
89 nodeStoryInc nsd (Just ns@(NodeStory nls)) ni = do
90 case Map.lookup ni nls of
92 (NodeStory nls') <- nodeStoryRead nsd ni
93 pure $ NodeStory $ Map.union nls nls'
95 nodeStoryInc nsd Nothing ni = nodeStoryRead nsd ni
98 nodeStoryIncs :: NodeStoryDir
99 -> Maybe NodeListStory
102 nodeStoryIncs _ Nothing [] = pure $ NodeStory $ Map.empty
103 nodeStoryIncs nsd (Just nls) ns = foldM (\m n -> nodeStoryInc nsd (Just m) n) nls ns
104 nodeStoryIncs nsd Nothing (ni:ns) = do
105 m <- nodeStoryRead nsd ni
106 nodeStoryIncs nsd (Just m) ns
109 nodeStoryDec :: NodeStoryDir
113 nodeStoryDec nsd ns@(NodeStory nls) ni = do
114 case Map.lookup ni nls of
116 -- we make sure the corresponding file repo is really removed
117 _ <- nodeStoryRemove nsd ni
120 let ns' = Map.filterWithKey (\k _v -> k /= ni) nls
121 _ <- nodeStoryRemove nsd ni
125 nodeStoryRead :: NodeStoryDir -> NodeId -> IO NodeListStory
126 nodeStoryRead nsd ni = do
127 _repoDir <- createDirectoryIfMissing True nsd
128 let nsp = nodeStoryPath nsd ni
129 exists <- doesFileExist nsp
131 then deserialise <$> DBL.readFile nsp
132 else pure (initNodeStory ni)
134 nodeStoryRemove :: NodeStoryDir -> NodeId -> IO ()
135 nodeStoryRemove nsd ni = do
136 let nsp = nodeStoryPath nsd ni
137 exists <- doesFileExist nsp
144 nodeStoryRead_test :: NodeStoryDir -> NodeId -> IO (Maybe [ TableNgrams.NgramsType ])
145 nodeStoryRead_test nsd ni = nodeStoryRead nsd ni >>= \n -> pure
151 ------------------------------------------------------------------------
152 type NodeStoryDir = FilePath
154 writeNodeStories :: NodeStoryDir -> NodeListStory -> IO ()
155 writeNodeStories fp nls = do
156 _done <- mapM (writeNodeStory fp) $ splitByNode nls
157 -- printDebug "[writeNodeStories]" done
160 writeNodeStory :: NodeStoryDir -> (NodeId, NodeListStory) -> IO ()
161 writeNodeStory rdfp (n, ns) = saverAction' rdfp n ns
163 splitByNode :: NodeListStory -> [(NodeId, NodeListStory)]
164 splitByNode (NodeStory m) =
165 List.map (\(n,a) -> (n, NodeStory $ Map.singleton n a)) $ Map.toList m
168 saverAction' :: Serialise a => NodeStoryDir -> NodeId -> a -> IO ()
169 saverAction' repoDir nId a = do
170 withTempFile repoDir ((cs $ show nId) <> "-tmp-repo.cbor") $ \fp h -> do
171 -- printDebug "[repoSaverAction]" fp
172 DBL.hPut h $ serialise a
174 renameFile fp (nodeStoryPath repoDir nId)
176 nodeStoryPath :: NodeStoryDir -> NodeId -> FilePath
177 nodeStoryPath repoDir nId = repoDir <> "/" <> filename
179 filename = "repo" <> "-" <> (cs $ show nId) <> ".cbor"
182 ------------------------------------------------------------------------
183 -- TODO : repo Migration TODO TESTS
185 repoMigration :: NodeStoryDir -> NgramsRepo -> IO ()
186 repoMigration fp r = writeNodeStories fp (repoToNodeListStory r)
188 repoToNodeListStory :: NgramsRepo -> NodeListStory
189 repoToNodeListStory (Repo _v s h) = NodeStory $ Map.fromList ns
191 s' = ngramsState_migration s
192 h' = ngramsStatePatch_migration h
193 ns = List.map (\(n,ns')
194 -> (n, let hs = fromMaybe [] (Map.lookup n h') in
195 Archive { _a_version = List.length hs
201 ngramsState_migration :: NgramsState
202 -> Map NodeId NgramsState'
203 ngramsState_migration ns =
204 Map.fromListWith (Map.union) $
207 -> map (\(nid, table)
208 -> (nid, Map.singleton nt table)
209 ) $ Map.toList nTable
213 ngramsStatePatch_migration :: [NgramsStatePatch]
214 -> Map NodeId [NgramsStatePatch']
215 ngramsStatePatch_migration np' = Map.fromListWith (<>)
219 toPatch :: NgramsStatePatch -> [(NodeId, [NgramsStatePatch'])]
223 -> map (\(nid, table)
224 -> (nid, [fst $ Patch.singleton nt table])
225 ) $ Patch.toList nTable