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.Orchestrator.Types
45 import Gargantext.API.Types
46 import Gargantext.Core.Types.Main
47 import Gargantext.Database.Config
48 import Gargantext.Database.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
49 import Gargantext.Database.Schema.Ngrams
50 import Gargantext.Database.Node.Select
51 import Gargantext.Database.Schema.Node (getNodeWith, getNodeUser, defaultList, insertGraph)
52 import Gargantext.Database.Types.Errors (HasNodeError)
53 import Gargantext.Database.Types.Node hiding (node_id) -- (GraphId, ListId, CorpusId, NodeId)
54 import Gargantext.Database.Node.UpdateOpaleye (updateHyperdata)
55 import Gargantext.Database.Utils (Cmd)
56 import Gargantext.Prelude
57 import qualified Gargantext.Prelude as P
58 import Gargantext.Viz.Graph
59 import qualified Gargantext.Viz.Graph as G
60 import Gargantext.Viz.Graph.Tools -- (cooc2graph)
62 -- Converts to GEXF format
63 -- See https://gephi.org/gexf/format/
64 instance Xmlbf.ToXml Graph where
65 toXml (Graph { _graph_nodes = graphNodes
66 , _graph_edges = graphEdges }) = root graphNodes graphEdges
68 root :: [G.Node] -> [G.Edge] -> [Xmlbf.Node]
70 Xmlbf.element "gexf" params $ meta <> (graph gn ge)
72 params = HashMap.fromList [ ("xmlns", "http://www.gexf.net/1.2draft")
73 , ("version", "1.2") ]
74 meta = Xmlbf.element "meta" params $ creator <> desc
76 params = HashMap.fromList [ ("lastmodifieddate", "2020-03-13") ]
77 creator = Xmlbf.element "creator" HashMap.empty $ Xmlbf.text "Gargantext.org"
78 desc = Xmlbf.element "description" HashMap.empty $ Xmlbf.text "Gargantext gexf file"
79 graph :: [G.Node] -> [G.Edge] -> [Xmlbf.Node]
80 graph gn ge = Xmlbf.element "graph" params $ (nodes gn) <> (edges ge)
82 params = HashMap.fromList [ ("mode", "static")
83 , ("defaultedgetype", "directed") ]
84 nodes :: [G.Node] -> [Xmlbf.Node]
85 nodes gn = Xmlbf.element "nodes" HashMap.empty $ P.concatMap node gn
86 node :: G.Node -> [Xmlbf.Node]
87 node (G.Node { node_id = nId, node_label = l }) =
88 Xmlbf.element "node" params []
90 params = HashMap.fromList [ ("id", nId)
92 edges :: [G.Edge] -> [Xmlbf.Node]
93 edges gn = Xmlbf.element "edges" HashMap.empty $ P.concatMap edge gn
94 edge :: G.Edge -> [Xmlbf.Node]
95 edge (G.Edge { edge_id = eId, edge_source = es, edge_target = et }) =
96 Xmlbf.element "edge" params []
98 params = HashMap.fromList [ ("id", eId)
102 ------------------------------------------------------------------------
104 -- | There is no Delete specific API for Graph since it can be deleted
106 type GraphAPI = Get '[JSON] Graph
107 :<|> Post '[JSON] [GraphId]
109 :<|> "gexf" :> Get '[XML] (Headers '[Servant.Header "Content-Disposition" Text] Graph)
111 :<|> "versions" :> GraphVersionsAPI
114 data GraphVersions = GraphVersions { gv_graph :: Maybe Int
115 , gv_repo :: Int } deriving (Show, Generic)
117 instance ToJSON GraphVersions
118 instance ToSchema GraphVersions
120 graphAPI :: UserId -> NodeId -> GargServer GraphAPI
121 graphAPI u n = getGraph u n
124 :<|> getGraphGexf u n
126 :<|> graphVersionsAPI u n
128 ------------------------------------------------------------------------
130 getGraph :: UserId -> NodeId -> GargNoServer Graph
131 getGraph uId nId = do
132 nodeGraph <- getNodeWith nId HyperdataGraph
133 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
134 -- let listVersion = graph ^? _Just
141 -- let v = repo ^. r_version
142 nodeUser <- getNodeUser (NodeId uId)
144 let uId' = nodeUser ^. node_userId
146 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
148 $ nodeGraph ^. node_parentId
152 graph' <- computeGraph cId NgramsTerms repo
153 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
154 pure $ trace "Graph empty, computing" $ graph'
156 Just graph' -> pure $ trace "Graph exists, returning" $ graph'
158 -- Just graph' -> if listVersion == Just v
161 -- graph'' <- computeGraph cId NgramsTerms repo
162 -- _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
168 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
169 recomputeGraph uId nId = do
170 nodeGraph <- getNodeWith nId HyperdataGraph
171 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
172 let listVersion = graph ^? _Just
179 let v = repo ^. r_version
180 nodeUser <- getNodeUser (NodeId uId)
182 let uId' = nodeUser ^. node_userId
184 let cId = maybe (panic "[ERR:G.V.G.API] Node has no parent")
186 $ nodeGraph ^. node_parentId
190 graph' <- computeGraph cId NgramsTerms repo
191 _ <- insertGraph cId uId' (HyperdataGraph $ Just graph')
192 pure $ trace "[recomputeGraph] Graph empty, computing" $ graph'
194 Just graph' -> if listVersion == Just v
197 graph'' <- computeGraph cId NgramsTerms repo
198 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
199 pure $ trace "[recomputeGraph] Graph exists, recomputing" $ graph''
203 -- TODO use Database Monad only here ?
204 computeGraph :: HasNodeError err
209 computeGraph cId nt repo = do
210 lId <- defaultList cId
212 let metadata = GraphMetadata "Title" [cId]
213 [ LegendField 1 "#FFF" "Cluster"
214 , LegendField 2 "#FFF" "Cluster"
216 (ListForGraph lId (repo ^. r_version))
217 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
219 lIds <- selectNodesWithUsername NodeList userMaster
220 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
222 myCooc <- Map.filter (>1)
223 <$> getCoocByNgrams (Diagonal False)
224 <$> groupNodesByNgrams ngs
225 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
227 graph <- liftBase $ cooc2graph 0 myCooc
228 let graph' = set graph_metadata (Just metadata) graph
233 postGraph :: NodeId -> GargServer (Post '[JSON] [NodeId])
234 postGraph = undefined
236 putGraph :: NodeId -> GargServer (Put '[JSON] Int)
240 ------------------------------------------------------------
242 getGraphGexf :: UserId -> NodeId -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
243 getGraphGexf uId nId = do
244 graph <- getGraph uId nId
245 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph
247 ------------------------------------------------------------
249 type GraphAsyncAPI = Summary "Update graph"
251 :> AsyncJobsAPI ScraperStatus () ScraperStatus
253 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
256 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
259 graphAsync' :: UserId
261 -> (ScraperStatus -> GargNoServer ())
262 -> GargNoServer ScraperStatus
263 graphAsync' u n logStatus = do
264 logStatus ScraperStatus { _scst_succeeded = Just 0
265 , _scst_failed = Just 0
266 , _scst_remaining = Just 1
267 , _scst_events = Just []
269 _g <- trace (show u) $ recomputeGraph u n
270 pure ScraperStatus { _scst_succeeded = Just 1
271 , _scst_failed = Just 0
272 , _scst_remaining = Just 0
273 , _scst_events = Just []
276 ------------------------------------------------------------
278 type GraphVersionsAPI = Summary "Graph versions"
279 :> Get '[JSON] GraphVersions
280 :<|> Summary "Recompute graph version"
281 :> Post '[JSON] Graph
283 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
284 graphVersionsAPI u n =
286 :<|> recomputeVersions u n
288 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
289 graphVersions _uId nId = do
290 nodeGraph <- getNodeWith nId HyperdataGraph
291 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
292 let listVersion = graph ^? _Just
299 let v = repo ^. r_version
301 pure $ GraphVersions { gv_graph = listVersion
304 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
305 recomputeVersions uId nId = recomputeGraph uId nId