Commit f17d0602 authored by Alexandre Delanoë's avatar Alexandre Delanoë

Merge remote-tracking branch 'origin/sigma-merge'

parents 4d66dce9 da689190
.
.logoSmall { .logoSmall {
line-height:15px; line-height:15px;
height:10px; height:10px;
...@@ -6,6 +7,51 @@ ...@@ -6,6 +7,51 @@
} }
#logo-designed {
border:15px;
}
#logo-designed img {
height:150px;
border:3px solid white;
}
#page-wrapper {
margin-top : 96px;
}
#user-page-header {
border-bottom : 1px solid black;
}
#user-page-info {
margin-top : 38px;
}
.tableHeader {
background-color : blue;
color: white;
}
#toolbar ul li
{ margin-right : 19px;
}
#horizontal-checkbox ul li
{ display : inline;
float : left;
margin-top: 12px;
margin-right : 21px;
}
logoSmall {
line-height:15px;
height:10px;
padding: 10px 10px;
}
#logo-designed { #logo-designed {
border:15px; border:15px;
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -8,8 +8,7 @@ ...@@ -8,8 +8,7 @@
"react": "^16.2.0", "react": "^16.2.0",
"react-dom": "^16.2.0", "react-dom": "^16.2.0",
"react-echarts-v3": "^1.0.14", "react-echarts-v3": "^1.0.14",
"sigma": "^1.2.1", "react-sigma": "^1.2.30"
"graph-explorer": "git+ssh://git@gitlab.iscpif.fr:20022/gargantext/graphExplorer.git"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
module GraphExplorer where
import Prelude hiding (div)
import Control.Monad.Aff (runAff)
import Control.Monad.Aff.Class (liftAff)
import Control.Monad.Aff.Console (CONSOLE)
import Control.Monad.Cont.Trans (lift)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Console (log)
import DOM (DOM)
import DOM.File.FileReader (fileReader, readAsText, result)
import DOM.File.Types (File, fileToBlob)
import React (ReactClass, createElement)
import React.DOM (button, button', div, form', input, li, li', menu, text, ul')
import React.DOM.Props (_id, _type, className, name, onChange, onClick, placeholder, style, value)
import Thermite (PerformAction, Render, Spec, modifyState, simpleSpec)
foreign import data GraphData :: Type
foreign import initialGraph :: GraphData
foreign import initialFile :: File
foreign import getFile :: forall e. e -> File
foreign import parseJSON :: forall eff a. a -> Eff eff GraphData
foreign import graphExplorerComponent :: ReactClass {graph :: GraphData, mode :: String}
foreign import logger :: forall a eff. a -> Eff eff Unit
newtype State = State {mode :: String, graph :: GraphData, file :: File}
data Action
= SetGraph
| SetFile File
initialState :: State
initialState = State {mode : "select", graph : initialGraph, file : initialFile}
reader :: forall eff. File -> Eff (console :: CONSOLE, dom :: DOM | eff) GraphData
reader f = do
fr <- fileReader
readAsText (fileToBlob f) fr
res <- result fr
--log $ show res
let da = parseJSON res
logger da
da
spec :: forall eff props. Spec (console :: CONSOLE, dom :: DOM | eff) State props Action
spec = simpleSpec performAction render
where
render :: Render State props Action
render d _ (State st) _ =
[ div [className "row"] [
div [className "col-md-12"]
[ menu [_id "toolbar"]
[ ul'
[ li [style {display : "inline-block"}]
[ form'
[ input [_type "file", name "file", onChange (\e -> d $ SetFile $ getFile e)] []
, input [_type "button", value "submit", onClick \_ -> d SetGraph] []
]
]
, li'
[ button [className "btn btn-success btn-sm"] [text "Change Type"]
]
, li'
[ button [className "btn btn-primary btn-sm"] [text "Change Level"]
]
, li'
[ form'
[ input [_type "text", name "query", placeholder "Select Topics"] []
, input [_type "submit", value "Search"] []
]
]
, li'
[ button' [text "Screenshot"]]
, li'
[ button' [text "Save"] -- TODO: Implement Save!
]
]
]
]
]
, div [className "row"]
[ div [className "col-md-8"]
[ div [style {border : "1px black solid", height: "90%"}]
[ text "GraphExplorer here...."
, createElement graphExplorerComponent { graph : st.graph
, mode : st.mode
} []
]
]
, div [className "col-md-4"]
[ div [_id "sidepanel", style {border : "1px black solid", height: "90%"}]
[ text "SidePanel for contextual information"
]
]
]
]
performAction :: PerformAction (console :: CONSOLE, dom :: DOM | eff) State props Action
performAction SetGraph _ (State st) = void do
gd <- liftEff $ reader st.file
modifyState \(State s) -> State $ s {graph = gd}
performAction (SetFile f) _ _ = void do
modifyState \(State s) -> State $ s {file = f}
'use strict';
exports.isNull = function(v) {
return v === null;
};
module GraphExplorer.DecodeMaybe where
import Prelude
import Data.Argonaut (class DecodeJson, JObject, getFieldOptional)
import Data.Either (Either)
import Data.Maybe (Maybe(..))
foreign import isNull :: forall a. a -> Boolean
getFieldOptional' :: forall a. DecodeJson a => JObject -> String -> Either String (Maybe a)
getFieldOptional' o s = (case _ of
Just v -> if isNull v then Nothing else v
Nothing -> Nothing
) <$> (getFieldOptional o s)
infix 7 getFieldOptional' as .?|
'use strict';
const SJS = require('react-sigma');
const FL = require('react-sigma/lib/ForceLink');
exports.edgeShapesClass = SJS.EdgeShapes;
exports.filterClass = SJS.Filter;
exports.forceAtlas2Class = SJS.ForceAtlas2;
exports.loadGEXFClass = SJS.LoadGEXF;
exports.loadJSONClass = SJS.LoadJSON;
exports.nOverlapClass = SJS.NOverlap;
exports.neoCypherClass = SJS.NeoCypher;
exports.neoGraphItemsProducersClass = SJS.NeoGraphItemsProducers;
exports.nodeShapesClass = SJS.NodeShapes;
exports.randomizeNodePositionsClass = SJS.RandomizeNodePositions;
exports.relativeSizeClass = SJS.RelativeSize;
exports.sigmaClass = SJS.Sigma;
exports.sigmaEnableSVGClass = SJS.SigmaEnableSVG;
exports.sigmaEnableWebGLClass = SJS.SigmaEnableWebGL;
exports.forceLinkClass = FL.default;
This diff is collapsed.
module GraphExplorer.Types where
import Prelude
import Data.Argonaut (class DecodeJson, decodeJson, (.?))
import Data.Array (concat, group, head, length, sort, take)
import Data.Maybe (fromJust)
import Data.Newtype (class Newtype)
import Data.NonEmpty (NonEmpty(..))
import Partial.Unsafe (unsafePartial)
newtype Node = Node
{ id_ :: String
, size :: Int
, type_ :: String
, label :: String
, attributes :: Cluster
}
derive instance newtypeNode :: Newtype Node _
newtype Cluster = Cluster { clustDefault :: Int }
derive instance newtypeCluster :: Newtype Cluster _
newtype Edge = Edge
{ id_ :: String
, source :: String
, target :: String
, weight :: Number
}
derive instance newtypeEdge :: Newtype Edge _
newtype GraphData = GraphData
{ nodes :: Array Node
, edges :: Array Edge
}
derive instance newtypeGraphData :: Newtype GraphData _
instance decodeJsonGraphData :: DecodeJson GraphData where
decodeJson json = do
obj <- decodeJson json
nodes <- obj .? "nodes"
edges <- obj .? "edges"
pure $ GraphData { nodes, edges }
instance decodeJsonNode :: DecodeJson Node where
decodeJson json = do
obj <- decodeJson json
id_ <- obj .? "id"
type_ <- obj .? "type"
label <- obj .? "label"
size <- obj .? "size"
attributes <- obj .? "attributes"
pure $ Node { id_, type_, size, label, attributes }
instance decodeJsonCluster :: DecodeJson Cluster where
decodeJson json = do
obj <- decodeJson json
clustDefault <- obj .? "clust_default"
pure $ Cluster { clustDefault }
instance decodeJsonEdge :: DecodeJson Edge where
decodeJson json = do
obj <- decodeJson json
id_ <- obj .? "id"
source <- obj .? "source"
target <- obj .? "target"
weight <- obj .? "weight"
pure $ Edge { id_, source, target, weight }
newtype Legend = Legend {id_ ::Int , label :: String}
instance eqLegend :: Eq Legend where
eq (Legend l1) (Legend l2) = eq l1.id_ l2.id_
instance ordLegend :: Ord Legend where
compare (Legend l1) (Legend l2) = compare l1.id_ l2.id_
getLegendData :: GraphData -> Array Legend
getLegendData (GraphData {nodes, edges}) = nn
where
mp (NonEmpty a ary) = [a] <> (if length ary > 0 then [unsafePartial $ fromJust $ head ary] else [])
n = sort $ map t' nodes
g = group n
nn = take 5 $ concat $ map mp g
t' :: Node -> Legend
t' (Node r) = Legend { id_ : clustDefault, label : r.label}
where
(Cluster {clustDefault}) = r.attributes
...@@ -4,6 +4,7 @@ import DOM ...@@ -4,6 +4,7 @@ import DOM
import Gargantext.Data.Lang import Gargantext.Data.Lang
import Prelude hiding (div) import Prelude hiding (div)
import AddCorpusview as AC import AddCorpusview as AC
import DocAnnotation as D import DocAnnotation as D
import Control.Monad.Cont.Trans (lift) import Control.Monad.Cont.Trans (lift)
...@@ -18,7 +19,6 @@ import Data.Lens (Lens', Prism', lens, over, prism) ...@@ -18,7 +19,6 @@ import Data.Lens (Lens', Prism', lens, over, prism)
import Data.Maybe (Maybe(Nothing, Just), fromJust) import Data.Maybe (Maybe(Nothing, Just), fromJust)
import Data.Tuple (Tuple(..)) import Data.Tuple (Tuple(..))
import DocView as DV import DocView as DV
import GraphExplorer as GE
import Landing as L import Landing as L
import Login as LN import Login as LN
import Modal (modalShow) import Modal (modalShow)
...@@ -37,9 +37,10 @@ import Tabview as TV ...@@ -37,9 +37,10 @@ import Tabview as TV
import Thermite (PerformAction, Render, Spec, _render, cotransform, defaultPerformAction, defaultRender, focus, modifyState, simpleSpec, withState) import Thermite (PerformAction, Render, Spec, _render, cotransform, defaultPerformAction, defaultRender, focus, modifyState, simpleSpec, withState)
import Unsafe.Coerce (unsafeCoerce) import Unsafe.Coerce (unsafeCoerce)
import UserPage as UP import UserPage as UP
import GraphExplorer as GE
import NgramsTable as NG import NgramsTable as NG
import Dashboard as Dsh import Dashboard as Dsh
import Graph as GE
type E e = (dom :: DOM, ajax :: AJAX, console :: CONSOLE | e) type E e = (dom :: DOM, ajax :: AJAX, console :: CONSOLE | e)
...@@ -315,10 +316,10 @@ pagesComponent s = ...@@ -315,10 +316,10 @@ pagesComponent s =
-- To be removed -- To be removed
selectSpec SearchView = layout0 $ focus _searchState _searchAction S.searchSpec selectSpec SearchView = layout0 $ focus _searchState _searchAction S.searchSpec
selectSpec NGramsTable = layout0 $ focus _ngState _ngAction NG.ngramsTableSpec selectSpec NGramsTable = layout0 $ focus _ngState _ngAction NG.ngramsTableSpec
selectSpec PGraphExplorer = focus _graphExplorerState _graphExplorerAction GE.spec selectSpec PGraphExplorer = focus _graphExplorerState _graphExplorerAction GE.specOld
selectSpec Dashboard = layout0 $ focus _dashBoardSate _dashBoardAction Dsh.layoutDashboard selectSpec Dashboard = layout0 $ focus _dashBoardSate _dashBoardAction Dsh.layoutDashboard
selectSpec _ = simpleSpec defaultPerformAction defaultRender -- selectSpec _ = simpleSpec defaultPerformAction defaultRender
routingSpec :: forall props eff. Spec (ajax :: AJAX, console :: CONSOLE, dom :: DOM |eff) AppState props Action routingSpec :: forall props eff. Spec (ajax :: AJAX, console :: CONSOLE, dom :: DOM |eff) AppState props Action
routingSpec = simpleSpec performAction defaultRender routingSpec = simpleSpec performAction defaultRender
...@@ -645,7 +646,7 @@ dispatchAction dispatcher _ CorpusAnalysis = do ...@@ -645,7 +646,7 @@ dispatchAction dispatcher _ CorpusAnalysis = do
dispatchAction dispatcher _ PGraphExplorer = do dispatchAction dispatcher _ PGraphExplorer = do
_ <- dispatcher $ SetRoute $ PGraphExplorer _ <- dispatcher $ SetRoute $ PGraphExplorer
--_ <- dispatcher $ GraphExplorerA $ GE.NoOp _ <- dispatcher $ GraphExplorerA $ GE.LoadGraph "imtNew.json"
pure unit pure unit
dispatchAction dispatcher _ NGramsTable = do dispatchAction dispatcher _ NGramsTable = do
......
var React = require('react');
var PropTypes = require('prop-types');
var graphExplorer = require('graph-explorer');
console.log(graphExplorer);
const GraphExplorer = graphExplorer.default;
class ReactGraphExplorer extends React.Component {
constructor(props) {
super(props);
this.ge = new GraphExplorer(props.settings, props.handlers);
this.state = { graph: props.graph, mode: props.mode };
this.initRenderer = this.initRenderer.bind(this);
}
componentWillReceiveProps(nextProps) {
console.log(this.ge);
if (nextProps.graph) {
this.setState({ graph: nextProps.graph }, () => {
this.ge.loadGraph(this.state.graph);
this.ge.clusterize();
this.ge.spatialize();
});
}
this.setState({ mode: nextProps.mode }, () => {
this.ge.sigma.settings('mode', this.state.mode);
});
}
initRenderer(container) {
this.ge.addRenderer(container);
}
render() {
return React.createElement('div', { id: 'ge-container', ref: this.initRenderer }, null);
}
}
ReactGraphExplorer.propTypes = {
graph: PropTypes.shape({
nodes: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
label: PropTypes.string,
x: PropTypes.number,
y: PropTypes.number,
size: PropTypes.number,
type: PropTypes.string,
attributes: PropTypes.object // TODO(lucas): Specify this further.
})
).isRequired,
edges: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
target: PropTypes.string.isRequired,
weight: PropTypes.number // NOTE(lucas): required?
}).isRequired
)
}),
settings: PropTypes.shape({
// TODO(lucas)
}),
mode: PropTypes.string,
handlers: PropTypes.shape({
overNode: PropTypes.func,
outNode: PropTypes.func,
clickNode: PropTypes.func,
doubleClickNode: PropTypes.func,
rightClickNode: PropTypes.func,
overEdge: PropTypes.func,
outEdge: PropTypes.func,
clickEdge: PropTypes.func,
doubleClickEdge: PropTypes.func,
rightClickEdge: PropTypes.func,
clickStage: PropTypes.func,
doubleClickStage: PropTypes.func,
rightClickStage: PropTypes.func
})
};
export default ReactGraphExplorer;
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
This diff is collapsed.
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