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 SqlJsonb HyperdataGraph
196 defaultFromField = fromPGSFromField
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'
233 linkV32edge :: Int -> EdgeV3 -> Edge
234 linkV32edge n (EdgeV3 eo_s' eo_t' eo_w') =
235 Edge { edge_source = cs $ show eo_s'
236 , edge_target = cs $ show eo_t'
237 , edge_weight = (T.read $ T.unpack eo_w') :: Double
238 , edge_confluence = 0.5
239 , edge_id = cs $ show n }
242 graphV3ToGraphWithFiles :: FilePath -> FilePath -> IO ()
243 graphV3ToGraphWithFiles g1 g2 = do
244 -- GraphV3 <- IO Fichier
245 graph <- DBL.readFile g1
246 let newGraph = case DA.decode graph :: Maybe GraphV3 of
247 Nothing -> panic (T.pack "no graph")
250 DBL.writeFile g2 (DA.encode $ graphV3ToGraph newGraph)
252 readGraphFromJson :: MonadBase IO m => FilePath -> m (Maybe Graph)
253 readGraphFromJson fp = do
254 graph <- liftBase $ DBL.readFile fp
255 pure $ DA.decode graph
258 -----------------------------------------------------------
259 mergeGraphNgrams :: Graph -> Maybe (HashMap NgramsTerm NgramsRepoElement) -> Graph
260 mergeGraphNgrams g Nothing = g
261 mergeGraphNgrams graph@(Graph { _graph_nodes }) (Just listNgrams) = set graph_nodes newNodes graph
263 newNodes = insertChildren <$> _graph_nodes
264 insertChildren (Node { node_label, .. }) = Node { node_children = children', .. }
266 -- lookup (NgramsTerm node_label) in listNgrams, then fetch (NgramsRepoElement _nre_children)
267 children' = case (lookup (NgramsTerm node_label) listNgrams) of
269 Just (NgramsRepoElement { _nre_children }) -> unNgramsTerm <$> mSetToList _nre_children