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 Debug.Trace (trace)
31 import qualified Data.Map as Map
32 import Data.Maybe (Maybe(..))
35 import GHC.Generics (Generic)
38 import Servant.Job.Async
39 import Gargantext.API.Ngrams (NgramsRepo, r_version)
40 import Gargantext.API.Ngrams.Tools
41 import Gargantext.API.Admin.Orchestrator.Types
42 import Gargantext.API.Prelude
43 import Gargantext.Core.Types.Main
44 import Gargantext.Database.Admin.Config
45 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
46 import Gargantext.Database.Schema.Node (node_userId, node_parentId, node_hyperdata)
47 import Gargantext.Database.Schema.Ngrams
48 import Gargantext.Database.Query.Table.Node.Select
49 import Gargantext.Database.Query.Table.Node
50 import Gargantext.Database.Query.Table.Node.User
51 import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
52 import Gargantext.Database.Admin.Types.Node
53 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
54 import Gargantext.Database.Prelude (Cmd)
55 import Gargantext.Prelude
56 import Gargantext.Viz.Graph
57 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
58 import Gargantext.Viz.Graph.GEXF ()
60 ------------------------------------------------------------------------
61 -- | There is no Delete specific API for Graph since it can be deleted
63 type GraphAPI = Get '[JSON] Graph
64 :<|> Post '[JSON] [GraphId]
66 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
68 :<|> "versions" :> GraphVersionsAPI
72 GraphVersions { gv_graph :: Maybe Int
74 deriving (Show, Generic)
76 instance ToJSON GraphVersions
77 instance ToSchema GraphVersions
79 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
80 graphAPI u n = getGraph u n
85 :<|> graphVersionsAPI u n
87 ------------------------------------------------------------------------
89 getGraph :: UserId -> NodeId -> GargNoServer Graph
91 nodeGraph <- getNodeWith nId HyperdataGraph
92 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
93 -- let listVersion = graph ^? _Just
100 -- let v = repo ^. r_version
101 nodeUser <- getNodeUser (NodeId uId)
103 let uId' = nodeUser ^. node_userId
105 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
107 $ nodeGraph ^. node_parentId
111 graph' <- computeGraph cId NgramsTerms repo
112 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
113 pure $ trace "Graph empty, computing" $ graph'
115 Just graph' -> pure $ trace "Graph exists, returning" $ graph'
117 -- Just graph' -> if listVersion == Just v
120 -- graph'' <- computeGraph cId NgramsTerms repo
121 -- _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
127 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
128 recomputeGraph uId nId = do
129 nodeGraph <- getNodeWith nId HyperdataGraph
130 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
131 let listVersion = graph ^? _Just
138 let v = repo ^. r_version
139 nodeUser <- getNodeUser (NodeId uId)
141 let uId' = nodeUser ^. node_userId
143 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
145 $ nodeGraph ^. node_parentId
149 graph' <- computeGraph cId NgramsTerms repo
150 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
151 pure $ trace "[recomputeGraph] Graph empty, computing" $ graph'
153 Just graph' -> if listVersion == Just v
156 graph'' <- computeGraph cId NgramsTerms repo
157 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
158 pure $ trace "[recomputeGraph] Graph exists, recomputing" $ graph''
162 -- TODO use Database Monad only here ?
163 computeGraph :: HasNodeError err
168 computeGraph cId nt repo = do
169 lId <- defaultList cId
171 let metadata = GraphMetadata "Title" [cId]
172 [ LegendField 1 "#FFF" "Cluster"
173 , LegendField 2 "#FFF" "Cluster"
175 (ListForGraph lId (repo ^. r_version))
176 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
178 lIds <- selectNodesWithUsername NodeList userMaster
179 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
181 myCooc <- Map.filter (>1)
182 <$> getCoocByNgrams (Diagonal True)
183 <$> groupNodesByNgrams ngs
184 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
186 graph <- liftBase $ cooc2graph 0 myCooc
187 let graph' = set graph_metadata (Just metadata) graph
192 postGraph :: NodeId -> GargServer (Post '[JSON] [NodeId])
193 postGraph = undefined
195 putGraph :: NodeId -> GargServer (Put '[JSON] Int)
199 ------------------------------------------------------------
201 getGraphGexf :: UserId -> NodeId -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
202 getGraphGexf uId nId = do
203 graph <- getGraph uId nId
204 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph
206 ------------------------------------------------------------
208 type GraphAsyncAPI = Summary "Update graph"
210 :> AsyncJobsAPI ScraperStatus () ScraperStatus
212 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
215 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
218 graphAsync' :: UserId
220 -> (ScraperStatus -> GargNoServer ())
221 -> GargNoServer ScraperStatus
222 graphAsync' u n logStatus = do
223 logStatus ScraperStatus { _scst_succeeded = Just 0
224 , _scst_failed = Just 0
225 , _scst_remaining = Just 1
226 , _scst_events = Just []
228 _g <- trace (show u) $ recomputeGraph u n
229 pure ScraperStatus { _scst_succeeded = Just 1
230 , _scst_failed = Just 0
231 , _scst_remaining = Just 0
232 , _scst_events = Just []
235 ------------------------------------------------------------
237 type GraphVersionsAPI = Summary "Graph versions"
238 :> Get '[JSON] GraphVersions
239 :<|> Summary "Recompute graph version"
240 :> Post '[JSON] Graph
242 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
243 graphVersionsAPI u n =
245 :<|> recomputeVersions u n
247 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
248 graphVersions _uId nId = do
249 nodeGraph <- getNodeWith nId HyperdataGraph
250 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
251 let listVersion = graph ^? _Just
258 let v = repo ^. r_version
260 pure $ GraphVersions { gv_graph = listVersion
263 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
264 recomputeVersions uId nId = recomputeGraph uId nId