2 Module : Gargantext.Viz.Graph
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 {-# OPTIONS_GHC -fno-warn-orphans #-}
15 {-# LANGUAGE OverloadedLists #-} -- allows to write Map and HashMap as lists
16 {-# LANGUAGE TypeOperators #-}
18 module Gargantext.Viz.Graph.API
21 import Control.Lens (set, (^.), _Just, (^?))
23 import Data.Maybe (Maybe(..))
26 import Debug.Trace (trace)
27 import GHC.Generics (Generic)
28 import Gargantext.API.Admin.Orchestrator.Types
29 import Gargantext.API.Ngrams (NgramsRepo, r_version)
30 import Gargantext.API.Ngrams.Tools
31 import Gargantext.API.Prelude
32 import Gargantext.Core.Types.Main
33 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
34 import Gargantext.Database.Admin.Config
35 import Gargantext.Database.Admin.Types.Node
36 import Gargantext.Database.Prelude (Cmd)
37 import Gargantext.Database.Query.Table.Node
38 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
39 import Gargantext.Database.Query.Table.Node.Select
40 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
41 import Gargantext.Database.Schema.Ngrams
42 import Gargantext.Database.Schema.Node (node_parentId, node_hyperdata)
43 import Gargantext.Prelude
44 import Gargantext.Viz.Graph
45 import Gargantext.Viz.Graph.GEXF ()
46 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
47 import Gargantext.Viz.Graph.Distances (Distance(..), GraphMetric(..))
49 import Servant.Job.Async
51 import qualified Data.Map as Map
53 ------------------------------------------------------------------------
54 -- | There is no Delete specific API for Graph since it can be deleted
56 type GraphAPI = Get '[JSON] Graph
57 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
59 :<|> "versions" :> GraphVersionsAPI
62 GraphVersions { gv_graph :: Maybe Int
65 deriving (Show, Generic)
67 instance ToJSON GraphVersions
68 instance ToSchema GraphVersions
70 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
71 graphAPI u n = getGraph u n
74 :<|> graphVersionsAPI u n
76 ------------------------------------------------------------------------
77 getGraph :: UserId -> NodeId -> GargNoServer Graph
78 getGraph _uId nId = do
79 nodeGraph <- getNodeWith nId HyperdataGraph
80 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
84 let cId = maybe (panic "[G.V.G.API] Node has no parent")
86 $ nodeGraph ^. node_parentId
88 -- TODO Distance in Graph params
91 graph' <- computeGraph cId Conditional NgramsTerms repo
92 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
93 pure $ trace "[G.V.G.API] Graph empty, computing" $ graph'
95 Just graph' -> pure $ trace "[G.V.G.API] Graph exists, returning" $ graph'
100 recomputeGraph :: UserId -> NodeId -> Distance -> GargNoServer Graph
101 recomputeGraph _uId nId d = do
102 nodeGraph <- getNodeWith nId HyperdataGraph
103 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
104 let listVersion = graph ^? _Just
111 let v = repo ^. r_version
112 let cId = maybe (panic "[G.V.G.API.recomputeGraph] Node has no parent")
114 $ nodeGraph ^. node_parentId
118 graph' <- computeGraph cId d NgramsTerms repo
119 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
120 pure $ trace "[G.V.G.API.recomputeGraph] Graph empty, computed" $ graph'
122 Just graph' -> if listVersion == Just v
125 graph'' <- computeGraph cId d NgramsTerms repo
126 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
127 pure $ trace "[G.V.G.API] Graph exists, recomputing" $ graph''
131 -- TODO use Database Monad only here ?
132 computeGraph :: HasNodeError err
138 computeGraph cId d nt repo = do
139 lId <- defaultList cId
141 let metadata = GraphMetadata "Title"
144 [ LegendField 1 "#FFF" "Cluster"
145 , LegendField 2 "#FFF" "Cluster"
147 (ListForGraph lId (repo ^. r_version))
148 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
150 lIds <- selectNodesWithUsername NodeList userMaster
151 let ngs = filterListWithRoot MapTerm $ mapTermListRoot [lId] nt repo
153 myCooc <- Map.filter (>1)
154 <$> getCoocByNgrams (Diagonal True)
155 <$> groupNodesByNgrams ngs
156 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
158 graph <- liftBase $ cooc2graph d 0 myCooc
159 let graph' = set graph_metadata (Just metadata) graph
162 ------------------------------------------------------------
163 type GraphAsyncAPI = Summary "Update graph"
165 :> AsyncJobsAPI JobLog () JobLog
168 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
171 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
174 graphAsync' :: UserId
176 -> (JobLog -> GargNoServer ())
177 -> GargNoServer JobLog
178 graphAsync' u n logStatus = do
179 logStatus JobLog { _scst_succeeded = Just 0
180 , _scst_failed = Just 0
181 , _scst_remaining = Just 1
182 , _scst_events = Just []
184 _g <- trace (show u) $ recomputeGraph u n Conditional
185 pure JobLog { _scst_succeeded = Just 1
186 , _scst_failed = Just 0
187 , _scst_remaining = Just 0
188 , _scst_events = Just []
191 ------------------------------------------------------------
192 type GraphVersionsAPI = Summary "Graph versions"
193 :> Get '[JSON] GraphVersions
194 :<|> Summary "Recompute graph version"
195 :> Post '[JSON] Graph
197 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
198 graphVersionsAPI u n =
200 :<|> recomputeVersions u n
202 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
203 graphVersions _uId nId = do
204 nodeGraph <- getNodeWith nId HyperdataGraph
205 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
206 let listVersion = graph ^? _Just
213 let v = repo ^. r_version
215 pure $ GraphVersions { gv_graph = listVersion
218 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
219 recomputeVersions uId nId = recomputeGraph uId nId Conditional
221 ------------------------------------------------------------
222 getGraphGexf :: UserId
224 -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
225 getGraphGexf uId nId = do
226 graph <- getGraph uId nId
227 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph