Move NetworkSelector to widgets

This commit is contained in:
Ivan Danyliuk 2018-10-23 06:42:56 +02:00
parent 1a6f3b1366
commit 2064fa5201
No known key found for this signature in database
GPG Key ID: 97ED33CE024E1DBF
14 changed files with 12570 additions and 12493 deletions

View File

@ -1,12 +1,8 @@
package main
import (
"encoding/json"
"errors"
"io"
"runtime"
"github.com/divan/graphx/graph"
"github.com/gopherjs/vecty"
)
@ -41,44 +37,3 @@ func (p *Page) ApplyForces() {
p.webgl.updatePositions()
p.webgl.rt.Disable()
}
// GraphFromJSON is a custom version of graphx JSON importer, as we want to use
// some additional fields (Description).
// TODO(divan): that's probably can be done better within the limits of graphx library.
func GraphFromJSON(r io.Reader) (*graph.Graph, string, error) {
// decode into temporary struct to process
var res struct {
Description string `json:"description"`
Nodes []*graph.BasicNode `json:"nodes"`
Links []*struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"links"`
}
err := json.NewDecoder(r).Decode(&res)
if err != nil {
return nil, "", err
}
if len(res.Nodes) == 0 {
return nil, "", errors.New("empty graph")
}
// convert links IDs into indices
g := graph.NewGraphMN(len(res.Nodes), len(res.Links))
for _, node := range res.Nodes {
g.AddNode(node)
}
for _, link := range res.Links {
err := g.AddLink(link.Source, link.Target)
if err != nil {
return nil, "", err
}
}
g.UpdateCache()
return g, res.Description, nil
}

View File

@ -1,70 +0,0 @@
//go:generate go-bindata data/
package main
import (
"bytes"
"fmt"
"io"
"github.com/divan/graphx/graph"
)
// Network represents network graph and information, used for
// for simulation and visualization.
type Network struct {
Name string
Description string
Data *graph.Graph
}
// LoadNetwork loads network information from the JSON file.
// JSON format is specified in graphx/formats package.
func LoadNetwork(file string) (*Network, error) {
content, err := Asset(file)
if err != nil {
return nil, fmt.Errorf("open bindata '%s': %v", file, err)
}
r := bytes.NewReader(content)
n, err := LoadNetworkFromReader(r)
if err != nil {
return nil, fmt.Errorf("open file '%s': %v", file, err)
}
n.Name = file
return n, nil
}
// LoadNetworkFromReader loads network information from the io.Reader.
func LoadNetworkFromReader(r io.Reader) (*Network, error) {
g, desc, err := GraphFromJSON(r)
if err != nil {
return nil, fmt.Errorf("parse JSON: %v", err)
}
return &Network{
Description: desc,
Data: g,
}, nil
}
// String implements Stringer for Network.
func (n *Network) String() string {
return fmt.Sprintf("[%s: %s] - %d nodes, %d links", n.Name, n.Description, n.NodesCount(), n.LinksCount())
}
// NodesCount returns number of the nodes in the network.
func (n *Network) NodesCount() int {
if n.Data == nil {
return 0
}
return len(n.Data.Nodes())
}
// LinksCount returns number of the links in the network.
func (n *Network) LinksCount() int {
if n.Data == nil {
return 0
}
return len(n.Data.Links())
}

View File

@ -6,7 +6,7 @@
// data/net300.json
// DO NOT EDIT!
package main
package network
import (
"bytes"

132
network/network.go Normal file
View File

@ -0,0 +1,132 @@
//go:generate go-bindata -pkg network data/
package network
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/divan/graphx/graph"
)
// Network represents network graph and information, used for
// for simulation and visualization.
type Network struct {
Name string
Description string
Data *graph.Graph
}
// LoadNetwork loads network information from the JSON file.
// JSON format is specified in graphx/formats package.
func LoadNetwork(file string) (*Network, error) {
content, err := Asset(file)
if err != nil {
return nil, fmt.Errorf("open bindata '%s': %v", file, err)
}
r := bytes.NewReader(content)
n, err := LoadNetworkFromReader(r)
if err != nil {
return nil, fmt.Errorf("open file '%s': %v", file, err)
}
n.Name = file
return n, nil
}
// LoadNetworks imports preloaded neworks from the directory with JSON files.
func LoadNetworks() (map[string]*Network, error) {
files, err := AssetDir("data")
if err != nil {
return nil, err
}
networks := map[string]*Network{}
for _, file := range files {
network, err := LoadNetwork("data/" + file)
if err != nil {
return nil, fmt.Errorf("load network: %v", err)
}
networks[file] = network
}
return networks, nil
}
// LoadNetworkFromReader loads network information from the io.Reader.
func LoadNetworkFromReader(r io.Reader) (*Network, error) {
g, desc, err := GraphFromJSON(r)
if err != nil {
return nil, fmt.Errorf("parse JSON: %v", err)
}
return &Network{
Description: desc,
Data: g,
}, nil
}
// String implements Stringer for Network.
func (n *Network) String() string {
return fmt.Sprintf("[%s: %s] - %d nodes, %d links", n.Name, n.Description, n.NodesCount(), n.LinksCount())
}
// NodesCount returns number of the nodes in the network.
func (n *Network) NodesCount() int {
if n.Data == nil {
return 0
}
return len(n.Data.Nodes())
}
// LinksCount returns number of the links in the network.
func (n *Network) LinksCount() int {
if n.Data == nil {
return 0
}
return len(n.Data.Links())
}
// GraphFromJSON is a custom version of graphx JSON importer, as we want to use
// some additional fields (Description).
// TODO(divan): that's probably can be done better within the limits of graphx library.
func GraphFromJSON(r io.Reader) (*graph.Graph, string, error) {
// decode into temporary struct to process
var res struct {
Description string `json:"description"`
Nodes []*graph.BasicNode `json:"nodes"`
Links []*struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"links"`
}
err := json.NewDecoder(r).Decode(&res)
if err != nil {
return nil, "", err
}
if len(res.Nodes) == 0 {
return nil, "", errors.New("empty graph")
}
// convert links IDs into indices
g := graph.NewGraphMN(len(res.Nodes), len(res.Links))
for _, node := range res.Nodes {
g.AddNode(node)
}
for _, link := range res.Links {
err := g.AddLink(link.Source, link.Target)
if err != nil {
return nil, "", err
}
}
g.UpdateCache()
return g, res.Description, nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/gopherjs/vecty/elem"
"github.com/gopherjs/vecty/event"
"github.com/gopherjs/vecty/prop"
"github.com/status-im/whispervis/network"
"github.com/status-im/whispervis/widgets"
)
@ -24,7 +25,7 @@ type Page struct {
loader *widgets.Loader
forceEditor *widgets.ForceEditor
network *NetworkSelector
network *widgets.NetworkSelector
simulationWidget *widgets.Simulation
statsWidget *widgets.Stats
@ -37,7 +38,7 @@ func NewPage() *Page {
loader: widgets.NewLoader(),
}
page.forceEditor = widgets.NewForceEditor(page.onForcesApply)
page.network = NewNetworkSelector(page.onNetworkChange)
page.network = widgets.NewNetworkSelector(page.onNetworkChange)
page.webgl = NewWebGLScene()
page.simulationWidget = widgets.NewSimulation("http://localhost:8084", page.startSimulation, page.replaySimulation)
page.statsWidget = widgets.NewStats()
@ -120,7 +121,7 @@ func (p *Page) onForcesApply() {
p.UpdateGraph()
}
func (p *Page) onNetworkChange(network *Network) {
func (p *Page) onNetworkChange(network *network.Network) {
fmt.Println("Network changed:", network)
config := p.forceEditor.Config()
p.layout = layout.New(network.Data, config.Config)

View File

@ -42,7 +42,7 @@ func (p *Page) runSimulation(address string) (*Simulation, error) {
// currentNetworkJSON returns JSON encoded description of the current graph/network.
func (p *Page) currentNetworkJSON() []byte {
net := p.network.current.Data
net := p.network.Current().Data
var buf bytes.Buffer
err := formats.NewD3JSON(&buf, true).ExportGraph(net)
if err != nil {

View File

@ -6,7 +6,7 @@ import (
)
func (p *Page) RecalculateStats(plog *propagation.Log) *stats.Stats {
net := p.network.current
net := p.network.Current()
nodes := len(net.Data.Nodes())
links := len(net.Data.Links())

24748
whispervis.js generated

File diff suppressed because one or more lines are too long

2
whispervis.js.map generated

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
package main
package widgets
import (
"bytes"
@ -7,7 +7,7 @@ import (
"github.com/gopherjs/vecty"
"github.com/gopherjs/vecty/elem"
"github.com/gopherjs/vecty/event"
"github.com/status-im/whispervis/widgets"
"github.com/status-im/whispervis/network"
)
// DefaultNetwork specifies the network shown by default when app is first opened.
@ -16,23 +16,22 @@ const DefaultNetwork = "grid25.json"
// NetworkSelector represents widget for choosing or uploading network topology
// to be used for visualization.
// TODO: move to widgets package
type NetworkSelector struct {
vecty.Core
current *Network
current *network.Network
isCustom bool
networks map[string]*Network
networks map[string]*network.Network
upload *widgets.UploadWidget
upload *UploadWidget
handler func(*Network) // executed on network change
handler func(*network.Network) // executed on network change
}
// NewNetworkSelector creates new NetworkSelector.
func NewNetworkSelector(handler func(*Network)) *NetworkSelector {
current := &Network{}
networks, err := LoadNetworks()
func NewNetworkSelector(handler func(*network.Network)) *NetworkSelector {
current := &network.Network{}
networks, err := network.LoadNetworks()
if err != nil {
fmt.Println("No networks loaded:", err)
} else {
@ -44,15 +43,15 @@ func NewNetworkSelector(handler func(*Network)) *NetworkSelector {
current: current,
handler: handler,
}
ns.upload = widgets.NewUploadWidget(ns.onUpload)
ns.upload = NewUploadWidget(ns.onUpload)
ns.setCurrentNetwork(current)
return ns
}
// Render implements the vecty.Component interface.
func (n *NetworkSelector) Render() vecty.ComponentOrHTML {
return widgets.Widget(
widgets.Header("Network graph:"),
return Widget(
Header("Network graph:"),
elem.Div(
vecty.Markup(
vecty.Class("select", "is-fullwidth"),
@ -94,25 +93,6 @@ func (n *NetworkSelector) descriptionBlock() *vecty.HTML {
)
}
// LoadNetworks imports preloaded neworks from the directory with JSON files.
func LoadNetworks() (map[string]*Network, error) {
files, err := AssetDir("data")
if err != nil {
return nil, err
}
networks := map[string]*Network{}
for _, file := range files {
network, err := LoadNetwork("data/" + file)
if err != nil {
return nil, fmt.Errorf("load network: %v", err)
}
networks[file] = network
}
return networks, nil
}
// networkOptions renders 'option' elements for network 'select' input tag.
func (n *NetworkSelector) networkOptions() vecty.List {
var options vecty.List
@ -149,7 +129,7 @@ func (n *NetworkSelector) onChange(e *vecty.Event) {
// onUpload implements callback for "Upload" button clicked event.
func (n *NetworkSelector) onUpload(json []byte) {
r := bytes.NewReader(json)
net, err := LoadNetworkFromReader(r)
net, err := network.LoadNetworkFromReader(r)
if err != nil {
fmt.Printf("[ERROR] Load network: %v", err)
}
@ -162,8 +142,13 @@ func (n *NetworkSelector) onUpload(json []byte) {
vecty.Rerender(n)
}
// Current returns the currently selected network.
func (n *NetworkSelector) Current() *network.Network {
return n.current
}
// setCurrentNetwork changes current network and runs needed update handlers.
func (n *NetworkSelector) setCurrentNetwork(net *Network) {
func (n *NetworkSelector) setCurrentNetwork(net *network.Network) {
n.current = net
if n.handler != nil {