2 Module : Gargantext.Core.Viz.Graph
3 Description : Graph utils
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
12 {-# OPTIONS_GHC -fno-warn-orphans #-}
13 {-# LANGUAGE TemplateHaskell #-}
15 module Gargantext.Core.Viz.Graph
18 import Data.ByteString.Lazy as DBL (readFile, writeFile)
19 import Data.HashMap.Strict (HashMap, lookup)
20 import Data.Text (pack)
21 import GHC.IO (FilePath)
23 import qualified Data.Aeson as DA
24 import qualified Data.Text as T
25 import qualified Text.Read as T
27 import Gargantext.API.Ngrams.Types (NgramsTerm(..), NgramsRepoElement(..), mSetToList)
28 import Gargantext.Core.Methods.Distances (GraphMetric)
29 import Gargantext.Core.Types (ListId)
30 import Gargantext.Database.Admin.Types.Hyperdata.Prelude
31 import Gargantext.Database.Admin.Types.Node (NodeId)
32 import Gargantext.Prelude
35 data TypeNode = Terms | Unknown
36 deriving (Show, Generic)
38 instance ToJSON TypeNode
39 instance FromJSON TypeNode
40 instance ToSchema TypeNode
42 data Attributes = Attributes { clust_default :: Int }
43 deriving (Show, Generic)
44 $(deriveJSON (unPrefix "") ''Attributes)
45 instance ToSchema Attributes
47 data Node = Node { node_size :: Int
48 , node_type :: TypeNode -- TODO NgramsType | Person
49 , node_id :: Text -- TODO NgramId
51 , node_x_coord :: Double
52 , node_y_coord :: Double
53 , node_attributes :: Attributes
54 , node_children :: [Text]
56 deriving (Show, Generic)
57 $(deriveJSON (unPrefix "node_") ''Node)
58 instance ToSchema Node where
59 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "node_")
62 data Edge = Edge { edge_source :: Text
64 , edge_weight :: Double
65 , edge_confluence :: Double
68 deriving (Show, Generic)
70 $(deriveJSON (unPrefix "edge_") ''Edge)
72 instance ToSchema Edge where
73 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "edge_")
75 ---------------------------------------------------------------
76 data LegendField = LegendField { _lf_id :: Int
79 } deriving (Show, Generic)
80 $(deriveJSON (unPrefix "_lf_") ''LegendField)
82 instance ToSchema LegendField where
83 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_lf_")
85 makeLenses ''LegendField
86 ---------------------------------------------------------------
89 ListForGraph { _lfg_listId :: ListId
90 , _lfg_version :: Version
91 } deriving (Show, Generic)
92 $(deriveJSON (unPrefix "_lfg_") ''ListForGraph)
94 instance ToSchema ListForGraph where
95 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_lfg_")
97 makeLenses ''ListForGraph
101 GraphMetadata { _gm_title :: Text -- title of the graph
102 , _gm_metric :: GraphMetric
103 , _gm_corpusId :: [NodeId] -- we can map with different corpus
104 , _gm_legend :: [LegendField] -- legend of the Graph
105 , _gm_list :: ListForGraph
106 , _gm_startForceAtlas :: Bool
107 -- , _gm_version :: Int
109 deriving (Show, Generic)
110 $(deriveJSON (unPrefix "_gm_") ''GraphMetadata)
111 instance ToSchema GraphMetadata where
112 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_gm_")
113 makeLenses ''GraphMetadata
116 data Graph = Graph { _graph_nodes :: [Node]
117 , _graph_edges :: [Edge]
118 , _graph_metadata :: Maybe GraphMetadata
120 deriving (Show, Generic)
121 $(deriveJSON (unPrefix "_graph_") ''Graph)
124 instance ToSchema Graph where
125 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_graph_")
127 -- | Intances for the mock
128 instance Arbitrary Graph where
129 arbitrary = elements $ [defaultGraph]
131 defaultGraph :: Graph
132 defaultGraph = Graph {_graph_nodes = [Node {node_x_coord=0, node_y_coord=0, node_size = 4, node_type = Terms, node_id = pack "0", node_label = pack "animal", node_attributes = Attributes {clust_default = 0}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 3, node_type = Terms, node_id = pack "1", node_label = pack "bird", node_attributes = Attributes {clust_default = 0}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 2, node_type = Terms, node_id = pack "2", node_label = pack "boy", node_attributes = Attributes {clust_default = 1}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 2, node_type = Terms, node_id = pack "3", node_label = pack "dog", node_attributes = Attributes {clust_default = 0}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 2, node_type = Terms, node_id = pack "4", node_label = pack "girl", node_attributes = Attributes {clust_default = 1}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 4, node_type = Terms, node_id = pack "5", node_label = pack "human body", node_attributes = Attributes {clust_default = 1}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 3, node_type = Terms, node_id = pack "6", node_label = pack "object", node_attributes = Attributes {clust_default = 2}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 2, node_type = Terms, node_id = pack "7", node_label = pack "pen", node_attributes = Attributes {clust_default = 2}, node_children = []},Node {node_x_coord=0, node_y_coord=0, node_size = 2, node_type = Terms, node_id = pack "8", node_label = pack "table", node_attributes = Attributes {clust_default = 2}, node_children = []}], _graph_edges = [Edge {edge_source = pack "0", edge_target = pack "0", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "0"},Edge {edge_source = pack "1", edge_target = pack "0", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "1"},Edge {edge_source = pack "1", edge_target = pack "1", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "2"},Edge {edge_source = pack "2", edge_target = pack "2", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "3"},Edge {edge_source = pack "2", edge_target = pack "5", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "4"},Edge {edge_source = pack "3", edge_target = pack "0", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "5"},Edge {edge_source = pack "3", edge_target = pack "1", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "6"},Edge {edge_source = pack "3", edge_target = pack "3", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "7"},Edge {edge_source = pack "4", edge_target = pack "4", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "8"},Edge {edge_source = pack "4", edge_target = pack "5", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "9"},Edge {edge_source = pack "5", edge_target = pack "5", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "10"},Edge {edge_source = pack "6", edge_target = pack "6", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "11"},Edge {edge_source = pack "7", edge_target = pack "6", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "12"},Edge {edge_source = pack "7", edge_target = pack "7", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "13"},Edge {edge_source = pack "8", edge_target = pack "6", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "14"},Edge {edge_source = pack "8", edge_target = pack "7", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "15"},Edge {edge_source = pack "8", edge_target = pack "8", edge_weight = 1.0, edge_confluence=0.5, edge_id = pack "16"}], _graph_metadata = Nothing}
135 -----------------------------------------------------------
136 -- V3 Gargantext Version
138 data AttributesV3 = AttributesV3 { cl :: Int }
139 deriving (Show, Generic)
140 $(deriveJSON (unPrefix "") ''AttributesV3)
142 data NodeV3 = NodeV3 { no_id :: Int
143 , no_at :: AttributesV3
147 deriving (Show, Generic)
148 $(deriveJSON (unPrefix "no_") ''NodeV3)
150 data EdgeV3 = EdgeV3 { eo_s :: Int
154 deriving (Show, Generic)
155 $(deriveJSON (unPrefix "eo_") ''EdgeV3)
157 data GraphV3 = GraphV3 { go_links :: [EdgeV3]
158 , go_nodes :: [NodeV3]
160 deriving (Show, Generic)
161 $(deriveJSON (unPrefix "go_") ''GraphV3)
163 -----------------------------------------------------------
164 data Camera = Camera { _camera_ratio :: Double
165 , _camera_x :: Double
166 , _camera_y :: Double }
167 deriving (Show, Generic)
168 $(deriveJSON (unPrefix "_camera_") ''Camera)
171 instance ToSchema Camera where
172 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_camera_")
174 -----------------------------------------------------------
175 data HyperdataGraph =
176 HyperdataGraph { _hyperdataGraph :: !(Maybe Graph)
177 , _hyperdataCamera :: !(Maybe Camera)
178 } deriving (Show, Generic)
179 $(deriveJSON (unPrefix "_") ''HyperdataGraph)
180 instance ToSchema HyperdataGraph where
181 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_")
183 defaultHyperdataGraph :: HyperdataGraph
184 defaultHyperdataGraph = HyperdataGraph Nothing Nothing
187 instance Hyperdata HyperdataGraph
188 makeLenses ''HyperdataGraph
190 instance FromField HyperdataGraph
192 fromField = fromField'
194 instance DefaultFromField PGJsonb HyperdataGraph
196 defaultFromField = fieldQueryRunnerColumn
198 -----------------------------------------------------------
199 -- This type is used to return graph via API
200 -- hyperdataGraphAPI field is not a Maybe anymore – graph is always computed
201 data HyperdataGraphAPI =
202 HyperdataGraphAPI { _hyperdataAPIGraph :: Graph
203 , _hyperdataAPICamera :: !(Maybe Camera)
204 } deriving (Show, Generic)
205 $(deriveJSON (unPrefix "_hyperdataAPI") ''HyperdataGraphAPI)
206 instance ToSchema HyperdataGraphAPI where
207 declareNamedSchema = genericDeclareNamedSchema (unPrefixSwagger "_hyperdataAPI")
209 makeLenses ''HyperdataGraphAPI
211 instance FromField HyperdataGraphAPI
213 fromField = fromField'
215 -----------------------------------------------------------
216 graphV3ToGraph :: GraphV3 -> Graph
217 graphV3ToGraph (GraphV3 links nodes) = Graph { _graph_nodes = map nodeV32node nodes
218 , _graph_edges = zipWith linkV32edge [1..] links
219 , _graph_metadata = Nothing }
221 nodeV32node :: NodeV3 -> Node
222 nodeV32node (NodeV3 no_id' (AttributesV3 cl') no_s' no_lb')
223 = Node { node_size = no_s'
225 , node_id = cs $ show no_id'
226 , node_label = no_lb'
229 , node_attributes = Attributes cl'
230 , node_children = [] }
232 linkV32edge :: Int -> EdgeV3 -> Edge
233 linkV32edge n (EdgeV3 eo_s' eo_t' eo_w') =
234 Edge { edge_source = cs $ show eo_s'
235 , edge_target = cs $ show eo_t'
236 , edge_weight = (T.read $ T.unpack eo_w') :: Double
237 , edge_confluence = 0.5
238 , edge_id = cs $ show n }
241 graphV3ToGraphWithFiles :: FilePath -> FilePath -> IO ()
242 graphV3ToGraphWithFiles g1 g2 = do
243 -- GraphV3 <- IO Fichier
244 graph <- DBL.readFile g1
245 let newGraph = case DA.decode graph :: Maybe GraphV3 of
246 Nothing -> panic (T.pack "no graph")
249 DBL.writeFile g2 (DA.encode $ graphV3ToGraph newGraph)
251 readGraphFromJson :: MonadBase IO m => FilePath -> m (Maybe Graph)
252 readGraphFromJson fp = do
253 graph <- liftBase $ DBL.readFile fp
254 pure $ DA.decode graph
257 -----------------------------------------------------------
258 mergeGraphNgrams :: Graph -> Maybe (HashMap NgramsTerm NgramsRepoElement) -> Graph
259 mergeGraphNgrams g Nothing = g
260 mergeGraphNgrams graph@(Graph { _graph_nodes }) (Just listNgrams) = set graph_nodes newNodes graph
262 newNodes = insertChildren <$> _graph_nodes
263 insertChildren (Node { node_label, .. }) = Node { node_children = children', .. }
265 -- lookup (NgramsTerm node_label) in listNgrams, then fetch (NgramsRepoElement _nre_children)
266 children' = case (lookup (NgramsTerm node_label) listNgrams) of
268 Just (NgramsRepoElement { _nre_children }) -> unNgramsTerm <$> mSetToList _nre_children