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 qualified Data.Map as Map
24 import Data.Maybe (Maybe(..))
27 import Debug.Trace (trace)
28 import GHC.Generics (Generic)
30 import Servant.Job.Async
33 import Gargantext.API.Admin.Orchestrator.Types
34 import Gargantext.API.Ngrams (NgramsRepo, r_version)
35 import Gargantext.API.Ngrams.Tools
36 import Gargantext.API.Prelude
37 import Gargantext.Core.Types.Main
38 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
39 import Gargantext.Database.Admin.Config
40 import Gargantext.Database.Admin.Types.Node
41 import Gargantext.Database.Prelude (Cmd)
42 import Gargantext.Database.Query.Table.Node
43 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
44 import Gargantext.Database.Query.Table.Node.Select
45 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
46 import Gargantext.Database.Schema.Ngrams
47 import Gargantext.Database.Schema.Node (node_parentId, node_hyperdata)
48 import Gargantext.Prelude
49 import Gargantext.Viz.Graph
50 import Gargantext.Viz.Graph.GEXF ()
51 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
52 import Gargantext.Viz.Graph.Distances (Distance(..), GraphMetric(..))
54 ------------------------------------------------------------------------
55 -- | There is no Delete specific API for Graph since it can be deleted
57 type GraphAPI = Get '[JSON] Graph
58 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
60 :<|> "versions" :> GraphVersionsAPI
63 GraphVersions { gv_graph :: Maybe Int
66 deriving (Show, Generic)
68 instance ToJSON GraphVersions
69 instance ToSchema GraphVersions
71 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
72 graphAPI u n = getGraph u n
75 :<|> graphVersionsAPI u n
77 ------------------------------------------------------------------------
78 getGraph :: UserId -> NodeId -> GargNoServer Graph
79 getGraph _uId nId = do
80 nodeGraph <- getNodeWith nId HyperdataGraph
81 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
85 let cId = maybe (panic "[G.V.G.API] Node has no parent")
87 $ nodeGraph ^. node_parentId
89 -- TODO Distance in Graph params
92 graph' <- computeGraph cId Conditional NgramsTerms repo
93 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
94 pure $ trace "[G.V.G.API] Graph empty, computing" graph'
96 Just graph' -> pure $ trace "[G.V.G.API] Graph exists, returning" graph'
99 recomputeGraph :: UserId -> NodeId -> Distance -> GargNoServer Graph
100 recomputeGraph _uId nId d = do
101 nodeGraph <- getNodeWith nId HyperdataGraph
102 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
103 let listVersion = graph ^? _Just
110 let v = repo ^. r_version
111 let cId = maybe (panic "[G.V.G.API.recomputeGraph] Node has no parent")
113 $ nodeGraph ^. node_parentId
117 graph' <- computeGraph cId d NgramsTerms repo
118 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
119 pure $ trace "[G.V.G.API.recomputeGraph] Graph empty, computed" graph'
121 Just graph' -> if listVersion == Just v
124 graph'' <- computeGraph cId d NgramsTerms repo
125 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
126 pure $ trace "[G.V.G.API] Graph exists, recomputing" graph''
129 -- TODO use Database Monad only here ?
130 computeGraph :: HasNodeError err
136 computeGraph cId d nt repo = do
137 lId <- defaultList cId
139 lIds <- selectNodesWithUsername NodeList userMaster
140 let ngs = filterListWithRoot MapTerm $ mapTermListRoot [lId] nt repo
142 -- TODO split diagonal
143 myCooc <- Map.filter (>1)
144 <$> getCoocByNgrams (Diagonal True)
145 <$> groupNodesByNgrams ngs
146 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
148 graph <- liftBase $ cooc2graph d 0 myCooc
151 let metadata = GraphMetadata "Title"
154 [ LegendField 1 "#FFF" "Cluster1"
155 , LegendField 2 "#FFF" "Cluster2"
156 , LegendField 3 "#FFF" "Cluster3"
157 , LegendField 4 "#FFF" "Cluster4"
159 (ListForGraph lId (repo ^. r_version))
160 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
162 pure $ set graph_metadata (Just metadata) graph
165 ------------------------------------------------------------
166 type GraphAsyncAPI = Summary "Update graph"
168 :> AsyncJobsAPI JobLog () JobLog
171 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
174 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
177 graphAsync' :: UserId
179 -> (JobLog -> GargNoServer ())
180 -> GargNoServer JobLog
181 graphAsync' u n logStatus = do
182 logStatus JobLog { _scst_succeeded = Just 0
183 , _scst_failed = Just 0
184 , _scst_remaining = Just 1
185 , _scst_events = Just []
187 _g <- trace (show u) $ recomputeGraph u n Conditional
188 pure JobLog { _scst_succeeded = Just 1
189 , _scst_failed = Just 0
190 , _scst_remaining = Just 0
191 , _scst_events = Just []
194 ------------------------------------------------------------
195 type GraphVersionsAPI = Summary "Graph versions"
196 :> Get '[JSON] GraphVersions
197 :<|> Summary "Recompute graph version"
198 :> Post '[JSON] Graph
200 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
201 graphVersionsAPI u n =
203 :<|> recomputeVersions u n
205 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
206 graphVersions _uId nId = do
207 nodeGraph <- getNodeWith nId HyperdataGraph
208 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
209 let listVersion = graph ^? _Just
216 let v = repo ^. r_version
218 pure $ GraphVersions { gv_graph = listVersion
221 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
222 recomputeVersions uId nId = recomputeGraph uId nId Conditional
224 ------------------------------------------------------------
225 getGraphGexf :: UserId
227 -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
228 getGraphGexf uId nId = do
229 graph <- getGraph uId nId
230 pure $ addHeader "attachment; filename=graph.gexf" graph