Esempio n. 1
0
def write_adl(graph):
    """
    Export graph as adjacency list (ADL)

    .. note:: This format does not store graph, node, or edge data.

    :param graph:   Graph object to export
    :type graph:    :graphit:Graph

    :return:        Graph object
    :rtype:         :py:str
    """

    # Create empty file buffer
    string_buffer = StringIO()

    # Process nodes with adjacency
    adjacency = graph.adjacency()
    adj_done = []
    for node, adj in adjacency.items():
        if len(adj):
            string_buffer.write('{0} {1}\n'.format(node, ' '.join([str(n) for n in adj])))
            adj_done.extend(adj)

    # Process isolated nodes
    for node, adj in adjacency.items():
        if not len(adj) and node not in adj_done:
            string_buffer.write('{0}\n'.format(node))

    logger.info('Graph {0} exported in Adjacency list format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 2
0
    def __init__(self, stream):

        self.stream = stream
        if isinstance(stream, str):
            self.stream = StringIO(stream)

        self.has_more = True
        self.block_pos = None

        self._line = None
        self._pos = 0
        self._cursor = self.stream.tell()
Esempio n. 3
0
def open_anything(source, mode='r'):
    """
    Open input available from a file, a Python file like object, standard
    input, a URL or a string and return a uniform Python file like object
    with standard methods.
    
    :param source: Input as file, Python file like object, standard
                   input, URL or a string
    :type source:  mixed
    :param mode:   file access mode, defaults to 'r'
    :type mode:    string
    :return:       Python file like object
    """

    # Check if the source is a file and open
    if os.path.isfile(source):
        logger.debug('Reading file from disk {0}'.format(source))
        return open(source, mode)

    # Check if source is file already openend using 'open' or 'file' return
    if hasattr(source, 'read'):
        logger.debug('Reading file {0} from file object'.format(source.name))
        return source

    # Check if source is standard input
    if source == '-':
        logger.debug('Reading file from standard input')
        return sys.stdin

    else:
        # Check if source is a URL and try to open
        try:
            if urlparse.urlparse(source)[0] == 'http':
                result = urllib.urlopen(source)
                logger.debug(
                    "Reading file from URL with access info:\n {0}".format(
                        result.info()))
                return result
        except IOError:
            logger.info("Unable to access URL")

        # Check if source is file and try to open else regard as string
        try:
            return open(source)
        except IOError:
            logger.debug("Unable to access as file, try to parse as string")
            return StringIO(str(source))
def write_flattened(graph, sep='.', default=None, allow_none=False, **kwargs):

    # No nodes, return empty dict
    if graph.empty():
        logging.info('Graph is empty: {0}'.format(repr(graph)))
        return {}

    # Graph should be of type GraphAxis with a root node nid defined
    if not isinstance(graph, GraphAxis):
        raise TypeError('Unsupported graph type {0}'.format(type(graph)))
    if graph.root is None:
        raise GraphitException('No graph root node defines')

    # Set current NodeTools aside and register new one
    curr_nt = graph.node_tools
    graph.origin.node_tools = NodeAxisTools

    # Create empty file buffer
    string_buffer = StringIO()

    value_tag = graph.data.value_tag
    for leaf in node_leaves(graph):
        node = graph.getnodes(leaf)
        value = node.get(value_tag, default=default)

        if value is None and not allow_none:
            continue

        path = '{0}{1}{2}\n'.format(node.path(sep=sep, **kwargs), sep, value)
        string_buffer.write(path)

    # Restore original NodeTools
    graph.origin.node_tools = curr_nt

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 5
0
def write_dot(graph, graph_name='graph', dot_directives=None):
    """
    DOT graphs are either directional (digraph) or undirectional, mixed mode
    is not supported.
    
    Basic types for node and edge attributes are supported.
    
    :param graph:          Graph object to export
    :type graph:           :graphit:Graph
    :param graph_name:     graph name to include
    :type graph_name:      :py:str
    :param dot_directives: special DOT format rendering directives
    :type dot_directives:  :py:dict
    
    :return:               DOT graph representation
    :rtype:                :py:str
    """
    
    indent = ' ' * 4
    link = '->' if graph.directed else '--'
    
    # Create empty file buffer
    string_buffer = StringIO()
    
    # Write header comment and graph container
    string_buffer.write('//Created by {0} version {1}\n'.format(__module__, __version__))
    string_buffer.write('{0} "{1}" {2}\n'.format('digraph' if graph.directed else 'graph', graph_name, '{'))
    
    # Write special DOT directives
    if isinstance(dot_directives, dict):
        for directive, value in dot_directives.items():
            string_buffer.write('{0}{1}={2}\n'.format(indent, directive, value))
    
    # Export nodes
    string_buffer.write('{0}//nodes\n'.format(indent))
    for node in graph.iternodes():
        attr = ['{0}={1}'.format(k, json.dumps(v)) for k,v in node.nodes[node.nid].items() if
                isinstance(v, PY_PRIMITIVES) and not k.startswith('$')]
        if attr:
            string_buffer.write('{0}{1} [{2}];\n'.format(indent, node.nid, ','.join(attr)))
    
    # Export adjacency
    string_buffer.write('{0}//edges\n'.format(indent))
    done = []
    for node, adj in graph.adjacency.items():
        for a in adj:
            edges = [(node, a), (a, node)]
            
            if all([e not in done for e in edges]):
                attr = {}
                for edge in edges:
                   attr.update(graph.edges.get(edge, {}))
                attr = ['{0}={1}'.format(k, json.dumps(v)) for k, v in attr.items() if
                        isinstance(v, PY_PRIMITIVES) and not k.startswith('$')]
                
                if attr:
                    string_buffer.write('{0}{1} {2} {3} [{4}];\n'.format(indent, node, link, a, ','.join(attr)))
                else:
                    string_buffer.write('{0}{1} {2} {3};\n'.format(indent, node, link, a))
            
            done.extend(edges)
    
    # Closing curly brace
    string_buffer.write('}\n')
    
    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 6
0
def write_tgf(graph, key_tag=None):
    """
    Export a graph in Trivial Graph Format
    
    .. note::
    TGF graph export uses the Graph iternodes and iteredges methods to retrieve
    nodes and edges and 'get' the data labels. The behaviour of this process is
    determined by the single node/edge mixin classes and the ORM mapper.
    
    :param graph:         Graph object to export
    :type graph:          :graphit:Graph
    :param key_tag:       node/edge data key
    :type key_tag:        :py:str
    
    :return:              TGF graph representation
    :rtype:               :py:str
    """

    # Define node and edge data tags to export
    key_tag = key_tag or graph.key_tag

    # Create empty file buffer
    string_buffer = StringIO()

    # Export nodes
    for node in graph.iternodes():
        string_buffer.write('{0} {1}\n'.format(node.nid,
                                               node.get(key_tag, default='')))

    # Export edges
    string_buffer.write('#\n')
    for edge in graph.iteredges():
        e1, e2 = edge.nid
        string_buffer.write('{0} {1} {2}\n'.format(
            e1, e2, edge.get(key_tag, default='')))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 7
0
def write_gml(graph, node_tools=None, edge_tools=None):
    """
    Export a graphit graph to GML format

    Export graphit Graph data, nodes and edges in Graph Modelling Language
    (GML) format. The function replaces the graph NodeTools and EdgeTools
    with a custom version exposing a `serialize` method responsible for
    serializing the node/edge attributes in a GML format. The NodeTools
    class is also used to export Graph.data attributes.

    Custom serializers may be introduced as custom NodeTools or EdgeTools
    classes using the `node_tools` and/or `edge_tools` attributes.
    In addition, the graph ORM may be used to inject tailored `serialize`
    methods in specific nodes or edges.

    :param graph:       Graph object to export
    :type graph:        :graphit:Graph
    :param node_tools:  NodeTools class with node serialize method
    :type node_tools:   :graphit:NodeTools
    :param edge_tools:  EdgeTools class with edge serialize method
    :type edge_tools:   :graphit:EdgeTools

    :return:            GML graph representation
    :rtype:             :py:str
    """

    # Set current node and edge tools aside and register GML ones for export
    curr_nt = graph.node_tools
    curr_et = graph.edge_tools

    if node_tools and not issubclass(node_tools, NodeTools):
        raise GraphitException('Node_tools ({0}) needs to inherit from the NodeTools class'.format(type(node_tools)))
    graph.node_tools = node_tools or type('GMLNodeTools', (GMLTools, NodeTools), {})

    if edge_tools and not issubclass(edge_tools, EdgeTools):
        raise GraphitException('Edge_tools ({0}) needs to inherit from the EdgeTools class'.format(type(edge_tools)))
    graph.edge_tools = edge_tools or type('GMLEdgeTools', (GMLTools, EdgeTools), {})

    # Create empty file buffer
    string_buffer = StringIO()

    # Serialize main graph instance
    gs = graph.node_tools()
    string_buffer.write('graph [\n')
    gs.serialize(graph.data.to_dict(), string_buffer, indent=2)

    # Serialize nodes
    for node in graph.iternodes(sort_key=int):
        node.serialize(node.nodes[node.nid], string_buffer, indent=2, class_name='node')

    # Serialize edges
    for edge in graph.iteredges():
        edge.serialize(edge.edges[edge.nid], string_buffer, indent=2, class_name='edge')

    string_buffer.write(']\n')

    # Restore original node and edge tools
    graph.node_tools = curr_nt
    graph.edge_tools = curr_et

    logger.info('Graph {0} exported in GML format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 8
0
def write_pgf(graph, pickle_graph=False):
    """
    Export graph as Graph Python Format file

    PGF format is the modules own file format consisting of a serialized
    graph data, nodes and edges dictionaries. Exports either as plain text
    serialized dictionary or pickled graph object.
    The format is feature rich with good performance but is not portable.

    :param graph:        Graph object to export
    :type graph:         :graphit:Graph
    :param pickle_graph: serialize the Graph using Python pickle
    :type pickle_graph:  :py:bool

    :return:             Graph in GPF graph representation
    :rtype:              :py:str
    """

    # Pickle or not
    if pickle_graph:
        return pickle.dumps(graph)

    # Export graph as serialized Graph Python Format
    pp = pprint.PrettyPrinter(indent=2)
    string_buffer = StringIO()
    string_buffer.write('{')

    # Export graph data dictionary
    string_buffer.write('"data": {0},\n'.format(
        pp.pformat(graph.data.to_dict())))

    # Export nodes as dictionary
    string_buffer.write('"nodes": {0},\n'.format(
        pp.pformat(graph.nodes.to_dict())))

    # Export edges as dictionary
    string_buffer.write('"edges": {0},\n'.format(
        pp.pformat(graph.edges.to_dict())))
    string_buffer.write('}')

    logger.info('Graph {0} exported in PGF format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 9
0
def write_web(graph, orm_data_tag='haddock_type', indent=2, root_nid=None):
    """
    Export a graph in Spyder .web format

    Empty data blocks or Python None values are not exported.

    .. note::
    Web graph export uses the Graph iternodes and iteredges methods to retrieve
    nodes and edges and 'get' the data labels. The behaviour of this process is
    determined by the single node/edge mixin classes and the ORM mapper.

    :param graph:          Graph object to export
    :type graph:           :graphit:Graph
    :param orm_data_tag:   data key to use for .web data identifier
    :type orm_data_tag:    :py:str
    :param indent:         .web file white space indentation level
    :type indent:          :py:int
    :param root_nid:       Root node ID in graph hierarchy

    :return:               Spyder .web graph representation
    :rtype:                :py:str
    """

    # Build ORM with format specific conversion classes
    weborm = GraphORM()
    weborm.node_mapping.add(
        RestraintsInterface,
        lambda x: x.get(graph.data.key_tag) == 'activereslist')
    weborm.node_mapping.add(
        RestraintsInterface,
        lambda x: x.get(graph.data.key_tag) == 'passivereslist')

    # Resolve the root node (if any) for hierarchical data structures
    if root_nid and root_nid not in graph.nodes:
        raise GraphitException(
            'Root node ID {0} not in graph'.format(root_nid))
    else:
        root_nid = resolve_root_node(graph)
        if root_nid is None:
            raise GraphitException('Unable to resolve root node ID')

    # Set current NodeTools aside and register new one
    curr_nt = graph.node_tools
    graph.node_tools = WebNodeTools

    # Set current ORM aside and register new one.
    curr_orm = graph.orm
    graph.orm = weborm

    # Create empty file buffer
    string_buffer = StringIO()

    # Traverse node hierarchy
    def _walk_dict(node, indent_level):

        # First, collect all leaf nodes and write. Sort according to 'key'
        for leaf in sorted(
            [n for n in node.children(include_self=True) if n.isleaf],
                key=lambda obj: obj.key):

            # Do not export nodes that have no data or None but do export
            # empty data blocks (has orm_data_tag)
            if leaf.get(graph.data.value_tag, None) is None:
                if leaf.get(orm_data_tag):
                    string_buffer.write('{0}{1} = {2} (\n'.format(
                        ' ' * indent_level, leaf.get(graph.data.key_tag),
                        leaf.get(orm_data_tag)))
                    string_buffer.write('{0}),\n'.format(' ' * indent_level))
                continue

            # Format 'Array' types when they are list style leaf nodes
            if leaf.get('is_array', False) or leaf.get('type') == 'array':
                string_buffer.write('{0}{1} = {2} (\n'.format(
                    ' ' * indent_level, leaf.get(graph.data.key_tag),
                    leaf.get(orm_data_tag)))

                array_indent = indent_level + indent
                for array_type in leaf.get(graph.data.value_tag, default=[]):
                    string_buffer.write('{0}{1},\n'.format(
                        ' ' * array_indent, array_type))

                string_buffer.write('{0}),\n'.format(' ' * indent_level))

            # Format key, value pairs
            else:
                string_buffer.write('{0}{1} = {2},\n'.format(
                    ' ' * indent_level, leaf.get(graph.data.key_tag),
                    leaf.get(graph.data.value_tag, default='')))

        # Second, process child non-leaf nodes
        for child in [n for n in node.children() if not n.isleaf]:

            # Write block header
            key = ''
            if not child.get('is_array',
                             False) or child.get('type') == 'array':
                key = '{0} = '.format(child.get(graph.data.key_tag))
            string_buffer.write('{0}{1}{2} (\n'.format(
                ' ' * indent_level, key, child.get(orm_data_tag)))

            # Indent new data block one level down and walk children
            indent_level += indent
            _walk_dict(child, indent_level)

            # Close data block and indent one level up
            indent_level -= indent
            string_buffer.write('{0}),\n'.format(' ' * indent_level))

    # Build adjacency only once
    with graph.adjacency as adj:
        rootnode = graph.getnodes(root_nid)

    if rootnode.isleaf:
        _walk_dict(rootnode, 0)
    else:
        string_buffer.write('{0} (\n'.format(rootnode.get(orm_data_tag)))
        _walk_dict(rootnode, indent)
        string_buffer.write(')\n')

    # Restore original ORM and NodeTools
    graph.node_tools = curr_nt
    graph.orm = curr_orm

    logger.info('Graph {0} exported in WEB format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 10
0
def write_dot(graph, graph_name=None):
    """
    DOT graphs are either directional (digraph) or undirectional, mixed mode
    is not supported.

    Nodes and edges are all exported separably, short hand notations are not
    supported. Grouping and supgraphs are not supported.
    Graph attributes in graph.data, graph.edges and graph.nodes will be
    exported as DOT directives regardless if they are official GraphVis DOT
    graph directives as listed in the reference documentation:
        https://www.graphviz.org/doc/info/attrs.html

    Dot reserved rendering keywords part of the graphs global attributes in
    graph.data or part of the node and edge attributes are exported as part
    of the DOT graph.

    :param graph:          Graph object to export
    :type graph:           :graphit:Graph
    :param graph_name:     name of the 'graph' or 'digraph'. Uses the 'title'
                           attribute in graph.data by default, else graph_name
    :type graph_name:      :py:str

    :return:               DOT graph representation
    :rtype:                :py:str
    """

    indent = ' ' * 4
    link = '->' if graph.directed else '--'

    # Create empty file buffer
    string_buffer = StringIO()

    # Write header comment and graph container
    graph_name = graph.data.get('title', graph_name or 'graph')
    string_buffer.write('//Created by {0} version {1}\n'.format(__module__, version()))
    string_buffer.write('{0} "{1}" {2}\n'.format('digraph' if graph.directed else 'graph', graph_name, '{'))

    # Write global DOT directives
    for dot_key, dot_value in graph.data.items():
        if isinstance(dot_value, PY_PRIMITIVES) and not dot_key.startswith('$'):
            string_buffer.write('{0}{1}={2}\n'.format(indent, dot_key, json.dumps(dot_value)))

    # Export nodes
    string_buffer.write('{0}//nodes\n'.format(indent))
    for node in graph.iternodes():
        attr = ['{0}={1}'.format(k, json.dumps(v)) for k, v in node.nodes[node.nid].items() if
                isinstance(v, PY_PRIMITIVES) and not k.startswith('$')]
        if attr:
            string_buffer.write('{0}{1} [{2}];\n'.format(indent, node.nid, ','.join(attr)))

    # Export adjacency
    string_buffer.write('{0}//edges\n'.format(indent))
    done = []
    for edge in graph.edges:
        if edge not in done:
            edges = sorted([edge, edge[::-1]])

            attr = []
            for e in edges:
                attr.extend(['{0}={1}'.format(k, json.dumps(v)) for k, v in graph.edges[e].items()
                             if isinstance(v, PY_PRIMITIVES) and not k.startswith('$')])

            start, end = edges[0]
            if attr:
                string_buffer.write('{0}{1} {2} {3} [{4}];\n'.format(indent, start, link, end, ','.join(attr)))
            else:
                string_buffer.write('{0}{1} {2} {3};\n'.format(indent, start, link, end))

            done.extend(edges)

    # Closing curly brace
    string_buffer.write('}\n')

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 11
0
def write_lgf(graph):
    """
    Write graph in LEMON Graph Format (LGF)

    :param graph:           Graph object to import LGF data in
    :type graph:            :graphit:Graph

    :return:                Graph object
    :rtype:                 :graphit:Graph
    """

    # Create empty file buffer
    string_buffer = StringIO()

    # Export meta-data
    string_buffer.write('# Graphit graph exported in LEMON Graph Format\n')
    string_buffer.write('# Graphit version: {0}, date: {1}\n'.format(
        version(), time.ctime()))

    # Export nodes
    if len(graph.nodes):
        string_buffer.write('\n@nodes\n')

        table = ExportTable(graph.nodes)
        string_buffer.write('id  {0}\n'.format(table.header))

        for node, attr in graph.nodes.items():
            string_buffer.write('{0}  {1}\n'.format(node,
                                                    table.export_row(attr)))

    # Export edges
    if len(graph.edges):
        string_buffer.write('\n@argcs\n' if graph.directed else '\n@edges\n')

        table = ExportTable(graph.edges)
        string_buffer.write('      {0}\n'.format(table.header or '_'))

        for edge, attr in graph.edges.items():
            string_buffer.write('{0}  {1}  {2}\n'.format(
                edge[0], edge[1], table.export_row(attr)))

    logger.info('Graph {0} exported in LEMON graph format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 12
0
class StreamReader(object):
    """
    StreamReader class

    Extention of the Python file like object (io class) to read data as
    flexible streams. Enables a stream to be read by character, or block
    of characters crossing file lines.

    :param stream:  textual data that can be parsed as file-like object.
    """

    def __init__(self, stream):

        self.stream = stream
        if isinstance(stream, str):
            self.stream = StringIO(stream)

        self.has_more = True
        self.block_pos = None

        self._line = None
        self._pos = 0
        self._cursor = self.stream.tell()

    def __getitem__(self, position):
        """
        Return character(s) at position

        Accepts a Python 'slice' object.
        Restores the cursor to the position before the call to __getitem__
        was made.

        :param position:    position of characters relative to document start
        :type postion:      :py:int or slice operator

        :rtype:             :py:str
        """

        # Key is a slice object
        if isinstance(position, slice):
            return self.slice(position.start, position.stop, step=position.step)

        curpos = self.tell()
        self.stream.seek(position, 0)

        line = self.stream.readline()
        self.stream.seek(curpos, 0)

        return line[0]

    def __iter__(self):
        """
        Implement class __iter__

        Iteration managed by 'next' method
        """

        return self

    def next(self):
        """
        Iterator next method

        Returns next character in the iterations as long as there are
        characters left in the file-like object

        :raises: StopIteration, if no more characters
        """

        if self._line is None:
            self._line = self.stream.readline()
            if not self._line:
                self.has_more = False
                raise StopIteration
            self._pos = self._cursor

        pos = self._cursor - self._pos
        if pos < len(self._line):
            self._cursor += 1
            return self._line[pos]
        else:
            self._line = None
            return self.next()

    __next__ = next

    def readline(self):
        """
        Returns 'readline' method of the base file-like object
        """

        line = self.stream.readline()
        self._cursor = self.stream.tell()

        return line

    def set_cursor(self, position):
        """
        Move the file reader cursor to a new position in the file

        :param position:    position to move to
        :type position:     :py:int
        """

        self.stream.seek(position, 0)
        self._line = None
        self._pos = 0
        self._cursor = position

    def read_upto_char(self, chars, keep=False):
        """
        Return characters from active position up to a certain
        character or the first occurrence of one of multiple
        characters.

        :param chars:   character(s) to search for.
        :type chars:    :py:str, :py:list, :py:tuple
        :param keep:    keep the character to search for as part of the
                        returned string
        :type keep:     :py:bool

        :return:        tuple of text segment and termination character
        :rtype:         :py:tuple
        """

        if not isinstance(chars, (list, tuple)):
            chars = [chars]

        curpos = self.tell()
        for char in self:
            if char in chars:
                stop = self.tell()
                if not keep:
                    stop -= 1
                self.block_pos = (curpos, stop)
                return self.slice(*self.block_pos), char

        return None, None

    def read_upto_block(self, blocks, sep=(' ', '\n'), keep=False):
        """
        Return characters from active position up to a certain block of
        characters or the first occurrence of one of multiple blocks.
        A block is defined as a sequence of characters bounded by separator
        characters `sep` usually spaces and newline characters.

        :param blocks:   block(s) to search for.
        :type blocks:    :py:str, :py:list, :py:tuple
        :param sep:      block seperation characters
        :type sep:       :py:tuple, :py:list
        :param keep:     keep the block to search for as part of the
                         returned string
        :type keep:      :py:bool

        :return:         tuple of text segment and termination character
        :rtype:          :py:tuple
        """

        if not isinstance(blocks, (list, tuple)):
            blocks = [blocks]

        curpos = self.tell()
        block = []
        for char in self:
            block.append(char)
            if char in sep:
                blockj = ''.join(block).strip()
                if len(block) and blockj in blocks:
                    self._cursor -= 1
                    if not keep:
                        self._cursor -= len(block)
                    self.block_pos = (curpos, self.tell())
                    return self.slice(*self.block_pos), blockj
                else:
                    block = []

        return None, None

    def slice(self, start, stop, step=1):
        """
        Text slice method.

        Returns a segment of text defined by a start and stop character
        position relative to the start of the text.

        :param start:   start character position
        :type start:    :py:int
        :param stop:    stop character position
        :type stop:     :py:str

        :rtype:         :py:str
        """

        if stop < start:
            return ''

        curpos = self.tell()
        self.stream.seek(start, 0)

        text_slice = []
        progress = self.stream.tell()
        read = True
        while read:
            line = self.stream.readline()
            if not line:
                read = False
                break
            if stop < self.stream.tell():
                text_slice.append(line[0:stop - progress])
                read = False
            else:
                text_slice.append(line)
                progress += len(line)

        self.stream.seek(curpos, 0)

        return ''.join(text_slice)

    def tell(self):
        """
        Return current position of file cursor

        :rtype: :py:int
        """

        return self._cursor
Esempio n. 13
0
def write_p2g(graph, graph_name_label='name'):
    """
    Export a graphit graph to P2G format

    :param graph:            Graph object to export
    :type graph:             :graphit:Graph
    :param graph_name_label: graph.data attribute label for the graph name
    :type graph_name_label:  :py:str

    :return:                 P2G graph representation
    :rtype:                  :py:str
    """

    # Create empty file buffer
    string_buffer = StringIO()

    # Write graph meta-data
    string_buffer.write('{0}\n'.format(
        graph.data.get(graph_name_label, 'graph')))
    string_buffer.write('{0} {1}\n'.format(len(graph.nodes), len(graph.edges)))

    # Write nodes and edges
    key_tag = graph.data['key_tag']
    nodes = sorted(graph.nodes.keys())
    for node in nodes:
        string_buffer.write('{0}\n'.format(graph[node].get(key_tag)))
        string_buffer.write('{0}\n'.format(' '.join(
            [str(nodes.index(n)) for n in graph.adjacency[node]])))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()
Esempio n. 14
0
def write_lgr(graph,
              node_key=None,
              edge_key=None,
              node_data_type='string',
              edge_data_type='void'):
    """
    Export a graph to an LGR data format

    The LEDA format allows for export of only one node or edge data type
    (as: |{data type}|). For nodes this is usually the node label and for
    edges any arbitrary data key,value pair. In both cases the data type
    is required to be of either: string, int, float or bool.

    Nodes and edges are exported by iterating over them using `iternodes`
    and `iteredges`. Iteration uses the graphit Object Relations Mapper (ORM)
    allowing full control over the data export by overriding the `get`
    method globally in the 'NodeTools' or 'EdgeTools' classes or using custom
    classes registered with the ORM.
    Data returned by the `get` method will be serialized regardless the return
    type.

    The node and edge data types are registered globally in the LENA file using
    `node_data_type` and `edge_data_type` set to 'void' (no data) by default.

    :param graph:           Graph to export
    :type graph:            :graphit:Graph
    :param node_key:        key name of node data to export
    :type node_key:         :py:str
    :param edge_key:        key name of edge data to export
    :type edge_key:         :py:str
    :param node_data_type:  primitive data type of exported node data
    :type node_data_type:   :py:str
    :param edge_data_type:  primitive data type of exported edge data
    :type edge_data_type:   :py:str

    :return:                Graph exported as LGR format
    :rtype:                 :py:str
    :raises:                GraphitException
    """

    # Default node_key to graph.data.key_tag
    if node_key is None:
        node_key = graph.data.key_tag

    # If export of node/edge data corresponding data types need to be defined
    if (node_key is not None
            and node_data_type == 'void') or (edge_key is not None
                                              and edge_data_type == 'void'):
        raise GraphitException('Define node_data_type and/or edge_data_type')

    # Create empty file buffer
    string_buffer = StringIO()

    # Print header
    string_buffer.write('#header section\nLEDA.GRAPH\n{0}\n{1}\n'.format(
        node_data_type, edge_data_type))
    string_buffer.write('{0}\n'.format(-1 if graph.directed else -2))

    # Print nodes
    string_buffer.write('#nodes section\n{0}\n'.format(len(graph.nodes)))
    node_mapping = {}
    for i, node in enumerate(graph.iternodes(), start=1):
        string_buffer.write('|{{{0}}}|\n'.format(
            str(node.get(node_key, default=''))))
        node_mapping[node.nid] = i

    # Print edges
    string_buffer.write('#edges section\n{0}\n'.format(len(graph.edges)))
    for edge in graph.iteredges():
        source, target = edge.nid
        string_buffer.write('{0} {1} 0 |{{{2}}}|\n'.format(
            node_mapping[source], node_mapping[target],
            str(edge.get(edge_key, default=''))))

    logger.info('Graph {0} exported in LEDA format'.format(repr(graph)))

    # Reset buffer cursor
    string_buffer.seek(0)
    return string_buffer.read()