2 Module : Gargantext.Viz.Phylo.Tools
3 Description : Phylomemy Tools to build/manage it
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.HashMap.Lazy as HashMap
32 import qualified Data.Map as Map
33 import Data.Maybe (Maybe(..))
36 import GHC.Generics (Generic)
38 import Servant.Job.Async
40 import qualified Xmlbf as Xmlbf
42 import Gargantext.API.Ngrams (NgramsRepo, r_version)
43 import Gargantext.API.Ngrams.Tools
44 import Gargantext.API.Admin.Orchestrator.Types
45 import Gargantext.API.Admin.Types
46 import Gargantext.Core.Types.Main
47 import Gargantext.Database.Admin.Config
48 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
49 import Gargantext.Database.Schema.Node (node_userId, node_parentId, node_hyperdata)
50 import Gargantext.Database.Schema.Ngrams
51 import Gargantext.Database.Query.Table.Node.Select
52 import Gargantext.Database.Query.Table.Node
53 import Gargantext.Database.Query.Table.Node.User
54 import Gargantext.Database.Admin.Types.Errors (HasNodeError)
55 import Gargantext.Database.Admin.Types.Node hiding (node_id) -- (GraphId, ListId, CorpusId, NodeId)
56 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
57 import Gargantext.Database.Admin.Utils (Cmd)
58 import Gargantext.Prelude
59 import qualified Gargantext.Prelude as P
60 import Gargantext.Viz.Graph
61 import qualified Gargantext.Viz.Graph as G
62 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
64 -- Converts to GEXF format
65 -- See https://gephi.org/gexf/format/
66 instance Xmlbf.ToXml Graph where
67 toXml (Graph { _graph_nodes = graphNodes
68 , _graph_edges = graphEdges }) = root graphNodes graphEdges
70 root :: [G.Node] -> [G.Edge] -> [Xmlbf.Node]
72 Xmlbf.element "gexf" params $ meta <> (graph gn ge)
74 params = HashMap.fromList [ ("xmlns", "http://www.gexf.net/1.2draft")
75 , ("version", "1.2") ]
76 meta = Xmlbf.element "meta" params $ creator <> desc
78 params = HashMap.fromList [ ("lastmodifieddate", "2020-03-13") ]
79 creator = Xmlbf.element "creator" HashMap.empty $ Xmlbf.text "Gargantext.org"
80 desc = Xmlbf.element "description" HashMap.empty $ Xmlbf.text "Gargantext gexf file"
81 graph :: [G.Node] -> [G.Edge] -> [Xmlbf.Node]
82 graph gn ge = Xmlbf.element "graph" params $ (nodes gn) <> (edges ge)
84 params = HashMap.fromList [ ("mode", "static")
85 , ("defaultedgetype", "directed") ]
86 nodes :: [G.Node] -> [Xmlbf.Node]
87 nodes gn = Xmlbf.element "nodes" HashMap.empty $ P.concatMap node' gn
89 node' :: G.Node -> [Xmlbf.Node]
90 node' (G.Node { node_id = nId, node_label = l }) =
91 Xmlbf.element "node" params []
93 params = HashMap.fromList [ ("id", nId)
95 edges :: [G.Edge] -> [Xmlbf.Node]
96 edges gn = Xmlbf.element "edges" HashMap.empty $ P.concatMap edge gn
97 edge :: G.Edge -> [Xmlbf.Node]
98 edge (G.Edge { edge_id = eId, edge_source = es, edge_target = et }) =
99 Xmlbf.element "edge" params []
101 params = HashMap.fromList [ ("id", eId)
105 ------------------------------------------------------------------------
107 -- | There is no Delete specific API for Graph since it can be deleted
109 type GraphAPI = Get '[JSON] Graph
110 :<|> Post '[JSON] [GraphId]
112 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
114 :<|> "versions" :> GraphVersionsAPI
117 data GraphVersions = GraphVersions { gv_graph :: Maybe Int
118 , gv_repo :: Int } deriving (Show, Generic)
120 instance ToJSON GraphVersions
121 instance ToSchema GraphVersions
123 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
124 graphAPI u n = getGraph u n
127 :<|> getGraphGexf u n
129 :<|> graphVersionsAPI u n
131 ------------------------------------------------------------------------
133 getGraph :: UserId -> NodeId -> GargNoServer Graph
134 getGraph uId nId = do
135 nodeGraph <- getNodeWith nId HyperdataGraph
136 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
137 -- let listVersion = graph ^? _Just
144 -- let v = repo ^. r_version
145 nodeUser <- getNodeUser (NodeId uId)
147 let uId' = nodeUser ^. node_userId
149 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
151 $ nodeGraph ^. node_parentId
155 graph' <- computeGraph cId NgramsTerms repo
156 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
157 pure $ trace "Graph empty, computing" $ graph'
159 Just graph' -> pure $ trace "Graph exists, returning" $ graph'
161 -- Just graph' -> if listVersion == Just v
164 -- graph'' <- computeGraph cId NgramsTerms repo
165 -- _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
171 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
172 recomputeGraph uId nId = do
173 nodeGraph <- getNodeWith nId HyperdataGraph
174 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
175 let listVersion = graph ^? _Just
182 let v = repo ^. r_version
183 nodeUser <- getNodeUser (NodeId uId)
185 let uId' = nodeUser ^. node_userId
187 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
189 $ nodeGraph ^. node_parentId
193 graph' <- computeGraph cId NgramsTerms repo
194 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
195 pure $ trace "[recomputeGraph] Graph empty, computing" $ graph'
197 Just graph' -> if listVersion == Just v
200 graph'' <- computeGraph cId NgramsTerms repo
201 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
202 pure $ trace "[recomputeGraph] Graph exists, recomputing" $ graph''
206 -- TODO use Database Monad only here ?
207 computeGraph :: HasNodeError err
212 computeGraph cId nt repo = do
213 lId <- defaultList cId
215 let metadata = GraphMetadata "Title" [cId]
216 [ LegendField 1 "#FFF" "Cluster"
217 , LegendField 2 "#FFF" "Cluster"
219 (ListForGraph lId (repo ^. r_version))
220 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
222 lIds <- selectNodesWithUsername NodeList userMaster
223 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
225 myCooc <- Map.filter (>1)
226 <$> getCoocByNgrams (Diagonal True)
227 <$> groupNodesByNgrams ngs
228 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
230 graph <- liftBase $ cooc2graph 0 myCooc
231 let graph' = set graph_metadata (Just metadata) graph
236 postGraph :: NodeId -> GargServer (Post '[JSON] [NodeId])
237 postGraph = undefined
239 putGraph :: NodeId -> GargServer (Put '[JSON] Int)
243 ------------------------------------------------------------
245 getGraphGexf :: UserId -> NodeId -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
246 getGraphGexf uId nId = do
247 graph <- getGraph uId nId
248 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph
250 ------------------------------------------------------------
252 type GraphAsyncAPI = Summary "Update graph"
254 :> AsyncJobsAPI ScraperStatus () ScraperStatus
256 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
259 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
262 graphAsync' :: UserId
264 -> (ScraperStatus -> GargNoServer ())
265 -> GargNoServer ScraperStatus
266 graphAsync' u n logStatus = do
267 logStatus ScraperStatus { _scst_succeeded = Just 0
268 , _scst_failed = Just 0
269 , _scst_remaining = Just 1
270 , _scst_events = Just []
272 _g <- trace (show u) $ recomputeGraph u n
273 pure ScraperStatus { _scst_succeeded = Just 1
274 , _scst_failed = Just 0
275 , _scst_remaining = Just 0
276 , _scst_events = Just []
279 ------------------------------------------------------------
281 type GraphVersionsAPI = Summary "Graph versions"
282 :> Get '[JSON] GraphVersions
283 :<|> Summary "Recompute graph version"
284 :> Post '[JSON] Graph
286 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
287 graphVersionsAPI u n =
289 :<|> recomputeVersions u n
291 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
292 graphVersions _uId nId = do
293 nodeGraph <- getNodeWith nId HyperdataGraph
294 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
295 let listVersion = graph ^? _Just
302 let v = repo ^. r_version
304 pure $ GraphVersions { gv_graph = listVersion
307 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
308 recomputeVersions uId nId = recomputeGraph uId nId