2 Module : Gargantext.API.Ngrams.Types
3 Description : Ngrams List Types
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 NOTE This is legacy code. It keeps node stories in a directory
11 repo. We now have migrated to the DB. However this code is needed to
12 make the migration (see Gargantext.API.Ngrams.Tools)
16 module Gargantext.Core.NodeStoryFile where
18 import Control.Lens (view)
19 import Control.Monad (foldM)
20 import Codec.Serialise (serialise, deserialise)
21 import Codec.Serialise.Class
22 import Control.Concurrent (MVar(), modifyMVar_, newMVar, readMVar, withMVar)
23 import Control.Debounce (mkDebounce, defaultDebounceSettings, debounceFreq, debounceAction)
24 import Gargantext.Core.NodeStory hiding (readNodeStoryEnv)
25 import Gargantext.Core.Types (ListId, NodeId(..))
26 import Gargantext.Database.Prelude (CmdM, hasConfig)
27 import Gargantext.Prelude
28 import Gargantext.Prelude.Config (gc_repofilepath)
29 import System.Directory (renameFile, createDirectoryIfMissing, doesFileExist, removeFile)
30 import System.IO (FilePath, hClose)
31 import System.IO.Temp (withTempFile)
32 import qualified Data.ByteString.Lazy as DBL
33 import qualified Data.List as List
34 import qualified Data.Map.Strict as Map
35 import qualified Gargantext.Database.Query.Table.Ngrams as TableNgrams
38 getRepo :: HasNodeStory env err m
39 => [ListId] -> m NodeListStory
45 -- v <- liftBase $ f listIds
46 -- v' <- liftBase $ readMVar v
49 getRepoReadConfig :: (CmdM env err m)
50 => [ListId] -> m NodeListStory
51 getRepoReadConfig listIds = do
52 repoFP <- view $ hasConfig . gc_repofilepath
53 env <- liftBase $ readNodeStoryEnv repoFP
54 let g = view nse_getter env
59 getNodeListStory :: HasNodeStory env err m
60 => m ([NodeId] -> IO (MVar NodeListStory))
62 env <- view hasNodeStory
63 pure $ view nse_getter env
67 readNodeStoryEnv :: NodeStoryDir -> IO NodeStoryEnv
68 readNodeStoryEnv nsd = do
69 mvar <- nodeStoryVar nsd Nothing []
70 saver <- mkNodeStorySaver nsd mvar
71 let saver_immediate = withMVar mvar (writeNodeStories nsd)
72 let archive_saver_immediate ns = pure ns
73 pure $ NodeStoryEnv { _nse_var = mvar
75 , _nse_saver_immediate = saver_immediate
76 , _nse_archive_saver_immediate = archive_saver_immediate
77 , _nse_getter = nodeStoryVar nsd (Just mvar) }
79 ------------------------------------------------------------------------
80 mkNodeStorySaver :: NodeStoryDir -> MVar NodeListStory -> IO (IO ())
81 mkNodeStorySaver nsd mvns = mkDebounce settings
83 settings = defaultDebounceSettings
84 { debounceAction = withMVar mvns (writeNodeStories nsd)
85 , debounceFreq = 1 * minute
86 -- , debounceEdge = trailingEdge -- Trigger on the trailing edge
89 second = 10^(6 :: Int)
91 nodeStoryVar :: NodeStoryDir
92 -> Maybe (MVar NodeListStory)
94 -> IO (MVar NodeListStory)
95 nodeStoryVar nsd Nothing ni = nodeStoryIncs nsd Nothing ni >>= newMVar
96 nodeStoryVar nsd (Just mv) ni = do
97 _ <- modifyMVar_ mv $ \mv' -> (nodeStoryIncs nsd (Just mv') ni)
101 nodeStoryInc :: NodeStoryDir -> Maybe NodeListStory -> NodeId -> IO NodeListStory
102 nodeStoryInc nsd (Just ns@(NodeStory nls)) ni = do
103 case Map.lookup ni nls of
105 (NodeStory nls') <- nodeStoryRead nsd ni
106 pure $ NodeStory $ Map.union nls nls'
108 nodeStoryInc nsd Nothing ni = nodeStoryRead nsd ni
111 nodeStoryIncs :: NodeStoryDir
112 -> Maybe NodeListStory
115 nodeStoryIncs _ Nothing [] = pure $ NodeStory $ Map.empty
116 nodeStoryIncs nsd (Just nls) ns = foldM (\m n -> nodeStoryInc nsd (Just m) n) nls ns
117 nodeStoryIncs nsd Nothing (ni:ns) = do
118 m <- nodeStoryRead nsd ni
119 nodeStoryIncs nsd (Just m) ns
122 nodeStoryDec :: NodeStoryDir
126 nodeStoryDec nsd ns@(NodeStory nls) ni = do
127 case Map.lookup ni nls of
129 -- we make sure the corresponding file repo is really removed
130 _ <- nodeStoryRemove nsd ni
133 let ns' = Map.filterWithKey (\k _v -> k /= ni) nls
134 _ <- nodeStoryRemove nsd ni
138 nodeStoryRead :: NodeStoryDir -> NodeId -> IO NodeListStory
139 nodeStoryRead nsd ni = do
140 _repoDir <- createDirectoryIfMissing True nsd
141 let nsp = nodeStoryPath nsd ni
142 exists <- doesFileExist nsp
144 then deserialise <$> DBL.readFile nsp
145 else pure (initNodeStory ni)
147 nodeStoryRemove :: NodeStoryDir -> NodeId -> IO ()
148 nodeStoryRemove nsd ni = do
149 let nsp = nodeStoryPath nsd ni
150 exists <- doesFileExist nsp
157 nodeStoryRead_test :: NodeStoryDir -> NodeId -> IO (Maybe [ TableNgrams.NgramsType ])
158 nodeStoryRead_test nsd ni = nodeStoryRead nsd ni >>= \n -> pure
164 ------------------------------------------------------------------------
165 type NodeStoryDir = FilePath
167 writeNodeStories :: NodeStoryDir -> NodeListStory -> IO ()
168 writeNodeStories fp nls = do
169 _done <- mapM (writeNodeStory fp) $ splitByNode nls
170 -- printDebug "[writeNodeStories]" done
173 writeNodeStory :: NodeStoryDir -> (NodeId, NodeListStory) -> IO ()
174 writeNodeStory rdfp (n, ns) = saverAction' rdfp n ns
176 splitByNode :: NodeListStory -> [(NodeId, NodeListStory)]
177 splitByNode (NodeStory m) =
178 List.map (\(n,a) -> (n, NodeStory $ Map.singleton n a)) $ Map.toList m
181 saverAction' :: Serialise a => NodeStoryDir -> NodeId -> a -> IO ()
182 saverAction' repoDir nId a = do
183 withTempFile repoDir ((cs $ show nId) <> "-tmp-repo.cbor") $ \fp h -> do
184 -- printDebug "[repoSaverAction]" fp
185 DBL.hPut h $ serialise a
187 renameFile fp (nodeStoryPath repoDir nId)
189 nodeStoryPath :: NodeStoryDir -> NodeId -> FilePath
190 nodeStoryPath repoDir nId = repoDir <> "/" <> filename
192 filename = "repo" <> "-" <> (cs $ show nId) <> ".cbor"
195 ------------------------------------------------------------------------
196 -- TODO : repo Migration TODO TESTS
198 repoMigration :: NodeStoryDir -> NgramsRepo -> IO ()
199 repoMigration fp r = writeNodeStories fp (repoToNodeListStory r)
201 repoToNodeListStory :: NgramsRepo -> NodeListStory
202 repoToNodeListStory (Repo _v s h) = NodeStory $ Map.fromList ns
204 s' = ngramsState_migration s
205 h' = ngramsStatePatch_migration h
206 ns = List.map (\(n,ns')
207 -> (n, let hs = fromMaybe [] (Map.lookup n h') in
208 Archive { _a_version = List.length hs
214 ngramsState_migration :: NgramsState
215 -> Map NodeId NgramsState'
216 ngramsState_migration ns =
217 Map.fromListWith (Map.union) $
220 -> map (\(nid, table)
221 -> (nid, Map.singleton nt table)
222 ) $ Map.toList nTable
226 ngramsStatePatch_migration :: [NgramsStatePatch]
227 -> Map NodeId [NgramsStatePatch']
228 ngramsStatePatch_migration np' = Map.fromListWith (<>)
232 toPatch :: NgramsStatePatch -> [(NodeId, [NgramsStatePatch'])]
236 -> map (\(nid, table)
237 -> (nid, [fst $ Patch.singleton nt table])
238 ) $ Patch.toList nTable