Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bot Networks #45

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5386967
Display a page
s2t2 Oct 30, 2020
a8b8a7a
Something is happening - looks like a blue and red death star
s2t2 Oct 30, 2020
685da6d
OK just needed to get the data mapped properly, to define the levels
s2t2 Oct 30, 2020
aec7b52
Container size
s2t2 Oct 30, 2020
1f69a18
Code review
s2t2 Oct 30, 2020
d90f747
Combining files, customizing to show real data. starts looking like a…
s2t2 Oct 30, 2020
94a165d
Networks are untagled after removing the dagmode param; getting close…
s2t2 Oct 30, 2020
f4f23f2
Label with handles
s2t2 Oct 30, 2020
1c1190d
Let me see these on the same page while prototyping
s2t2 Oct 30, 2020
c8f8c98
Let's see if we can get the top-down direction to stick
s2t2 Oct 30, 2020
ffe2f35
This is good enough to show for a prototype. need different data for …
s2t2 Oct 30, 2020
0e1aa64
Explain what this is
s2t2 Oct 30, 2020
5d19ca9
Hide the prototype
s2t2 Oct 30, 2020
2028054
Why are the nodes so large on staging but not local
s2t2 Oct 30, 2020
05809e7
More decay prevents nodes from flying away
s2t2 Oct 30, 2020
a2f0079
Narration
s2t2 Oct 30, 2020
d2e6436
Node click to view opinion dashboard
s2t2 Oct 30, 2020
f4721bb
Event tracking
s2t2 Oct 30, 2020
ba1cdcc
Copy edit
s2t2 Oct 30, 2020
3fcbbdd
Test update svg width on window resize
s2t2 Oct 31, 2020
b797fd4
Yup
s2t2 Oct 31, 2020
12ebcf5
Update child width based on container width
s2t2 Oct 31, 2020
b3be446
Better default graph size
s2t2 Oct 31, 2020
6c5d47f
Re-render graph on window-resize, updates width properly but for some…
s2t2 Nov 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
717 changes: 717 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"react-bootstrap": "^1.3.0",
"react-bootstrap-range-slider": "^1.2.2",
"react-dom": "^16.13.1",
"react-force-graph": "^1.37.1",
"react-ga": "^3.1.2",
"react-gauge-chart": "^0.2.5",
"react-plotly.js": "^2.5.0",
Expand Down
49 changes: 49 additions & 0 deletions src/_App/BotAnalysis/Networks/ForceTree1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// source: https://github.com/vasturiano/react-force-graph/blob/master/example/tree/index.html

import React, {useEffect, useRef } from 'react' // useState
import { ForceGraph2D } from 'react-force-graph'
import * as d3 from 'd3'

//const useForceUpdate = () => {
// const setToggle = useState(false)[1]
// return () => setToggle(b => !b)
//}

const ForceTree = ({ data }) => {
const fgRef = useRef()

//const [controls] = useState({ 'DAG Orientation': 'td'});
//const forceUpdate = useForceUpdate()

//useEffect(() => {
// // add controls GUI
// const gui = new dat.GUI()
// gui.add(controls, 'DAG Orientation', ['td', 'bu', 'lr', 'rl', 'radialout', 'radialin', null])
// .onChange(forceUpdate)
//}, [])

useEffect(() => {
// add collision force
fgRef.current.d3Force('collision', d3.forceCollide(node => Math.sqrt(100 / (node.level + 1))));
}, [])

console.log("GRAPH DATA", data)
return <ForceGraph2D width={900} height={550}
ref={fgRef}
graphData={data}
dagMode="td" // dagMode={controls['DAG Orientation']}
dagLevelDistance={300}
backgroundColor="#101020"
linkColor={() => 'rgba(255,255,255,0.2)'}
nodeRelSize={1}
nodeId="path"
nodeVal={node => 100 / (node.level + 1)}
nodeLabel="path"
nodeAutoColorBy="module"
linkDirectionalParticles={2}
linkDirectionalParticleWidth={2}
d3VelocityDecay={0.3}
/>
}

export default ForceTree
137 changes: 137 additions & 0 deletions src/_App/BotAnalysis/Networks/Graph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@

// adapted from source: https://github.com/vasturiano/react-force-graph/blob/master/example/tree/index.html
// https://github.com/vasturiano/react-force-graph#data-input

import React, { PureComponent, createRef, useEffect, useRef} from 'react'
import { ForceGraph2D } from 'react-force-graph'
import * as d3 from 'd3'
import Card from 'react-bootstrap/Card'
//import Row from 'react-bootstrap/Row'
//import Col from 'react-bootstrap/Col'
import ReactGA from 'react-ga'

import Spinner from '../../Spinner'
import cachedData from './data'

//function MyGraph(props){
// var height = props.height || 500
// var width = props.width || 300 // todo: use container ref instead
//
// return <span>
// <p>Height: {height} Width: {width}</p>
// <svg height={height} width={width} style={{border:"1px solid black"}}></svg>
// </span>
//}

function nodeClickHandler(node, event){
console.log("YOU CLICKED", node["id"])
ReactGA.event({category: "Bot Networks", action: "Click Node", label: node["id"]})
//window.open(`https://twitter.com/${node["id"]}`, '_blank')
window.open(`/user-opinions?sn=${node["id"]}`, '_blank')
}

function MyGraph(props){
var height = props.height || 500
var width = props.width || 300 // todo: use container ref instead
var data = props.data

data["nodes"] = data["nodes"].map(function(node){
return {
"id": node["id"], // NODE ID
"mean_opinion": node["opinion"],
"status_count": node["Rate"],
"color": node["color"], // NODE COLOR
//"x": node["xcoord"],
//"y": node["ycoord"],
}
})

data["links"] = data["links"].map(function(link){
return {
"source": link["source"], // SOURCE NODE ID
"target": link["target"], // TARGET NODE ID
//"weight": link["weight"],
}
})

//console.log("GRAPH DATA", data)

const containerRef = useRef()
useEffect(() => {
// add collision force
//containerRef.current.d3Force('collision', d3.forceCollide(node => Math.sqrt(100 / (node.level + 1))));
//containerRef.current.d3Force('collision', d3.forceCollide(node => Math.sqrt(node["Rate"])));
containerRef.current.d3Force('collision', d3.forceCollide(node => Math.sqrt(50)));
}, [])

return <ForceGraph2D ref={containerRef} width={width} height={height} backgroundColor="#101020"
graphData={data}

nodeId="id"
//nodeVal="status_count"
//nodeVal={node => Math.sqrt(node["status_count"])}
nodeVal={5}
nodeLabel={(node) => `@${node["id"]}`}
nodeRelSize={1}
onNodeClick={nodeClickHandler}

//dagMode="td"
//dagLevelDistance={300}
linkColor={() => 'rgba(255,255,255,0.2)'} // todo: source opinion color
linkDirectionalParticles={2}
linkDirectionalParticleWidth={2}
d3VelocityDecay={0.6} // lower numbers push the nodes farther and faster from eachother (keep large to prevent red bots from flying off screen)
/>
}

export default class NetworkGraph extends PureComponent {
constructor(props) {
super(props)
this.state = {parsedResponse: null, width: null}
this.containerRef = createRef()
this.handleResize = this.handleResize.bind(this)
}

render() {
return (
<Card style={{marginBottom:0}}>
<Card.Body>
{!this.state.parsedResponse ?
<Spinner/> :

<div ref={this.containerRef}>
<MyGraph data={this.state.parsedResponse}
width={this.state.width} // so it will re-render on window resize
/>
</div>
}
</Card.Body>
</Card>
)
}

componentDidMount(){
console.log("COMPONENT DID MOUNT")
window.addEventListener("resize", this.handleResize)
//const width = this.containerRef.current.clientWidth // containerRef is null here
const width = window.innerWidth * 0.74
this.setState({parsedResponse:cachedData, width: width})
}

componentWillUnmount() {
console.log("COMPONENT WILL UNMOUNT")
window.removeEventListener("resize", this.handleResize);
}

handleResize(event){
const width = this.containerRef.current.clientWidth // window.innerWidth * 0.75
//const width = this.containerRef.current.width
console.log("RESIZED THE WINDOW!!", width)
this.setState({width: width})
}

componentDidUpdate(){
//console.log("COMPONENT DID UPDATE")
}

}
109 changes: 109 additions & 0 deletions src/_App/BotAnalysis/Networks/Graph1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { PureComponent} from 'react' //createRef, useState, useEffect, useRef
import * as d3 from 'd3'

import Card from 'react-bootstrap/Card'
//import Row from 'react-bootstrap/Row'
//import Col from 'react-bootstrap/Col'

import Spinner from '../../Spinner'
import cachedData from './data'
import ForceTree from './ForceTree1'

const MOCK_DATA_MODE = true

export default class NetworkGraph extends PureComponent {
constructor(props) {
super(props)
this.state = {parsedResponse: null} // orientation: "td",
//this.containerRef = createRef()
}

render() {
return (
<Card style={{marginBottom:0}}>
<Card.Body>
{!this.state.parsedResponse ?
<Spinner/> :
//<ForceGraph2D
// ref={this.containerRef}
// graphData={this.state.parsedResponse}
// dagMode={this.state.orientation}
// dagLevelDistance={300}
// backgroundColor="#fff" //"#101020"
// linkColor={() => 'rgba(255,255,255,0.2)'}
// nodeRelSize={1}
// nodeId="path"
// nodeVal={node => 100 / (node.level + 1)}
// nodeLabel="path"
// nodeAutoColorBy="module"
// linkDirectionalParticles={2}
// linkDirectionalParticleWidth={2}
// d3VelocityDecay={0.3}
///>
//<ForceTree data={{ nodes, links }}/>
<ForceTree data={this.state.parsedResponse}/>
//<Row>
// <Col md={12}>
// <ForceTree data={this.state.parsedResponse}/>
// </Col>
//</Row>
}
</Card.Body>
</Card>
)
}

componentDidMount() {
console.log("DASHBOARD DID MOUNT")

if(MOCK_DATA_MODE){
this.fetchData()
} else {
this.setState({parsedResponse: cachedData})
//setTimeout(function(){
// this.setState({parsedResponse: cachedData})
//}.bind(this), 1000) // let you see the spinner
}
}

fetchData() {
// https://github.com/vasturiano/react-force-graph/blob/master/example/tree/index.html
const requestUrl = "https://raw.githubusercontent.com/vasturiano/react-force-graph/master/example/datasets/d3-dependencies.csv"
console.log("REQUEST URL:", requestUrl)
fetch(requestUrl)
.then(r => r.text())
.then(d3.csvParse)
.then(data => {
const nodes = [], links = []

data.forEach(({ size, path }) => {
const levels = path.split('/')
const level = levels.length - 1
const module = level > 0 ? levels[1] : null
const leaf = levels.pop()
const parent = levels.join('/')

const node = {
path,
leaf,
module,
size: +size || 20,
level
};

nodes.push(node)

if (parent) {
links.push({source: parent, target: path,
//targetNode: node
})
}
})

this.setState({parsedResponse: {nodes: nodes, links: links}})

})

}

}
Loading