
com.tinkerpop.gremlin.console.plugin.GephiRemoteAcceptor.groovy Maven / Gradle / Ivy
The newest version!
package com.tinkerpop.gremlin.console.plugin
import com.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor
import com.tinkerpop.gremlin.groovy.plugin.RemoteException
import com.tinkerpop.gremlin.process.Traversal
import com.tinkerpop.gremlin.structure.Edge
import com.tinkerpop.gremlin.structure.Graph
import com.tinkerpop.gremlin.structure.Vertex
import groovy.json.JsonSlurper
import groovyx.net.http.HTTPBuilder
import org.codehaus.groovy.tools.shell.Groovysh
import org.codehaus.groovy.tools.shell.IO
import static groovyx.net.http.ContentType.JSON
/**
* @author Stephen Mallette (http://stephen.genoprime.com)
* @author Randall Barnhart ([email protected])
*/
class GephiRemoteAcceptor implements RemoteAcceptor {
private String host = "localhost"
private int port = 8080
private String workspace = "workspace0"
private final Groovysh shell
private final IO io
private long vizStepDelay
private float[] vizStartRGBColor
private char vizColorToFade
private float vizColorFadeRate
private Map fadingVertexColors;
public GephiRemoteAcceptor(final Groovysh shell, final IO io) {
this.shell = shell
this.io = io
// traversal visualization defaults
vizStepDelay = 1000; // 1 second pause between viz of steps
vizStartRGBColor = [0.0f, 1.0f, 0.5f] // light aqua green
vizColorToFade = 'g' // will fade so blue is strongest
vizColorFadeRate = 0.7 // the multiplicative rate to fade visited vertices
}
@Override
connect(final List args) throws RemoteException {
if (args.size() >= 1)
workspace = args[0]
if (args.size() >= 2)
host = args[1]
if (args.size() >= 3) {
try {
port = Integer.parseInt(args[2])
} catch (Exception ex) {
throw new RemoteException("Port must be an integer value")
}
}
String vizConfig = " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " +
"colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate"
if (args.size() >= 4) {
if (args.size() > 7) {
vizConfig = configVizOptions(args.subList(3, 6))
} else {
vizConfig = configVizOptions(args.subList(3, args.size()))
}
}
return "Connection to Gephi - http://$host:$port/$workspace" + vizConfig
}
@Override
Object configure(final List args) throws RemoteException {
if (args.size() != 2)
throw new RemoteException("Expects [host |port |workspace |" +
"stepDelay |startRGBColor |" +
"colorToFade: ]|colorFadeRate: ")
if (args[0] == "host")
host = args[1]
else if (args[0] == "port") {
try {
port = Integer.parseInt(args[1])
} catch (Exception ignored) {
throw new RemoteException("Port must be an integer value")
}
} else if (args[0] == "workspace")
workspace = args[1]
else if (args[0] == "stepDelay")
parseVizStepDelay(args[1])
else if (args[0] == "startRGBColor")
parseVizStartRGBColor(args[1])
else if (args[0] == "colorToFade")
parseVizColorToFade(args[1])
else if (args[0] == "colorFadeRate")
parseVizColorFadeRate(args[1])
else
throw new RemoteException("Expects [host |port |workspace |" +
"stepDelay |startRGBColor |" +
"colorToFade: ]|colorFadeRate: ")
return "Connection to Gephi - http://$host:$port/$workspace" +
" with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " +
"colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate"
}
private Object configVizOptions(final List vizConfigArgs) {
if (vizConfigArgs.size() >= 1)
parseVizStepDelay(vizConfigArgs[0])
if (vizConfigArgs.size() >= 2)
parseVizStartRGBColor(vizConfigArgs[1])
if (vizConfigArgs.size() >= 3)
parseVizColorToFade(vizConfigArgs[2])
if (vizConfigArgs.size() >= 4)
parseVizColorFadeRate(vizConfigArgs[3])
return " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " +
"colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate"
}
private void parseVizStepDelay(String arg) {
try {
vizStepDelay = Long.parseLong(arg)
} catch (Exception ignored) {
throw new RemoteException("The stepDelay must be a long value")
}
}
private void parseVizStartRGBColor(String arg) {
try {
vizStartRGBColor = arg[1..-2].tokenize(',')*.toFloat()
assert (vizStartRGBColor.length == 3)
} catch (Exception ignored) {
throw new RemoteException("The vizStartRGBColor must be an array of 3 float values, e.g. [0.0,1.0,0.5]")
}
}
private void parseVizColorToFade(String arg) {
try {
vizColorToFade = arg.charAt(0).toLowerCase();
assert (vizColorToFade == 'r' || vizColorToFade == 'g' || vizColorToFade == 'b')
} catch (Exception ignored) {
throw new RemoteException("The vizColorToFade must be one character value among: r, g, b, R, G, B")
}
}
private void parseVizColorFadeRate(String arg) {
try {
vizColorFadeRate = Float.parseFloat(arg)
} catch (Exception ignored) {
throw new RemoteException("The colorFadeRate must be a float value")
}
}
@Override
Object submit(final List args) throws RemoteException {
final String line = String.join(" ", args)
final Object o = shell.execute(line)
if (o instanceof Graph) {
clearGraph()
def g = (Graph) o
g.V().sideEffect { addVertexToGephi(it.get()) }.iterate()
} else if (o instanceof Traversal) {
fadingVertexColors = [:]
def traversal = (Traversal) o
def memKeys = traversal.asAdmin().getSideEffects().keys()
def memSize = memKeys.size()
// assumes user called store("1")...store("n") in ascension
for (int i = 1; i <= memSize; i++) {
def stepKey = Integer.toString(i)
if (memKeys.contains(stepKey)) {
io.out.print("Visualizing vertices at step: $stepKey... ")
updateVisitedVertices()
int visitedCount = 0
if (traversal.asAdmin().getSideEffects().exists(stepKey)) {
traversal.asAdmin().getSideEffects().get(stepKey).each { element ->
visitVertexToGephi((Vertex) element)
visitedCount++
}
}
io.out.println("Visited: $visitedCount")
}
sleep(vizStepDelay)
}
}
}
@Override
void close() throws IOException {
}
def updateVisitedVertices() {
fadingVertexColors.keySet().each { vertex ->
def currentColor = fadingVertexColors.get(vertex)
currentColor *= vizColorFadeRate
fadingVertexColors.put(vertex, currentColor)
def props = [:]
props.put(vizColorToFade.toString(), currentColor)
updateGephiGraph([cn: [(vertex): props]])
}
}
def visitVertexToGephi(def Vertex v) {
def props = [:]
props.put('r', vizStartRGBColor[0])
props.put('g', vizStartRGBColor[1])
props.put('b', vizStartRGBColor[2])
props.put('x', 1)
updateGephiGraph([cn: [(v.id().toString()): props]])
fadingVertexColors.put(v.id().toString(), vizStartRGBColor[fadeColorIndex()])
}
def fadeColorIndex() {
if (vizColorToFade == 'r')
return 0
else if (vizColorToFade == 'g')
return 1
else if (vizColorToFade == 'b')
return 2
}
def addVertexToGephi(def Vertex v, def boolean ignoreEdges = false) {
// grab the first property value from the strategies of values
def props = v.valueMap().next().collectEntries { kv -> [(kv.key): kv.value[0]] }
props << [label: v.label()]
// only add if it does not exist in graph already
if (!getFromGephiGraph([operation: "getNode", id: v.id().toString()]).isPresent())
updateGephiGraph([an: [(v.id().toString()): props]])
if (!ignoreEdges) {
v.outE().sideEffect {
addEdgeToGephi(it.get())
}.iterate()
}
}
def addEdgeToGephi(def Edge e) {
def props = e.valueMap().next()
props.put('label', e.label())
props.put('source', e.outV().id().next().toString())
props.put('target', e.inV().id().next().toString())
props.put('directed', true)
// make sure the in vertex is there but don't add its edges - that will happen later as we are looping
// all vertices in the graph
addVertexToGephi(e.inV().next(), true)
// both vertices are definitely there now, so add the edge
updateGephiGraph([ae: [(e.id().toString()): props]])
}
def clearGraph() {
updateGephiGraph([dn: [filter: "ALL"]])
}
def getFromGephiGraph(def Map queryArgs) {
def http = new HTTPBuilder("http://$host:$port/")
def resp = http.get(path: "/$workspace", query: queryArgs).getText()
// gephi streaming plugin does not set the content type or respect the Accept header - treat as text
if (resp.isEmpty())
return Optional.empty()
else
return Optional.of(new JsonSlurper().parseText(resp))
}
def updateGephiGraph(def Map postBody) {
def http = new HTTPBuilder("http://$host:$port/")
http.post(path: "/$workspace", requestContentType: JSON, body: postBody, query: [format: "JSON", operation: "updateGraph"])
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy