text: """
  This module provides methods to export data from NetworKit directly to Gephi using the
  Gephi Streaming Plugin.
  """
  
  __author__ = "Gerd Lindner, Moritz v.Looz"
  
  
  import urllib as _urllib
  import time
  import math
  
  from . import pyclient as _gephipyclient   # we want to hide these from the user
  
  class GephiStreamingClient:
  
      """
       A python singleton providing access to a running Master instance of the Gephi
       Streaming plugin
      """
  
      def __init__(self, url='http://localhost:8080/workspace0'):
          #Disabling Flushing means quite a good performance boost.
          self._pygephi = _gephipyclient.GephiClient(url, autoflush=10000)
          self.graphExported = False
  
      def _urlError(self, e):
          print("Could not connect to the gephi streaming plugin.")
          print("Did you start the streaming master server in gephi and provide the name of your workspace?")
          print("If the workspace is named 'Workspace 0', the corresponding url is http://localhost:8080/workspace0 (adapt port)")
  
      def exportGraph(self, graph):
          """ Exports the given graph to gephi. No attributes or weights are exported.
          Please note that only graphs with indexed edges can be exported, so
          edges will be indexed if neccessary. """
  
          try:
              self._pygephi.clean()
  
              #Index edges if neccessary
              graph.indexEdges()
              self.directed = graph.isDirected()
  
              self._exportNodes(graph.iterNodes())
  
              for u, v in graph.iterEdges():
                  if self.directed:
                      edgeId = str(u) + '->' + str(v)
                  else:
                      edgeId = str(min(edge[0],edge[1])) + '-' + str(max(edge[0],edge[1]))
                  self._pygephi.add_edge(edgeId, edge[0], edge[1], self.directed)
  
              self._pygephi.flush()
              self.graphExported = True
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def _exportNodes(self, nodes):
          nAttrs = {'size': 2.0, 'r': 0.6, 'g': 0.6, 'b': 0.6, 'y':1.0}
  
          # the default approximately shows -2000 to 2000, so we want to
          # distribute the nodes in that area. Since Gephi 0.9, no nodes
          # may have exactly the same coordinates, thus a deterministic
          # distribution scheme is used.
          NODE_AREA_SIZE = 2000
          nodesPerSquareSide = 0 if len(nodes) == 0 else math.ceil(math.sqrt(len(nodes)))
          stepSize = NODE_AREA_SIZE / nodesPerSquareSide
          offset = NODE_AREA_SIZE / 2
  
          nodeNumber = 0
          for node in nodes:
              nAttrs['x'] = (nodeNumber % nodesPerSquareSide) * stepSize - offset
              nAttrs['y'] = (nodeNumber // nodesPerSquareSide) * stepSize - offset
              nodeNumber += 1
              self._pygephi.add_node(str(node), **nAttrs)
  
      def exportAdditionalEdge(self, u, v):
          """ Adds an edge (u,v) in an already exported graph. If the edge is already present, nothing happens.
              If the graph is directed, the edge goes from u to v
  
              Parameters:
              - u: first node
              - v: second node
          """
          if self.graphExported != True:
              print("Error: Cannot add edges. Export Graph first!")
              return
          try:
              if self.directed:
                  edgeId = str(u) + '->' + str(v)
              else:
                  edgeId = str(min(u,v)) + '-' + str(max(u,v))
              self._pygephi.add_edge(edgeId, u, v, self.directed)
              self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def removeExportedEdge(self, u, v):
          """ Removes an edge from an already exported graph."""
          if self.graphExported != True:
              print("Error: Cannot remove edges. Export Graph first!")
              return
          try:
              if self.directed:
                  edgeId = str(u) + '->' + str(v)
              else:
                  edgeId = str(min(u,v)) + '-' + str(max(u,v))
              self._pygephi.delete_edge(edgeId)
              self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def exportEventStream(self, stream, timeStepDelay = 0):
          if self.graphExported != True:
              print("Error: Cannot export event stream. Export Graph first!")
              return
  
          nAttrs = {}
          try:
              for ev in stream:
                  if ev.type == ev.NODE_ADDITION:
                      self._pygephi.add_node(str(ev.u), **nAttrs)
                  elif ev.type == ev.NODE_REMOVAL:
                      self._pygephi.delete_node(str(ev.u))
                  elif ev.type == ev.EDGE_ADDITION:
                      if self.directed:
                          edgeId = str(ev.u) + '->' + str(ev.v)
                      else:
                          edgeId = str(min(ev.u,ev.v)) + '-' + str(max(ev.u,ev.v))
                      self._pygephi.add_edge(edgeId, ev.u, ev.v, self.directed)
                  elif ev.type == ev.EDGE_REMOVAL:
                      if self.directed:
                          edgeId = str(ev.u) + '->' + str(ev.v)
                      else:
                          edgeId = str(min(ev.u,ev.v)) + '-' + str(max(ev.u,ev.v))
                      self._pygephi.delete_edge(edgeId)
                  elif ev.type == ev.EDGE_WEIGHT_UPDATE:
                      print("Edge weights not yet supported in gephi streaming!")
                  elif ev.type == ev.EDGE_WEIGHT_INCREMENT:
                      print("Edge weights not yet supported in gephi streaming!")
                  elif ev.type == ev.TIME_STEP:
                      self._pygephi.flush()
                      if timeStepDelay > 0:
                          time.sleep(timeStepDelay)
                  self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def exportNodeValues(self, graph, values, attribute_name):
          """
          This method exports node values (e.g. community information, betwenness centrality values)
          to gephi using the Gephi Streaming Plugin. Use exportGraph(Graph) first to export
          the graph itself.
  
          Parameters:
          - values: python list or Partition object that contains the values to be exported.
          - attribute_name: name of the node attribute in gephi
          """
  
          try:
              if len(values) != graph.numberOfNodes():
                  print("Warning: #Nodes (", graph.numberOfNodes(), ") does not match #Values (", len(values), ").")
  
              for i in graph.iterNodes():
                  nAttrs = {attribute_name:values[i]}
                  self._pygephi.change_node(str(i), **nAttrs)
  
              self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def exportCoordinates(self, graph, scale=1):
          try:
              xcoords = [scale*graph.getCoordinate(v)[0] for v in graph.iterNodes()]
              ycoords = [scale*graph.getCoordinate(v)[1] for v in graph.iterNodes()]
              self.exportNodeValues(graph, xcoords, 'x')
              self.exportNodeValues(graph, ycoords, 'y')
              self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def exportEdgeValues(self, graph, values, attribute_name):
          """
          This method exports an edge attribute to gephi using the Gephi Streaming Plugin.
          Use exportGraph(Graph) first to export graph itself.
  
          Parameters:
          - values: python list contains the values to be exported.
          - attribute_name: name of the edge attribute in gephi
          """
          try:
              if len(values) != graph.upperEdgeIdBound():
                  print("Warning: Upper edge id bound (", graph.upperEdgeIdBound(), ") does not match #Values (", len(values), ").")
  
              idx = 0
              for edge in graph.iterEdges():
                  if self.directed:
                      edgeId = str(u) + '->' + str(v)
                      edgetype = "Directed"
                  else:
                      edgeId = str(min(edge[0],edge[1])) + '-' + str(max(edge[0],edge[1]))
                      edgetype = "Undirected"
                  eAttrs = {attribute_name:values[graph.edgeId(edge[0], edge[1])], "Type":edgetype}#still need to use the old edge to access the graph array
                  self._pygephi.change_edge(edgeId, edge[0], edge[1], self.directed, **eAttrs)
                  idx += 1
  
              self._pygephi.flush()
          except _urllib.error.URLError as e:
              self._urlError(e)
  
      def clearGraph(self):
          """ Cleans the first workspace in Gephi. """
  
          try:
              self._pygephi.clean()
              self._pygephi.flush()
              self.graphExported = False
  
          except _urllib.error.URLError as e:
              self._urlError(e)
  