Commit f54eb815 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-refactor-views-panel

parents a61b1948 7002ad83
......@@ -6,7 +6,6 @@
"aff-promise",
"affjax",
"argonaut",
"codec-argonaut",
"console",
"css",
"datetime",
......
......@@ -104,7 +104,8 @@ appCpt = R.hooksComponent "G.C.App.app" cpt where
, session
, sessions: (fst sessions)
, showLogin
, treeReload }
--, treeReload
}
type ForestLayoutProps =
( child :: R.Element
......
......@@ -16,6 +16,7 @@ import FFI.Simple (delay)
import Reactix as R
import Reactix.DOM.HTML as RH
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Hooks.Sigmax.Sigma as Sigma
......@@ -24,16 +25,18 @@ type OnProps = ()
data Stage = Init | Ready | Cleanup
type Props sigma forceatlas2 =
( elRef :: R.Ref (Nullable Element)
type Props sigma forceatlas2 = (
elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2
, graph :: SigmaxTypes.SGraph
, mCamera :: Maybe GET.Camera
, multiSelectEnabledRef :: R.Ref Boolean
, selectedNodeIds :: R.State SigmaxTypes.NodeIds
, showEdges :: R.State SigmaxTypes.ShowEdgesState
, sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma
, stage :: R.State Stage
, startForceAtlas :: Boolean
, transformedGraph :: SigmaxTypes.SGraph
)
......@@ -46,6 +49,15 @@ graphCpt = R.hooksComponent "G.C.Graph" cpt
cpt props _ = do
stageHooks props
R.useEffectOnce $ do
pure $ do
log "[graphCpt (Cleanup)]"
Sigmax.dependOnSigma (R.readRef props.sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do
Sigma.stopForceAtlas2 sigma
log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma
Sigma.kill sigma
log "[graphCpt (Cleanup)] sigma killed"
-- NOTE: This div is not empty after sigma initializes.
-- When we change state, we make it empty though.
--pure $ RH.div { ref: props.elRef, style: {height: "95%"} } []
......@@ -54,7 +66,7 @@ graphCpt = R.hooksComponent "G.C.Graph" cpt
Just el -> R.createPortal [] el
stageHooks props@{multiSelectEnabledRef, selectedNodeIds, sigmaRef, stage: (Init /\ setStage)} = do
R.useEffectOnce $ do
R.useEffectOnce' $ do
let rSigma = R.readRef props.sigmaRef
case Sigmax.readSigma rSigma of
......@@ -82,7 +94,17 @@ graphCpt = R.hooksComponent "G.C.Graph" cpt
pure unit
Sigmax.setEdges sig false
-- log2 "[graph] startForceAtlas" props.startForceAtlas
if props.startForceAtlas then
Sigma.startForceAtlas2 sig props.forceAtlas2Settings
else
Sigma.stopForceAtlas2 sig
case props.mCamera of
Nothing -> pure unit
Just (GET.Camera { ratio, x, y }) -> do
Sigma.updateCamera sig { ratio, x, y }
pure unit
Just sig -> do
......@@ -90,11 +112,7 @@ graphCpt = R.hooksComponent "G.C.Graph" cpt
setStage $ const Ready
delay unit $ \_ -> do
log "[graphCpt] cleanup"
pure $ pure unit
stageHooks props@{showEdges: (showEdges /\ _), sigmaRef, stage: (Ready /\ setStage), transformedGraph} = do
stageHooks props@{ showEdges: (showEdges /\ _), sigmaRef, stage: (Ready /\ setStage), transformedGraph } = do
let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap transformedGraph
......
......@@ -7,7 +7,7 @@ import Data.Array as A
import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Int (toNumber)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromJust)
import Data.Maybe (Maybe(..), fromJust, maybe)
import Data.Nullable (null, Nullable)
import Data.Sequence as Seq
import Data.Set as Set
......@@ -45,12 +45,13 @@ type LayoutProps =
, session :: Session
, sessions :: Sessions
, showLogin :: R.State Boolean
, treeReload :: R.State Int
--, treeReload :: R.State Int
)
type Props = (
graph :: SigmaxT.SGraph
, graphVersion :: R.State Int
, hyperdataGraph :: GET.HyperdataGraph
, mMetaData :: Maybe GET.MetaData
| LayoutProps
)
......@@ -75,8 +76,10 @@ explorerLayoutView graphVersion p = R.createElement el p []
useLoader graphId (getNodes session graphVersion) handler
where
handler loaded =
explorer (Record.merge props { graph, graphVersion, mMetaData })
where (Tuple mMetaData graph) = convert loaded
explorer (Record.merge props { graph, graphVersion, hyperdataGraph: loaded, mMetaData })
where
GET.HyperdataGraph { graph: hyperdataGraph } = loaded
(Tuple mMetaData graph) = convert hyperdataGraph
--------------------------------------------------------------
explorer :: Record Props -> R.Element
......@@ -90,16 +93,29 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, graphId
, graphVersion
, handed
, hyperdataGraph
, mCurrentRoute
, mMetaData
, session
, sessions
, showLogin
, treeReload } _ = do
--, treeReload
} _ = do
let startForceAtlas = maybe true (\(GET.MetaData { startForceAtlas }) -> startForceAtlas) mMetaData
let forceAtlasS = if startForceAtlas then SigmaxT.InitialRunning else SigmaxT.InitialStopped
dataRef <- R.useRef graph
graphRef <- R.useRef null
graphVersionRef <- R.useRef (fst graphVersion)
controls <- Controls.useGraphControls graph graphId session
treeReload <- R.useState' 0
controls <- Controls.useGraphControls { forceAtlasS
, graph
, graphId
, hyperdataGraph
, session
, treeReload: \_ -> (snd treeReload) $ (+) 1
}
multiSelectEnabledRef <- R.useRef $ fst controls.multiSelectEnabled
R.useEffect' $ do
......@@ -117,7 +133,7 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
snd controls.removedNodeIds $ const SigmaxT.emptyNodeIds
snd controls.selectedNodeIds $ const SigmaxT.emptyNodeIds
snd controls.showEdges $ const SigmaxT.EShow
snd controls.forceAtlasState $ const SigmaxT.InitialRunning
snd controls.forceAtlasState $ const forceAtlasS
snd controls.graphStage $ const Graph.Init
snd controls.showSidePanel $ const GET.InitialClosed
......@@ -147,6 +163,8 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, elRef: graphRef
, graphId
, graph
, hyperdataGraph
, mMetaData
, multiSelectEnabledRef
}
/\
......@@ -220,6 +238,8 @@ type GraphProps = (
, elRef :: R.Ref (Nullable Element)
, graphId :: GET.GraphId
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, mMetaData :: Maybe GET.MetaData
, multiSelectEnabledRef :: R.Ref Boolean
)
......@@ -230,7 +250,13 @@ graphView props = R.createElement graphViewCpt props []
graphViewCpt :: R.Component GraphProps
graphViewCpt = R.hooksComponent "GraphView" cpt
where
cpt {controls, elRef, graphId, graph, multiSelectEnabledRef} _children = do
cpt { controls
, elRef
, graphId
, graph
, hyperdataGraph: GET.HyperdataGraph { mCamera }
, mMetaData
, multiSelectEnabledRef } _children = do
-- TODO Cache this?
let louvainGraph =
if (fst controls.showLouvain) then
......@@ -240,6 +266,7 @@ graphViewCpt = R.hooksComponent "GraphView" cpt
else
graph
let transformedGraph = transformGraph controls louvainGraph
let startForceAtlas = maybe true (\(GET.MetaData { startForceAtlas }) -> startForceAtlas) mMetaData
R.useEffect1' (fst controls.multiSelectEnabled) $ do
R.setRef multiSelectEnabledRef $ fst controls.multiSelectEnabled
......@@ -248,12 +275,14 @@ graphViewCpt = R.hooksComponent "GraphView" cpt
elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings
, graph
, mCamera
, multiSelectEnabledRef
, selectedNodeIds: controls.selectedNodeIds
, showEdges: controls.showEdges
, sigmaRef: controls.sigmaRef
, sigmaSettings: Graph.sigmaSettings
, stage: controls.graphStage
, startForceAtlas
, transformedGraph
}
......@@ -261,9 +290,9 @@ convert :: GET.GraphData -> Tuple (Maybe GET.MetaData) SigmaxT.SGraph
convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
where
nodes = foldMapWithIndex nodeFn r.nodes
nodeFn _i (GET.Node n) =
Seq.singleton
{ borderColor: color
nodeFn _i nn@(GET.Node n) =
Seq.singleton {
borderColor: color
, color : color
, equilateral: { numPoints: 3 }
, gargType
......@@ -274,6 +303,7 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
, type : modeGraphType gargType
, x : n.x -- cos (toNumber i)
, y : n.y -- sin (toNumber i)
, _original: nn
}
where
cDef (GET.Cluster {clustDefault}) = clustDefault
......@@ -281,7 +311,7 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
gargType = unsafePartial $ fromJust $ Types.modeFromString n.type_
nodesMap = SigmaxT.nodesMap nodes
edges = foldMapWithIndex edgeFn $ A.sortWith (\(GET.Edge {weight}) -> weight) r.edges
edgeFn i (GET.Edge e) =
edgeFn i ee@(GET.Edge e) =
Seq.singleton
{ id : e.id_
, color
......@@ -294,6 +324,7 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
, targetNode
, weight : e.weight
, weightIdx: i
, _original: ee
}
where
sourceNode = unsafePartial $ fromJust $ Map.lookup e.source nodesMap
......@@ -308,7 +339,7 @@ modeGraphType Types.Sources = "star"
modeGraphType Types.Terms = "def"
getNodes :: Session -> R.State Int -> GET.GraphId -> Aff GET.GraphData
getNodes :: Session -> R.State Int -> GET.GraphId -> Aff GET.HyperdataGraph
getNodes session (graphVersion /\ _) graphId = get session $ NodeAPI Types.Graph (Just graphId) ("?version=" <> show graphVersion)
......
module Gargantext.Components.GraphExplorer.API where
import Data.Maybe (Maybe)
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.NgramsTable.Core as NTC
import Gargantext.Hooks.Sigmax.Types as SigmaxT
......@@ -21,11 +22,11 @@ type GraphAsyncUpdateParams =
)
graphAsyncUpdate :: Record GraphAsyncUpdateParams -> Aff GT.AsyncTaskWithType
graphAsyncUpdate {graphId, listId, nodes, session, termList, version} = do
graphAsyncUpdate { graphId, listId, nodes, session, termList, version } = do
task <- post session p q
pure $ GT.AsyncTaskWithType { task, typ: GT.GraphT }
pure $ GT.AsyncTaskWithType { task, typ: GT.GraphRecompute }
where
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphT
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute
q = { listId
, nodes
, termList
......@@ -41,9 +42,9 @@ type GraphAsyncRecomputeParams =
graphAsyncRecompute :: Record GraphAsyncRecomputeParams -> Aff GT.AsyncTaskWithType
graphAsyncRecompute { graphId, session } = do
task <- post session p q
pure $ GT.AsyncTaskWithType { task, typ: GT.GraphT }
pure $ GT.AsyncTaskWithType { task, typ: GT.GraphRecompute }
where
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphT
p = GR.GraphAPI graphId $ GT.asyncTaskTypePath GT.GraphRecompute
q = {}
type QueryProgressParams =
......@@ -80,3 +81,13 @@ type UpdateGraphVersionsParams =
updateGraphVersions :: Record UpdateGraphVersionsParams -> Aff GET.GraphData
updateGraphVersions { graphId, session } = post session (GR.GraphAPI graphId $ "versions") {}
type CloneGraphParams =
(
hyperdataGraph :: GET.HyperdataGraph
, id :: Int
, session :: Session
)
cloneGraph :: Record CloneGraphParams -> Aff Int
cloneGraph { hyperdataGraph, id, session } = post session (GR.GraphAPI id $ "clone") hyperdataGraph
......@@ -12,13 +12,18 @@ import Data.Maybe (Maybe(..))
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.String as DS
import DOM.Simple.Console (log2)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Effect.Now as EN
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryDataURL)
import Gargantext.Components.GraphExplorer.API (cloneGraph)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.GraphExplorer.Utils as GEU
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Sessions (Session)
......@@ -53,8 +58,21 @@ centerButton sigmaRef = simpleButton {
}
cameraButton :: Session -> Int -> R.Ref Sigmax.Sigma -> R.Element
cameraButton session id sigmaRef = simpleButton {
type CameraButtonProps = (
id :: Int
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, sigmaRef :: R.Ref Sigmax.Sigma
, treeReload :: Unit -> Effect Unit
)
cameraButton :: Record CameraButtonProps -> R.Element
cameraButton { id
, hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph }
, session
, sigmaRef
, treeReload } = simpleButton {
onClick: \_ -> do
let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do
......@@ -69,7 +87,24 @@ cameraButton session id sigmaRef = simpleButton {
, show $ fromEnum $ DDT.hour nowt
, show $ fromEnum $ DDT.minute nowt
, show $ fromEnum $ DDT.second nowt ]
edges <- Sigmax.getEdges s
nodes <- Sigmax.getNodes s
let graphData = GET.GraphData $ hyperdataGraph { edges = map GEU.stEdgeToGET edges
, nodes = map GEU.stNodeToGET nodes }
let cameras = map Sigma.toCamera $ Sigma.cameras s
let camera = case cameras of
[c] -> GET.Camera { ratio: c.ratio
, x: c.x
, y: c.y }
_ -> GET.Camera { ratio: 1.0
, x: 0.0
, y: 0.0 }
let hyperdataGraph = GET.HyperdataGraph { graph: graphData
, mCamera: Just camera }
launchAff_ $ do
uploadArbitraryDataURL session id (Just $ nowStr <> "-" <> "screenshot.png") screen
clonedGraphId <- cloneGraph { id, hyperdataGraph, session }
ret <- uploadArbitraryDataURL session clonedGraphId (Just $ nowStr <> "-" <> "screenshot.png") screen
liftEffect $ treeReload unit
pure ret
, text: "Screenshot"
}
......@@ -40,6 +40,7 @@ type Controls =
, graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, graphStage :: R.State Graph.Stage
, hyperdataGraph :: GET.HyperdataGraph
, multiSelectEnabled :: R.State Boolean
, nodeSize :: R.State Range.NumberRange
, removedNodeIds :: R.State SigmaxT.NodeIds
......@@ -51,6 +52,7 @@ type Controls =
, showSidePanel :: R.State GET.SidePanelState
, showTree :: R.State Boolean
, sigmaRef :: R.Ref Sigmax.Sigma
, treeReload :: Unit -> Effect Unit
)
type LocalControls =
......@@ -161,19 +163,34 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
, multiSelectEnabled: props.multiSelectEnabled
, selectedNodeIds: props.selectedNodeIds } ]
, RH.li {} [ mouseSelectorSizeButton props.sigmaRef localControls.mouseSelectorSize ]
, RH.li {} [ cameraButton props.session props.graphId props.sigmaRef ]
, RH.li {} [ cameraButton { id: props.graphId
, hyperdataGraph: props.hyperdataGraph
, session: props.session
, sigmaRef: props.sigmaRef
, treeReload: props.treeReload } ]
]
]
]
useGraphControls :: SigmaxT.SGraph -> GET.GraphId -> Session -> R.Hooks (Record Controls)
useGraphControls graph graphId session = do
useGraphControls :: { forceAtlasS :: SigmaxT.ForceAtlasState
, graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, treeReload :: Unit -> Effect Unit }
-> R.Hooks (Record Controls)
useGraphControls { forceAtlasS
, graph
, graphId
, hyperdataGraph
, session
, treeReload } = do
edgeConfluence <- R.useState' $ Range.Closed { min: 0.0, max: 1.0 }
edgeWeight <- R.useState' $ Range.Closed {
min: 0.0
, max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
}
forceAtlasState <- R.useState' SigmaxT.InitialRunning
forceAtlasState <- R.useState' forceAtlasS
graphStage <- R.useState' Graph.Init
multiSelectEnabled <- R.useState' false
nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 100.0 }
......@@ -193,6 +210,7 @@ useGraphControls graph graphId session = do
, graph
, graphId
, graphStage
, hyperdataGraph
, multiSelectEnabled
, nodeSize
, removedNodeIds
......@@ -204,6 +222,7 @@ useGraphControls graph graphId session = do
, showSidePanel
, showTree
, sigmaRef
, treeReload
}
getShowControls :: Record Controls -> Boolean
......
......@@ -116,6 +116,7 @@ pauseForceAtlasButtonCpt = R.hooksComponent "ForceAtlasToggleButton" cpt
[ H.text (text state) ]
]
text SigmaxTypes.InitialRunning = "Pause Force Atlas"
text SigmaxTypes.InitialStopped = "Start Force Atlas"
text SigmaxTypes.Running = "Pause Force Atlas"
text SigmaxTypes.Paused = "Start Force Atlas"
......
module Gargantext.Components.GraphExplorer.Types where
import Gargantext.Prelude
import Data.Argonaut (class DecodeJson, decodeJson, (.:))
import Data.Argonaut (class DecodeJson, decodeJson, class EncodeJson, encodeJson, (.:), (.:?), jsonEmptyObject, (~>), (:=))
import Data.Array ((!!), length)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Eq (genericEq)
import Data.Maybe (Maybe(..), fromJust)
import Data.Newtype (class Newtype)
import Data.Ord
import Partial.Unsafe (unsafePartial)
import Gargantext.Prelude
type GraphId = Int
newtype Node = Node
{ id_ :: String
newtype Node = Node {
attributes :: Cluster
, id_ :: String
, label :: String
, size :: Int
, type_ :: String
, label :: String
, x :: Number
, y :: Number
, attributes :: Cluster
}
derive instance genericNode :: Generic Node _
derive instance newtypeNode :: Newtype Node _
instance eqNode :: Eq Node where
eq = genericEq
instance ordNode :: Ord Node where
compare (Node n1) (Node n2) = compare n1.id_ n2.id_
newtype Cluster = Cluster { clustDefault :: Int }
derive instance genericCluster :: Generic Cluster _
derive instance newtypeCluster :: Newtype Cluster _
instance eqCluster :: Eq Cluster where
eq = genericEq
newtype Edge = Edge
{ confluence :: Number
newtype Edge = Edge {
confluence :: Number
, id_ :: String
, source :: String
, target :: String
, weight :: Number
}
derive instance genericEdge :: Generic Edge _
derive instance newtypeEdge :: Newtype Edge _
instance eqEdge :: Eq Edge where
eq = genericEq
instance ordEdge :: Ord Edge where
compare (Edge e1) (Edge e2) = compare e1.id_ e2.id_
-- | A 'fully closed interval' in CS parlance
type InclusiveRange t = { min :: t, max :: t }
......@@ -60,12 +77,14 @@ derive instance newtypeGraphData :: Newtype GraphData _
newtype MetaData = MetaData
{ title :: String
{ corpusId :: Array Int
, legend :: Array Legend
, corpusId :: Array Int
, list :: { listId :: ListId
, version :: Version
}
, metric :: String -- dummy value
, startForceAtlas :: Boolean
, title :: String
}
getLegend :: GraphData -> Maybe (Array Legend)
......@@ -100,7 +119,14 @@ initialGraphData = GraphData {
nodes: []
, edges: []
, sides: []
, metaData : Just $ MetaData {title : "", legend : [], corpusId : [], list: {listId : 0, version : 0}}
, metaData : Just $ MetaData {
corpusId : []
, legend : []
, list: { listId : 0, version : 0 }
, metric: "Order1"
, startForceAtlas: true
, title : ""
}
}
instance decodeJsonGraphData :: DecodeJson GraphData where
......@@ -118,6 +144,13 @@ instance decodeJsonGraphData :: DecodeJson GraphData where
let sides = side <$> corpusIds
pure $ GraphData { nodes, edges, sides, metaData }
instance encodeJsonGraphData :: EncodeJson GraphData where
encodeJson (GraphData gd) =
"nodes" := gd.nodes
~> "edges" := gd.edges
~> "metadata" := gd.metaData
~> jsonEmptyObject
instance decodeJsonNode :: DecodeJson Node where
decodeJson json = do
obj <- decodeJson json
......@@ -130,18 +163,47 @@ instance decodeJsonNode :: DecodeJson Node where
y <- obj .: "y_coord"
pure $ Node { id_, type_, size, label, attributes, x, y }
instance encodeJsonNode :: EncodeJson Node where
encodeJson (Node nd) =
"id" := nd.id_
~> "attributes" := nd.attributes
~> "label" := nd.label
~> "size" := nd.size
~> "type" := nd.type_
~> "x_coord" := nd.x
~> "y_coord" := nd.y
~> jsonEmptyObject
instance decodeJsonMetaData :: DecodeJson MetaData where
decodeJson json = do
obj <- decodeJson json
title <- obj .: "title"
legend <- obj .: "legend"
corpusId <- obj .: "corpusId"
list <- obj .: "list"
listId <- list .: "listId"
metric <- obj .: "metric"
startForceAtlas <- obj .: "startForceAtlas"
title <- obj .: "title"
version <- list .: "version"
pure $ MetaData { title, legend, corpusId, list: {listId, version}}
pure $ MetaData {
corpusId
, legend
, list: {listId, version}
, metric
, startForceAtlas
, title
}
instance encodeJsonMetaData :: EncodeJson MetaData where
encodeJson (MetaData md) =
"corpusId" := md.corpusId
~> "legend" := md.legend
~> "list" := md.list
~> "metric" := md.metric
~> "startForceAtlas" := md.startForceAtlas
~> "title" := md.title
~> jsonEmptyObject
instance decodeJsonLegend :: DecodeJson Legend where
decodeJson json = do
......@@ -151,6 +213,13 @@ instance decodeJsonLegend :: DecodeJson Legend where
label <- obj .: "label"
pure $ Legend { id_, color, label }
instance encodeJsonLegend :: EncodeJson Legend where
encodeJson (Legend lg) =
"id" := lg.id_
~> "color" := lg.color
~> "label" := lg.label
~> jsonEmptyObject
instance decodeJsonCluster :: DecodeJson Cluster where
decodeJson json = do
......@@ -158,6 +227,11 @@ instance decodeJsonCluster :: DecodeJson Cluster where
clustDefault <- obj .: "clust_default"
pure $ Cluster { clustDefault }
instance encodeJsonCluster :: EncodeJson Cluster where
encodeJson (Cluster cl) =
"clust_default" := cl.clustDefault
~> jsonEmptyObject
instance decodeJsonEdge :: DecodeJson Edge where
decodeJson json = do
obj <- decodeJson json
......@@ -168,6 +242,15 @@ instance decodeJsonEdge :: DecodeJson Edge where
confluence <- obj .: "confluence"
pure $ Edge { id_, source, target, weight, confluence }
instance jsonEncodeEdge :: EncodeJson Edge where
encodeJson (Edge ed) =
"id" := ed.id_
~> "confluence" := ed.confluence
~> "source" := ed.source
~> "target" := ed.target
~> "weight" := ed.weight
~> jsonEmptyObject
newtype Legend = Legend {id_ ::Int , color :: String, label :: String}
instance eqLegend :: Eq Legend where
......@@ -203,8 +286,42 @@ instance showSideTab :: Show SideTab where
show SideTabCommunity = "Community"
newtype Camera = Camera {
ratio :: Number
, x :: Number
, y :: Number
}
instance decodeCamera :: DecodeJson Camera where
decodeJson json = do
obj <- decodeJson json
ratio <- obj .: "ratio"
x <- obj .: "x"
y <- obj .: "y"
pure $ Camera { ratio, x, y }
instance jsonEncodeCamera :: EncodeJson Camera where
encodeJson (Camera c) =
"ratio" := c.ratio
~> "x" := c.x
~> "y" := c.y
~> jsonEmptyObject
newtype HyperdataGraph = HyperdataGraph {
graph :: GraphData
, mCamera :: Maybe Camera
}
instance decodeHyperdataGraph :: DecodeJson HyperdataGraph where
decodeJson json = do
obj <- decodeJson json
graph <- obj .: "graph"
mCamera <- obj .:? "camera"
pure $ HyperdataGraph { graph, mCamera }
instance jsonEncodeHyperdataGraph :: EncodeJson HyperdataGraph where
encodeJson (HyperdataGraph c) =
"camera" := c.mCamera
~> "graph" := c.graph
~> jsonEmptyObject
module Gargantext.Components.GraphExplorer.Utils
where
import Gargantext.Prelude
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as ST
stEdgeToGET :: Record ST.Edge -> GET.Edge
stEdgeToGET { _original } = _original
stNodeToGET :: Record ST.Node -> GET.Node
stNodeToGET { id, label, x, y, _original: GET.Node { attributes, size, type_ } } = GET.Node {
attributes
, id_: id
, label
, size
, type_
, x
, y
}
......@@ -22,8 +22,13 @@ import Data.Set as Set
import Data.Symbol (SProxy(..))
import Data.Tuple (Tuple(..), fst, snd)
import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log2)
import Effect (Effect)
import Effect.Aff (Aff)
import Reactix as R
import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
import Gargantext.Components.AutoUpdate (autoUpdateElt)
import Gargantext.Components.NgramsTable.Components as NTC
import Gargantext.Components.NgramsTable.Core
......@@ -37,9 +42,6 @@ import Gargantext.Utils (queryMatchesLabel, toggleSet)
import Gargantext.Utils.CacheAPI as GUC
import Gargantext.Utils.List (sortWith) as L
import Gargantext.Utils.Reactix as R2
import Reactix (Component, Element, State, createElement, fragment, hooksComponent, useState') as R
import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type State' =
CoreState
......@@ -492,7 +494,7 @@ mainNgramsTable props = R.createElement mainNgramsTableCpt props []
mainNgramsTableCpt :: R.Component MainNgramsTableProps
mainNgramsTableCpt = R.hooksComponent "G.C.NT.mainNgramsTable" cpt
where
cpt props@{nodeId, defaultListId, tabType, session, tabNgramType, withAutoUpdate} _ = do
cpt props@{nodeId, defaultListId, session, tabNgramType, tabType, withAutoUpdate} _ = do
let path = initialPageParams session nodeId [defaultListId] tabType
useLoaderWithCacheAPI {
......@@ -517,14 +519,16 @@ mainNgramsTableCpt = R.hooksComponent "G.C.NT.mainNgramsTable" cpt
, tabType
, termListFilter
, termSizeFilter
} = R.GetNgrams { limit
, listIds
, offset: Just offset
, orderBy: convOrderBy <$> orderBy
, searchQuery
, tabType
, termListFilter
, termSizeFilter } (Just nodeId)
} = R.GetNgramsTableAll { listIds
, tabType } (Just nodeId)
-- } = R.GetNgrams { limit
-- , listIds
-- , offset: Just offset
-- , orderBy: convOrderBy <$> orderBy
-- , searchQuery
-- , tabType
-- , termListFilter
-- , termSizeFilter } (Just nodeId)
handleResponse :: VersionedNgramsTable -> VersionedNgramsTable
handleResponse v = v
......@@ -548,8 +552,13 @@ mainNgramsTablePaintCpt :: R.Component MainNgramsTablePaintProps
mainNgramsTablePaintCpt = R.hooksComponent "G.C.NT.mainNgramsTablePaint" cpt
where
cpt {path, tabNgramType, versioned, withAutoUpdate} _ = do
R.useEffect' $ do
let (Versioned v) = versioned
log2 "[mainNgramsTablePaint] versioned values" $ show v.data
pathS <- R.useState' path
state <- R.useState' $ initialState versioned
pure $ loadedNgramsTable {
path: pathS
, state
......
......@@ -203,6 +203,9 @@ _list :: forall a row. Lens' { list :: a | row } a
_list = prop (SProxy :: SProxy "list")
derive instance newtypeNgramsElement :: Newtype NgramsElement _
derive instance genericNgramsElement :: Generic NgramsElement _
instance showNgramsElement :: Show NgramsElement where
show = genericShow
_NgramsElement :: Iso' NgramsElement {
children :: Set NgramsTerm
......@@ -262,7 +265,11 @@ instance decodeJsonVersioned :: DecodeJson a => DecodeJson (Versioned a) where
newtype NgramsTable = NgramsTable (Map NgramsTerm NgramsElement)
derive instance newtypeNgramsTable :: Newtype NgramsTable _
derive instance eqNgramsTable :: Eq NgramsTable
derive instance genericNgramsTable :: Generic NgramsTable _
instance eqNgramsTable :: Eq NgramsTable where
eq = genericEq
instance showNgramsTable :: Show NgramsTable where
show = genericShow
_NgramsTable :: Iso' NgramsTable (Map NgramsTerm NgramsElement)
_NgramsTable = _Newtype
......@@ -738,14 +745,17 @@ loadNgramsTable
{ nodeId, listIds, termListFilter, termSizeFilter, session, scoreType
, searchQuery, tabType, params: {offset, limit, orderBy}}
= get session query
where query = GetNgrams { limit
, offset: Just offset
, listIds
, orderBy: convOrderBy <$> orderBy
, searchQuery
, tabType
, termListFilter
, termSizeFilter } (Just nodeId)
where
query = GetNgramsTableAll { listIds
, tabType } (Just nodeId)
-- where query = GetNgrams { limit
-- , offset: Just offset
-- , listIds
-- , orderBy: convOrderBy <$> orderBy
-- , searchQuery
-- , tabType
-- , termListFilter
-- , termSizeFilter } (Just nodeId)
type NgramsListByTabType = Map TabType VersionedNgramsTable
......@@ -758,7 +768,7 @@ loadNgramsTableAll { nodeId, listIds, session, scoreType } = do
, CTabAuthors
, CTabInstitutes
]
query tabType = GetNgramsTableAll { tabType, listIds, scoreType } (Just nodeId)
query tabType = GetNgramsTableAll { listIds, tabType } (Just nodeId)
Map.fromFoldable <$> for cTagNgramTypes \cTagNgramType -> do
let tabType = TabCorpus $ TabNgramType cTagNgramType
......
......@@ -4,6 +4,7 @@ import Data.Argonaut (class DecodeJson)
import Data.Maybe (Maybe(..), maybe, isJust)
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log, log2)
import Effect.Aff (Aff, launchAff_, throwError)
import Effect.Class (liftEffect)
import Effect.Exception (error)
......@@ -72,6 +73,10 @@ useCachedAPILoaderEffect { cacheEndpoint
val <- if version == cacheReal then
pure vr
else do
-- liftEffect $ do
-- log "[useCachedAPILoaderEffect] versions dont match"
-- log2 "[useCachedAPILoaderEffect] cached version" version
-- log2 "[useCachedAPILoaderEffect] real version" cacheReal
_ <- GUC.delete cache req
vr@(Versioned { version, "data": d }) <- GUC.cachedJson cache req
if version == cacheReal then
......
......@@ -29,15 +29,15 @@ tabs props = R.createElement tabsCpt props []
tabsCpt :: R.Component Props
tabsCpt = R.hooksComponent "G.C.N.L.T.tabs" cpt
where
cpt {session, corpusId, corpusData: corpusData@{defaultListId}} _ = do
cpt { corpusData: corpusData@{ defaultListId }, corpusId, session } _ = do
(selected /\ setSelected) <- R.useState' 0
pure $ Tab.tabs { tabs: tabs', selected }
pure $ Tab.tabs { selected, tabs: tabs' }
where
tabs' = [ "Sources" /\ view Sources
, "Authors" /\ view Authors
tabs' = [ "Authors" /\ view Authors
, "Institutes" /\ view Institutes
, "Sources" /\ view Sources
, "Terms" /\ view Terms ]
view mode = ngramsView {mode, session, corpusId, corpusData}
view mode = ngramsView { corpusData, corpusId, mode, session }
type NgramsViewProps = ( mode :: Mode | Props )
......@@ -47,7 +47,7 @@ ngramsView props = R.createElement ngramsViewCpt props []
ngramsViewCpt :: R.Component NgramsViewProps
ngramsViewCpt = R.hooksComponent "G.C.N.L.T.ngramsView" cpt
where
cpt { corpusData: {defaultListId}
cpt { corpusData: { defaultListId }
, corpusId
, mode
, session } _ = do
......@@ -55,11 +55,11 @@ ngramsViewCpt = R.hooksComponent "G.C.N.L.T.ngramsView" cpt
pure $ R.fragment
( charts tabNgramType chartType
<> [ NT.mainNgramsTable { session
, defaultListId
<> [ NT.mainNgramsTable { defaultListId
, nodeId: corpusId
, tabType
, session
, tabNgramType
, tabType
, withAutoUpdate: false
}
]
......@@ -69,9 +69,9 @@ ngramsViewCpt = R.hooksComponent "G.C.N.L.T.ngramsView" cpt
tabType = TabCorpus (TabNgramType tabNgramType)
listId = defaultListId
path = { corpusId
, limit: Just 1000
, listId
, tabType
, limit: Just 1000
}
charts CTabTerms (chartType /\ setChartType) = [
......
......@@ -128,8 +128,7 @@ sessionPath (R.RecomputeListChart _ nt nId lId) = "node/" <> (show nId)
sessionPath (R.GraphAPI gId p) = "graph/" <> (show gId) <> "/" <> p
sessionPath (R.GetNgrams opts i) =
base opts.tabType
$ "ngrams?ngramsType="
<> showTabType' opts.tabType
$ "ngrams?ngramsType=" <> showTabType' opts.tabType
<> limitUrl opts.limit
<> offset opts.offset
<> orderByUrl opts.orderBy
......@@ -157,6 +156,8 @@ sessionPath (R.GetNgramsTableVersion opts i) =
$ "ngrams/version?ngramsType="
<> showTabType' opts.tabType
<> "&list=" <> show opts.listId
-- $ "ngrams/version?"
-- <> "list=" <> show opts.listId
sessionPath (R.ListDocument lId dId) =
sessionPath $ R.NodeAPI NodeList lId ("document/" <> (show $ fromMaybe 0 dId))
sessionPath (R.ListsRoute lId) = "lists/" <> show lId
......
......@@ -21,10 +21,11 @@ import Effect (Effect)
import Effect.Class.Console (error)
import Effect.Timer (TimeoutId, clearTimeout)
import FFI.Simple ((.=))
import Reactix as R
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as ST
import Gargantext.Utils.Reactix as R2
import Reactix as R
type Sigma =
{ sigma :: R.Ref (Maybe Sigma.Sigma)
......@@ -259,3 +260,9 @@ markSelectedNodes sigma selectedNodeIds graphNodes = do
_ <- pure $ (n .= "color") newColor
pure unit
Sigma.refresh sigma
getEdges :: Sigma.Sigma -> Effect (Array (Record ST.Edge))
getEdges sigma = Sigma.getEdges sigma
getNodes :: Sigma.Sigma -> Effect (Array (Record ST.Node))
getNodes sigma = Sigma.getNodes sigma
......@@ -42,7 +42,7 @@ sigma.canvas.nodes.selected = (node, context, settings) => {
node.type = 'def';
sigma.canvas.hovers.def(node, context, settings);
node.type = 'selected';
console.log('hovers, settings:', settings);
//console.log('hovers, settings:', settings);
};
CustomShapes.init();
......@@ -200,8 +200,18 @@ function takeScreenshot(sigma) {
return scene.toDataURL('image/png');
}
function getEdges(sigma) {
return sigma.graph.edges();
}
function getNodes(sigma) {
return sigma.graph.nodes();
}
exports._sigma = _sigma;
exports._addRenderer = addRenderer;
exports._bindMouseSelectorPlugin = bindMouseSelectorPlugin;
exports._bind = bind;
exports._takeScreenshot = takeScreenshot;
exports._getEdges = getEdges;
exports._getNodes = getNodes;
......@@ -16,9 +16,10 @@ import Effect.Timer (setTimeout)
import Effect.Uncurried (EffectFn1, EffectFn3, EffectFn4, mkEffectFn1, runEffectFn1, runEffectFn3, runEffectFn4)
import FFI.Simple ((..), (...), (.=))
import Foreign.Object as Object
import Gargantext.Hooks.Sigmax.Types as Types
import Type.Row (class Union)
import Gargantext.Hooks.Sigmax.Types as Types
-- | Type representing a sigmajs instance
foreign import data Sigma :: Type
-- | Type representing `sigma.graph`
......@@ -45,6 +46,10 @@ type SigmaOpts s = { settings :: s }
sigma :: forall opts err. SigmaOpts opts -> Effect (Either err Sigma)
sigma = runEffectFn3 _sigma Left Right
-- | Kill a sigmajs instance.
kill :: Sigma -> Effect Unit
kill sigma = pure $ sigma ... "kill" $ []
-- | Call the `refresh()` method on a sigmajs instance.
refresh :: Sigma -> Effect Unit
refresh s = pure $ s ... "refresh" $ []
......@@ -285,6 +290,22 @@ cameras s = Object.values cs
-- For some reason, `sigma.cameras` is an object with integer keys.
cs = s .. "cameras" :: Object.Object CameraInstance
toCamera :: CameraInstance -> Record CameraProps
toCamera c = { angle, ratio, x, y }
where
angle = c .. "angle" :: Number
ratio = c .. "ratio" :: Number
x = c .. "x" :: Number
y = c .. "y" :: Number
updateCamera :: Sigma -> { ratio :: Number, x :: Number, y :: Number } -> Effect Unit
updateCamera sig { ratio, x, y } = do
let camera = sig .. "camera"
_ <- pure $ (camera .= "ratio") ratio
_ <- pure $ (camera .= "x") x
_ <- pure $ (camera .= "y") y
pure unit
goTo :: Record CameraProps -> CameraInstance -> Effect Unit
goTo props cam = pure $ cam ... "goTo" $ [props]
......@@ -294,6 +315,12 @@ goToAllCameras s props = traverse_ (goTo props) $ cameras s
takeScreenshot :: Sigma -> Effect String
takeScreenshot = runEffectFn1 _takeScreenshot
getEdges :: Sigma -> Effect (Array (Record Types.Edge))
getEdges = runEffectFn1 _getEdges
getNodes :: Sigma -> Effect (Array (Record Types.Node))
getNodes = runEffectFn1 _getNodes
-- | FFI
foreign import _sigma ::
forall a b opts err.
......@@ -316,3 +343,5 @@ foreign import _bindMouseSelectorPlugin
(Either err Unit)
foreign import _bind :: forall e. EffectFn3 Sigma String (EffectFn1 e Unit) Unit
foreign import _takeScreenshot :: EffectFn1 Sigma String
foreign import _getEdges :: EffectFn1 Sigma (Array (Record Types.Edge))
foreign import _getNodes :: EffectFn1 Sigma (Array (Record Types.Node))
......@@ -13,6 +13,7 @@ import Data.Tuple (Tuple(..))
import Partial.Unsafe (unsafePartial)
import Prelude (class Eq, class Show, map, ($), (&&), (==), (||), (<$>), mod, not)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Data.Louvain as Louvain
import Gargantext.Types as GT
......@@ -29,8 +30,8 @@ type Renderer = { "type" :: String, container :: Element }
type NodeId = String
type EdgeId = String
type Node =
( borderColor :: String
type Node = (
borderColor :: String
, color :: String
, equilateral :: { numPoints :: Int }
, gargType :: GT.Mode
......@@ -41,10 +42,11 @@ type Node =
, type :: String -- available types: circle, cross, def, diamond, equilateral, pacman, square, star
, x :: Number
, y :: Number
, _original :: GET.Node
)
type Edge =
( color :: String
type Edge = (
color :: String
, confluence :: Number
, id :: EdgeId
, hidden :: Boolean
......@@ -55,6 +57,7 @@ type Edge =
, targetNode :: Record Node
, weight :: Number
, weightIdx :: Int
, _original :: GET.Edge
)
type NodeIds = Set.Set NodeId
......@@ -151,7 +154,7 @@ eqGraph (Graph {nodes: n1, edges: e1}) (Graph {nodes: n2, edges: e2}) = (n1 == n
-- however when graph is loaded initially, forceAtlas is running for a couple of
-- seconds and then stops (unless the user alters this by clicking the toggle
-- button).
data ForceAtlasState = InitialRunning | Running | Paused
data ForceAtlasState = InitialRunning | InitialStopped | Running | Paused
derive instance genericForceAtlasState :: Generic ForceAtlasState _
instance eqForceAtlasState :: Eq ForceAtlasState where
......@@ -159,6 +162,7 @@ instance eqForceAtlasState :: Eq ForceAtlasState where
toggleForceAtlasState :: ForceAtlasState -> ForceAtlasState
toggleForceAtlasState InitialRunning = Paused
toggleForceAtlasState InitialStopped = InitialRunning
toggleForceAtlasState Running = Paused
toggleForceAtlasState Paused = Running
......@@ -206,6 +210,7 @@ edgeStateStabilize s = s
forceAtlasEdgeState :: ForceAtlasState -> ShowEdgesState -> ShowEdgesState
forceAtlasEdgeState InitialRunning EShow = ETempHiddenThenShow
forceAtlasEdgeState InitialRunning es = es
forceAtlasEdgeState InitialStopped es = es
forceAtlasEdgeState Running EShow = ETempHiddenThenShow
forceAtlasEdgeState Running es = es
forceAtlasEdgeState Paused ETempHiddenThenShow = EShow
......
......@@ -10,6 +10,7 @@ import Effect (Effect)
import Data.Maybe (Maybe(..))
import Data.String.CodeUnits (length, slice) -- TODO: double check i'm the right choice
import Data.String.Regex (Regex)
import Gargantext.Utils.Regex (cloneRegex, execRegex, getRegexLastIndex)
import Gargantext.Utils.Array (push)
......
......@@ -19,7 +19,6 @@ import URI.Query (Query)
data Handed = LeftHanded | RightHanded
derive instance genericHanded :: Generic Handed _
instance eqHanded :: Eq Handed where
eq = genericEq
......@@ -342,20 +341,19 @@ instance showScoreType :: Show ScoreType where
type SearchQuery = String
type NgramsGetOpts =
{ tabType :: TabType
, limit :: Limit
{ limit :: Limit
, listIds :: Array ListId
, offset :: Maybe Offset
, orderBy :: Maybe OrderBy
, listIds :: Array ListId
, searchQuery :: SearchQuery
, tabType :: TabType
, termListFilter :: Maybe TermList
, termSizeFilter :: Maybe TermSize
, searchQuery :: SearchQuery
}
type NgramsGetTableAllOpts =
{ tabType :: TabType
, listIds :: Array ListId
, scoreType :: ScoreType
{ listIds :: Array ListId
, tabType :: TabType
}
type SearchOpts =
......@@ -566,14 +564,14 @@ instance encodeMode :: EncodeJson Mode where
modeTabType :: Mode -> CTabNgramType
modeTabType Authors = CTabAuthors
modeTabType Sources = CTabSources
modeTabType Institutes = CTabInstitutes
modeTabType Sources = CTabSources
modeTabType Terms = CTabTerms
modeFromString :: String -> Maybe Mode
modeFromString "Authors" = Just Authors
modeFromString "Sources" = Just Sources
modeFromString "Institutes" = Just Institutes
modeFromString "Sources" = Just Sources
modeFromString "Terms" = Just Terms
modeFromString _ = Nothing
......@@ -582,7 +580,7 @@ modeFromString _ = Nothing
-- corresponds to /add/form/async or /add/query/async
data AsyncTaskType = Form
| UploadFile
| GraphT
| GraphRecompute
| Query
| AddNode
| UpdateNode
......@@ -600,7 +598,7 @@ instance decodeJsonAsyncTaskType :: DecodeJson AsyncTaskType where
case obj of
"Form" -> pure Form
"UploadFile" -> pure UploadFile
"GraphT" -> pure GraphT
"GraphRecompute" -> pure GraphRecompute
"Query" -> pure Query
"AddNode" -> pure AddNode
s -> Left $ AtKey s $ TypeMismatch "Unknown string"
......@@ -609,7 +607,7 @@ asyncTaskTypePath :: AsyncTaskType -> String
asyncTaskTypePath Form = "add/form/async/"
asyncTaskTypePath UploadFile = "async/file/add/"
asyncTaskTypePath Query = "query/"
asyncTaskTypePath GraphT = "async/"
asyncTaskTypePath GraphRecompute = "async/recompute/"
asyncTaskTypePath AddNode = "async/nobody/"
asyncTaskTypePath UpdateNode = "update/"
......
......@@ -16,7 +16,7 @@ exports._openCache = function(cacheName) {
exports._delete = function(cache) {
return function(req) {
return function() {
cache.delete(req);
return cache.delete(req);
}
}
}
......
......@@ -4,13 +4,14 @@ import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Prelude
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Config.REST as REST
import Gargantext.Ends (toUrl)
import Gargantext.Sessions (Session(..))
import Gargantext.Sessions as Sessions
import Prelude
import Reactix as R
import Reactix.DOM.HTML as H
type Version = String
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment