├── .gitignore ├── MANIFEST.in ├── README.md ├── example_rest.py ├── example_ws.py ├── gephistreamer ├── __init__.py ├── graph.py └── streamer.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | requests* 37 | 38 | # Virtual env 39 | env/ 40 | 41 | .cache -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GephiStreamer 2 | ============= 3 | 4 | Python classes for streaming graph to gephi 5 | ![Demo](http://matthieu-totet.fr/Koumin/wp-content/uploads/2015/12/GephiStreamDemo.gif) 6 | 7 | Install 8 | ====== 9 | 10 | `pip install gephistreamer` 11 | 12 | Quick use 13 | ====== 14 | 15 | ```python 16 | # Basic import 17 | from gephistreamer import graph 18 | from gephistreamer import streamer 19 | 20 | # Create a Streamer 21 | # adapt if needed : streamer.GephiWS(hostname="localhost", port=8080, workspace="workspace0") 22 | # You can also use REST call with GephiREST (a little bit slower than Websocket) 23 | stream = streamer.Streamer(streamer.GephiWS()) 24 | 25 | # Create a node with a custom_property 26 | node_a = graph.Node("A",custom_property=1) 27 | 28 | # Create a node and then add the custom_property 29 | node_b = graph.Node("B") 30 | node_b.property['custom_property']=2 31 | 32 | # Add the node to the stream 33 | # you can also do it one by one or via a list 34 | # l = [node_a,node_b] 35 | # stream.add_node(*l) 36 | stream.add_node(node_a,node_b) 37 | 38 | # Create edge 39 | # You can also use the id of the node : graph.Edge("A","B",custom_property="hello") 40 | edge_ab = graph.Edge(node_a,node_b,custom_property="hello") 41 | stream.add_edge(edge_ab) 42 | 43 | ``` 44 | How to 45 | ===== 46 | 47 | Use the `Streamer` class to describe the action to perform: 48 | * add_node 49 | * change_node 50 | * delete_node 51 | * add_edge 52 | * change_edge 53 | * delete_edge 54 | 55 | Don't forget to have Gephi running with the plugin [Graph Streaming](https://marketplace.gephi.org/plugin/graph-streaming/) installed and active in "Master mode". 56 | ![Master mode](http://matthieu-totet.fr/Koumin/wp-content/uploads/2013/07/ScreenHunter_01-Jul.-30-08.39.jpg) 57 | 58 | GephiWS 59 | ===== 60 | 61 | The GephiWS class communicates with Gephi as Websocket call. 62 | 63 | GephiREST 64 | ===== 65 | 66 | The GephiREST class communicates with Gephi as REST call. 67 | 68 | 69 | Auto commit 70 | ===== 71 | By default, all action will trigger a "commit" and send information to Gephi. You still 72 | can use the old way by requiering a 73 | ```python 74 | stream = streamer.Streamer(streamer.GephiREST(),auto_commit=False) 75 | [.. actions ..] 76 | stream.commit() # Will send all actions buffered to Gephi 77 | ``` -------------------------------------------------------------------------------- /example_rest.py: -------------------------------------------------------------------------------- 1 | # Basic import 2 | from gephistreamer import graph 3 | from gephistreamer import streamer 4 | 5 | # Create a Streamer 6 | # adapt if needed : streamer.GephiREST(hostname="localhost", port=8080, workspace="workspace0") 7 | stream = streamer.Streamer(streamer.GephiREST()) 8 | 9 | # Create a node with a custom_property 10 | node_a = graph.Node("A",custom_property=1) 11 | 12 | # Create a node and then add the custom_property 13 | node_b = graph.Node("B") 14 | node_b.property['custom_property']=2 15 | 16 | # Add the node to the stream 17 | # you can also do it one by one or via a list 18 | # l = [node_a,node_b] 19 | # stream.add_node(*l) 20 | stream.add_node(node_a,node_b) 21 | 22 | # Create edge 23 | # You can also use the id of the node : graph.Edge("A","B",custom_property="hello") 24 | edge_ab = graph.Edge(node_a,node_b,custom_property="hello") 25 | stream.add_edge(edge_ab) -------------------------------------------------------------------------------- /example_ws.py: -------------------------------------------------------------------------------- 1 | # Basic import 2 | from gephistreamer import graph 3 | from gephistreamer import streamer 4 | 5 | import itertools 6 | import random 7 | import time 8 | 9 | # Same code as GephiREST, but it creates a Websocket client that keep connectivity until the script exits 10 | # Much faster than REST method 11 | stream = streamer.Streamer(streamer.GephiWS()) 12 | test = [x for x in itertools.permutations('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', 2) ] 13 | random.shuffle(test) 14 | for source, target in test: 15 | 16 | node_source = graph.Node(source) 17 | node_target = graph.Node(target) 18 | 19 | stream.add_node(node_source,node_target) 20 | # time.sleep(0.5) # Make it slower :D 21 | stream.add_edge(graph.Edge(node_source,node_target)) 22 | 23 | time.sleep(1) #It might be possible the script runs too fast and last action anr't sent properly 24 | 25 | 26 | -------------------------------------------------------------------------------- /gephistreamer/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Send graph data on Gephi with the Graph Stream Public 5 | """ 6 | 7 | from .graph import Node, Edge 8 | from .streamer import Streamer, GephiREST -------------------------------------------------------------------------------- /gephistreamer/graph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | __all__ = ['Node','Edge'] 5 | 6 | class Entity(object): 7 | ''' 8 | Abstract Class for Nodes and Edges 9 | ''' 10 | def __init__(self,eid, property): 11 | ''' 12 | Constructor 13 | ''' 14 | self.property = property 15 | self.object ={eid:self.property} 16 | 17 | def __str__(self): 18 | return "%s"%self.object 19 | 20 | def color_hex(self,r=None,g=None,b=None): 21 | ''' 22 | Change Color : Ask Hexa (more 'natural') 23 | ''' 24 | if r : self.property['r'] = float(r)/255 25 | if g : self.property['g'] = float(g)/255 26 | if b : self.property['b'] = float(b)/255 27 | 28 | def json(self): 29 | return self.object 30 | 31 | class Node(Entity): 32 | ''' 33 | Node object 34 | ''' 35 | def __init__(self,eid,label=None, size=1, x=0, y=0, z=0, red=0.5, green=0.5, blue=0.5, **kwargs): 36 | ''' 37 | Constructor 38 | ''' 39 | if not label: 40 | label = eid 41 | Entity.__init__(self, eid, dict({"label":label, 42 | "size":size, 43 | "x":x, 44 | "y":y, 45 | "z":z, 46 | "r":red, 47 | "g":green, 48 | "b":blue}, **kwargs) ) 49 | 50 | class Edge(Entity): 51 | ''' 52 | Edge object 53 | kwargs [label, size, x, y, z, red, green, blue] 54 | ''' 55 | def __init__(self, source, target, directed=True, kind="", eid=None, weight=1, label="", red=0.5, green=0.5, blue=0.5, **kwargs): 56 | ''' 57 | Constructor 58 | ''' 59 | if type(source)==Node: 60 | source=next(iter(source.object.keys())) 61 | if type(target)==Node: 62 | target=next(iter(target.object.keys())) 63 | 64 | if not eid: 65 | eid = self._generate_id(source,target,directed) 66 | 67 | Entity.__init__(self, eid, dict({"source":source, 68 | "target":target, 69 | "weight":weight, 70 | "kind":kind, 71 | "directed":directed, 72 | "label":label, 73 | "r":red, 74 | "g":green, 75 | "b":blue},**kwargs)) 76 | 77 | def _generate_id(self,source,target,directed): 78 | return "{source}--{direction}{target}".format(source=source, 79 | target=target, 80 | direction=directed and ">" or "-") 81 | -------------------------------------------------------------------------------- /gephistreamer/streamer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | __all__ = ['GephiREST','GephiWS','Streamer'] 5 | 6 | import json 7 | from enum import Enum 8 | 9 | import requests 10 | 11 | from .graph import Node, Edge 12 | 13 | # Actions used by Gephi Streaming 14 | 15 | class Action(Enum): 16 | ADD_NODE = "an" 17 | CHANGE_NODE = "cn" 18 | DELETE_NODE = "dn" 19 | 20 | ADD_EDGE = "ae" 21 | CHANGE_EDGE = "ce" 22 | DELETE_EDGE = "de" 23 | 24 | # Main class that handle all the action stack manager 25 | class Streamer: 26 | 27 | def __init__(self,streamer,auto_commit=True): 28 | self.stream_method = streamer 29 | # Nodes related 30 | self.add_node = StackManager(Node,Action.ADD_NODE,streamer,auto_commit) 31 | self.change_node = StackManager(Node,Action.CHANGE_NODE,streamer,auto_commit) 32 | self.delete_node = StackManager(Node,Action.DELETE_NODE,streamer,auto_commit) 33 | 34 | # Edges related 35 | self.add_edge = StackManager(Edge,Action.ADD_EDGE,streamer,auto_commit) 36 | self.change_edge = StackManager(Edge,Action.CHANGE_EDGE,streamer,auto_commit) 37 | self.delete_edge = StackManager(Edge,Action.DELETE_EDGE,streamer,auto_commit) 38 | 39 | # Flow of update all actions on commit. 40 | self.COMMIT_FLOW = [ self.add_node, 41 | self.add_edge, 42 | self.change_edge, 43 | self.change_node, 44 | self.delete_edge, 45 | self.delete_node 46 | ] 47 | 48 | # To use if auto_commit = False, to send all actions 49 | def commit(self): 50 | for action_manager in self.COMMIT_FLOW: 51 | action_manager.commit(self.stream_method.send) 52 | 53 | class StreamError(Exception): 54 | pass 55 | # Manage a list of action, apply it with the stream_method with commit 56 | class StackManager: 57 | def __init__(self,entity_type,action,stream_method,auto_commit=False): 58 | self.type = entity_type 59 | self.stack = list() 60 | self.header = action 61 | self.stream_method = stream_method 62 | self.auto_commit = auto_commit 63 | 64 | def __call__(self, *args): 65 | for entity in args: 66 | if type(entity) == self.type: 67 | self.stack.append(entity) 68 | else: 69 | raise StreamError("Should pass a {type}".format(type=self.type)) 70 | if self.auto_commit: 71 | self.commit() 72 | 73 | def reset(self): 74 | del self.stack[:] 75 | 76 | def action(self): 77 | action_json = {} 78 | for action in self.stack: 79 | action_json.update(action.json()) 80 | return {self.header.value:action_json} 81 | 82 | def commit(self,auto_reset=True): 83 | self.stream_method.send(self.action()) 84 | if auto_reset: 85 | self.reset() 86 | 87 | def json(self): 88 | return json.dumps({self.header:dict(self.stack)}) 89 | # Gephi Streaming via REST calls 90 | class GephiREST: 91 | def __init__(self, hostname="localhost", port=8080, workspace="workspace1"): 92 | self.hostname = hostname 93 | self.port = port 94 | self.workspace = workspace 95 | 96 | def _generate_url(self): 97 | return "http://{hostname}:{port}/{workspace}?operation=updateGraph".format(hostname=self.hostname, 98 | port=self.port, 99 | workspace=self.workspace) 100 | def send(self,action): 101 | url = self._generate_url() 102 | requests.post(url, data=json.dumps(action)) 103 | 104 | # Gephi Streaming via Websocket 105 | class GephiWS: 106 | from ws4py.client.threadedclient import WebSocketClient 107 | class Client(WebSocketClient): 108 | def send_data(self,action): 109 | self.send(json.dumps(action)) 110 | def __init__(self, hostname="localhost", port=8080, workspace="workspace0"): 111 | self.hostname = hostname 112 | self.port = port 113 | self.workspace = workspace 114 | self.websocket = self.Client(self._generate_url()) 115 | self.websocket.connect() 116 | def _generate_url(self): 117 | return "ws://{hostname}:{port}/{workspace}?operation=updateGraph".format(hostname=self.hostname, 118 | port=self.port, 119 | workspace=self.workspace) 120 | def send(self,action): 121 | self.websocket.send_data(action) 122 | """ 123 | # This method is blocking sometime and I don't know why. 124 | class GephiWS2: 125 | def __init__(self, hostname="localhost", port=8080, workspace="workspace0"): 126 | from websocket import create_connection,socket 127 | self.hostname = hostname 128 | self.port = port 129 | self.workspace = workspace 130 | self.websocket = create_connection(self._generate_url()) 131 | 132 | def _generate_url(self): 133 | return "ws://{hostname}:{port}/{workspace}?operation=updateGraph".format(hostname=self.hostname, 134 | port=self.port, 135 | workspace=self.workspace) 136 | def send(self,action): 137 | self.websocket.send(json.dumps(action)) 138 | self.websocket.recv() 139 | """ -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='GephiStreamer', 5 | 6 | version="2.0.3", 7 | 8 | packages=['gephistreamer'], 9 | 10 | author="Matthieu Totet (@totetmatt)", 11 | 12 | author_email="matthieu.totet@gmail.com", 13 | 14 | description="Tools to stream data to gephi", 15 | 16 | long_description=open('README.md').read(), 17 | 18 | include_package_data=True, 19 | 20 | url='https://github.com/totetmatt/GephiStreamer', 21 | 22 | install_requires=[ 23 | 'requests', 24 | 'ws4py', 25 | 'enum34' 26 | ], 27 | 28 | classifiers=[ 29 | "Programming Language :: Python", 30 | "Operating System :: OS Independent", 31 | "Programming Language :: Python :: 3.4", 32 | "Topic :: Communications", 33 | ], 34 | ) --------------------------------------------------------------------------------