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'
96 recomputeGraph :: UserId -> NodeId -> GargNoServer Graph
97 recomputeGraph _uId nId = do
98 nodeGraph <- getNodeWith nId HyperdataGraph
99 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
100 let listVersion = graph ^? _Just
107 let v = repo ^. r_version
108 let cId = maybe (panic "[G.V.G.API.recomputeGraph] Node has no parent")
110 $ nodeGraph ^. node_parentId
114 graph' <- computeGraph cId NgramsTerms repo
115 _ <- updateHyperdata nId (HyperdataGraph $ Just graph')
116 pure $ trace "[G.V.G.API.recomputeGraph] Graph empty, computed" $ graph'
118 Just graph' -> if listVersion == Just v
121 graph'' <- computeGraph cId NgramsTerms repo
122 _ <- updateHyperdata nId (HyperdataGraph $ Just graph'')
123 pure $ trace "[G.V.G.API] Graph exists, recomputing" $ graph''
127 -- TODO use Database Monad only here ?
128 computeGraph :: HasNodeError err
133 computeGraph cId nt repo = do
134 lId <- defaultList cId
136 let metadata = GraphMetadata "Title" [cId]
137 [ LegendField 1 "#FFF" "Cluster"
138 , LegendField 2 "#FFF" "Cluster"
140 (ListForGraph lId (repo ^. r_version))
141 -- (map (\n -> LegendField n "#FFFFFF" (pack $ show n)) [1..10])
143 lIds <- selectNodesWithUsername NodeList userMaster
144 let ngs = filterListWithRoot GraphTerm $ mapTermListRoot [lId] nt repo
146 myCooc <- Map.filter (>1)
147 <$> getCoocByNgrams (Diagonal True)
148 <$> groupNodesByNgrams ngs
149 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)
151 graph <- liftBase $ cooc2graph 0 myCooc
152 let graph' = set graph_metadata (Just metadata) graph
155 ------------------------------------------------------------
156 type GraphAsyncAPI = Summary "Update graph"
158 :> AsyncJobsAPI ScraperStatus () ScraperStatus
161 graphAsync :: UserId -> NodeId -> GargServer GraphAsyncAPI
164 JobFunction (\_ log' -> graphAsync' u n (liftBase . log'))
167 graphAsync' :: UserId
169 -> (ScraperStatus -> GargNoServer ())
170 -> GargNoServer ScraperStatus
171 graphAsync' u n logStatus = do
172 logStatus ScraperStatus { _scst_succeeded = Just 0
173 , _scst_failed = Just 0
174 , _scst_remaining = Just 1
175 , _scst_events = Just []
177 _g <- trace (show u) $ recomputeGraph u n
178 pure ScraperStatus { _scst_succeeded = Just 1
179 , _scst_failed = Just 0
180 , _scst_remaining = Just 0
181 , _scst_events = Just []
184 ------------------------------------------------------------
185 type GraphVersionsAPI = Summary "Graph versions"
186 :> Get '[JSON] GraphVersions
187 :<|> Summary "Recompute graph version"
188 :> Post '[JSON] Graph
190 graphVersionsAPI :: UserId -> NodeId -> GargServer GraphVersionsAPI
191 graphVersionsAPI u n =
193 :<|> recomputeVersions u n
195 graphVersions :: UserId -> NodeId -> GargNoServer GraphVersions
196 graphVersions _uId nId = do
197 nodeGraph <- getNodeWith nId HyperdataGraph
198 let graph = nodeGraph ^. node_hyperdata . hyperdataGraph
199 let listVersion = graph ^? _Just
206 let v = repo ^. r_version
208 pure $ GraphVersions { gv_graph = listVersion
211 recomputeVersions :: UserId -> NodeId -> GargNoServer Graph
212 recomputeVersions uId nId = recomputeGraph uId nId
214 ------------------------------------------------------------
215 getGraphGexf :: UserId
217 -> GargNoServer (Headers '[Servant.Header "Content-Disposition" Text] Graph)
218 getGraphGexf uId nId = do
219 graph <- getGraph uId nId
220 pure $ addHeader (concat [ "attachment; filename=graph.gexf" ]) graph