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 DataKinds #-}
16 {-# LANGUAGE DeriveGeneric #-}
17 {-# LANGUAGE FlexibleContexts #-}
18 {-# LANGUAGE FlexibleInstances #-}
19 {-# LANGUAGE NoImplicitPrelude #-}
20 {-# LANGUAGE OverloadedStrings #-} -- allows to write Text literals
21 {-# LANGUAGE OverloadedLists #-} -- allows to write Map and HashMap as lists
22 {-# LANGUAGE RankNTypes #-}
23 {-# LANGUAGE TypeOperators #-}
25 module Gargantext.Viz.Graph.API
28 import Control.Lens (set, (^.), _Just, (^?))
30 import Data.Maybe (Maybe(..))
33 import Debug.Trace (trace)
34 import GHC.Generics (Generic)
35 import Gargantext.API.Admin.Orchestrator.Types
36 import Gargantext.API.Ngrams (NgramsRepo, r_version)
37 import Gargantext.API.Ngrams.Tools
38 import Gargantext.API.Prelude
39 import Gargantext.Core.Types.Main
40 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
41 import Gargantext.Database.Admin.Config
42 import Gargantext.Database.Admin.Types.Node
43 import Gargantext.Database.Prelude (Cmd)
44 import Gargantext.Database.Query.Table.Node
45 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
46 import Gargantext.Database.Query.Table.Node.Select
47 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
48 import Gargantext.Database.Schema.Ngrams
49 import Gargantext.Database.Schema.Node (node_parentId, node_hyperdata)
50 import Gargantext.Prelude
51 import Gargantext.Viz.Graph
52 import Gargantext.Viz.Graph.GEXF ()
53 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
55 import Servant.Job.Async
57 import qualified Data.Map as Map
59 ------------------------------------------------------------------------
60 -- | There is no Delete specific API for Graph since it can be deleted
62 type GraphAPI = Get '[JSON] Graph
63 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
65 :<|> "versions" :> GraphVersionsAPI
68 GraphVersions { gv_graph :: Maybe Int
70 deriving (Show, Generic)
72 instance ToJSON GraphVersions
73 instance ToSchema GraphVersions
75 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
76 graphAPI u n = getGraph u n
79 :<|> graphVersionsAPI u n
81 ------------------------------------------------------------------------
82 getGraph :: UserId -> NodeId -> GargNoServer Graph
83 getGraph _uId nId = do
84 nodeGraph <- getNodeWith nId HyperdataGraph
85 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
86 -- let listVersion = graph ^? _Just
93 -- let v = repo ^. r_version
94 -- nodeUser <- getNodeUser (NodeId uId)
95 -- let uId' = nodeUser ^. node_userId
97 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
99 $ nodeGraph ^. node_parentId
103 graph' <- computeGraph cId NgramsTerms repo
104 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
105 pure $ trace "Graph empty, computing" $ graph'
107 Just graph' -> pure $ trace "Graph exists, returning" $ graph'
109 -- Just graph' -> if listVersion == Just v
112 -- graph'' <- computeGraph cId NgramsTerms repo
113 -- _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
118 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
119 recomputeGraph _uId nId = do
120 nodeGraph <- getNodeWith nId HyperdataGraph
121 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
122 let listVersion = graph ^? _Just
129 let v = repo ^. r_version
130 -- nodeUser <- getNodeUser (NodeId uId)
132 -- let uId' = nodeUser ^. node_userId
134 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
136 $ nodeGraph ^. node_parentId
140 graph' <- computeGraph cId NgramsTerms repo
141 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
142 pure $ trace "[recomputeGraph] Graph empty, computed" $ graph'
144 Just graph' -> if listVersion == Just v
147 graph'' <- computeGraph cId NgramsTerms repo
148 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
149 pure $ trace "[recomputeGraph] Graph exists, recomputing" $ graph''
153 -- TODO use Database Monad only here ?
154 computeGraph :: HasNodeError err
159 computeGraph cId nt repo = do
160 lId <- defaultList cId
162 let metadata = GraphMetadata "Title" [cId]
163 [ LegendField 1 "#FFF" "Cluster"
164 , LegendField 2 "#FFF" "Cluster"
166 (ListForGraph lId (repo ^. r_version))
167 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
169 lIds <- selectNodesWithUsername NodeList userMaster
170 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
172 myCooc <- Map.filter (>1)
173 <$> getCoocByNgrams (Diagonal True)
174 <$> groupNodesByNgrams ngs
175 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
177 graph <- liftBase $ cooc2graph 0 myCooc
178 let graph' = set graph_metadata (Just metadata) graph
181 ------------------------------------------------------------
183 getGraphGexf :: UserId -> NodeId -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
184 getGraphGexf uId nId = do
185 graph <- getGraph uId nId
186 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph
188 ------------------------------------------------------------
189 type GraphAsyncAPI = Summary "Update graph"
191 :> AsyncJobsAPI ScraperStatus () ScraperStatus
193 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
196 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
199 graphAsync' :: UserId
201 -> (ScraperStatus -> GargNoServer ())
202 -> GargNoServer ScraperStatus
203 graphAsync' u n logStatus = do
204 logStatus ScraperStatus { _scst_succeeded = Just 0
205 , _scst_failed = Just 0
206 , _scst_remaining = Just 1
207 , _scst_events = Just []
209 _g <- trace (show u) $ recomputeGraph u n
210 pure ScraperStatus { _scst_succeeded = Just 1
211 , _scst_failed = Just 0
212 , _scst_remaining = Just 0
213 , _scst_events = Just []
216 ------------------------------------------------------------
218 type GraphVersionsAPI = Summary "Graph versions"
219 :> Get '[JSON] GraphVersions
220 :<|> Summary "Recompute graph version"
221 :> Post '[JSON] Graph
223 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
224 graphVersionsAPI u n =
226 :<|> recomputeVersions u n
228 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
229 graphVersions _uId nId = do
230 nodeGraph <- getNodeWith nId HyperdataGraph
231 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
232 let listVersion = graph ^? _Just
239 let v = repo ^. r_version
241 pure $ GraphVersions { gv_graph = listVersion
244 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
245 recomputeVersions uId nId = recomputeGraph uId nId