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)
48 import Servant.Job.Async
50 import qualified Data.Map as Map
52 ------------------------------------------------------------------------
53 -- | There is no Delete specific API for Graph since it can be deleted
55 type GraphAPI = Get '[JSON] Graph
56 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
58 :<|> "versions" :> GraphVersionsAPI
61 GraphVersions { gv_graph :: Maybe Int
63 deriving (Show, Generic)
65 instance ToJSON GraphVersions
66 instance ToSchema GraphVersions
68 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
69 graphAPI u n = getGraph u n
72 :<|> graphVersionsAPI u n
74 ------------------------------------------------------------------------
75 getGraph :: UserId -> NodeId -> GargNoServer Graph
76 getGraph _uId nId = do
77 nodeGraph <- getNodeWith nId HyperdataGraph
78 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
81 let cId = maybe (panic "[G.V.G.API] Node has no parent")
83 $ nodeGraph ^. node_parentId
87 graph' <- computeGraph cId NgramsTerms repo
88 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
89 pure $ trace "[G.V.G.API] Graph empty, computing" graph'
91 Just graph' -> pure $ trace "[G.V.G.API] Graph exists, returning" graph'
94 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
95 recomputeGraph _uId nId = do
96 nodeGraph <- getNodeWith nId HyperdataGraph
97 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
98 let listVersion = graph ^? _Just
105 let v = repo ^. r_version
106 let cId = maybe (panic "[G.V.G.API.recomputeGraph] Node has no parent")
108 $ nodeGraph ^. node_parentId
112 graph' <- computeGraph cId NgramsTerms repo
113 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
114 pure $ trace "[G.V.G.API.recomputeGraph] Graph empty, computed" graph'
116 Just graph' -> if listVersion == Just v
119 graph'' <- computeGraph cId NgramsTerms repo
120 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
121 pure $ trace "[G.V.G.API] Graph exists, recomputing" graph''
124 -- TODO use Database Monad only here ?
125 computeGraph :: HasNodeError err
130 computeGraph cId nt repo = do
131 lId <- defaultList cId
133 let metadata = GraphMetadata "Title" [cId]
134 [ LegendField 1 "#FFF" "Cluster"
135 , LegendField 2 "#FFF" "Cluster"
137 (ListForGraph lId (repo ^. r_version))
138 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
140 lIds <- selectNodesWithUsername NodeList userMaster
141 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
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 0 myCooc
149 let graph' = set graph_metadata (Just metadata) graph
152 ------------------------------------------------------------
153 type GraphAsyncAPI = Summary "Update graph"
155 :> AsyncJobsAPI JobLog () JobLog
158 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
161 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
164 graphAsync' :: UserId
166 -> (JobLog -> GargNoServer ())
167 -> GargNoServer JobLog
168 graphAsync' u n logStatus = do
169 logStatus JobLog { _scst_succeeded = Just 0
170 , _scst_failed = Just 0
171 , _scst_remaining = Just 1
172 , _scst_events = Just []
174 _g <- trace (show u) $ recomputeGraph u n
175 pure JobLog { _scst_succeeded = Just 1
176 , _scst_failed = Just 0
177 , _scst_remaining = Just 0
178 , _scst_events = Just []
181 ------------------------------------------------------------
182 type GraphVersionsAPI = Summary "Graph versions"
183 :> Get '[JSON] GraphVersions
184 :<|> Summary "Recompute graph version"
185 :> Post '[JSON] Graph
187 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
188 graphVersionsAPI u n =
190 :<|> recomputeVersions u n
192 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
193 graphVersions _uId nId = do
194 nodeGraph <- getNodeWith nId HyperdataGraph
195 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
196 let listVersion = graph ^? _Just
203 let v = repo ^. r_version
205 pure $ GraphVersions { gv_graph = listVersion
208 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
209 recomputeVersions = recomputeGraph
211 ------------------------------------------------------------
212 getGraphGexf :: UserId
214 -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
215 getGraphGexf uId nId = do
216 graph <- getGraph uId nId
217 pure $ addHeader "attachment; filename=graph.gexf" graph