<commit_before>#!/usr/bin/env python
# -*- coding: utf8 -*-

from YakDB.Graph.Exceptions import ConsistencyException
from YakDB.Exceptions import ParameterException
from BasicAttributes import BasicAttributes
from ExtendedAttributes import ExtendedAttributes

class Edge(object):
    """
    Represents a directed edge in a graph.
    """
    def __init__(self, source, target, graph, edgeType="", basicAttrs=None):
        """
        Create a new edge instance.
        Usually this constructor shall not be used directly
        
        @param source The node instance that acts as the source node.
        @param target The node instance that acts as the target node
        @param The edge type
        @param basicAttrs The basic attributes, or None to use empty set.
        """
        self.sourceNodeId = source
        self.targetNodeId = target
        self.graphAttr = graph
        self.edgeType = edgeType
        #Serialize the edge database keys
        self.activeKey = <KEY> (edgeType, self.sourceNodeId, self.targetNodeId)
        self.passiveKey = <KEY> (edgeType, self.targetNodeId, self.sourceNodeId)
        #Initialize basic and extended attributes
        self.basicAttrs = BasicAttributes(self, basicAttrs)
        self.extendedAttrs = ExtendedAttributes(self)
    @property
    def basicAttributes(self):
        """
        The basic attributes for the current node
        """
        return self.basicAttrs
    @property
    def extendedAttributes(self):
        """
        Get the extended attributes. Note that extended attributes
        are generally lazy-loaded and not persistently stored in API classes
        """
        return self.extendedAttrs
    @property
    def graph(self):
        """
        The graph this node relates to
        """
        return self.graphAttr
    @property
    def id(self):
        """
        This method is used by the extended attributes
        to get the database key. The database key for extended attributes
        is the active edge, so we return that here.
        """
        return self.activeKey
    @property
    def source(self):
        """
        The ID of the source node
        """
        return self.sourceNodeId
    @property
    def target(self):
        """
        The ID of the target node
        """
        return self.targetNodeId
    @property
    def type(self):
        """
        The edge type
        """
        return self.edgeType
    def _save(self):
        """
        Writes the edge (both passive and active versions)
        to the database
        """
        self.graph._writeEdge(self.activeKey,
                              self.passiveKey,
                              self.basicAttrs.serialize())
    @staticmethod
    def _getAllEdgesScanKeys(nodeId, type=""):
        """ee
        Get the scan keys to scan for ALL edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0E" % formatTuple, "%s\x1F%s\x10" % formatTuple)
    @staticmethod
    def _getIncomingEdgesScanKeys(nodeId, type=""):
        """
        Get the scan keys to scan for Incoming edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0F" % formatTuple, "%s\x1F%s\x10" % formatTuple)
    @staticmethod
    def _getOutgoingEdgesScanKeys(nodeId, type=""):
        """
        Get the scan keys to scan for OUtgoing edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0E" % formatTuple, "%s\x1F%s\x0F" % formatTuple)
    @staticmethod
    def _deserializeEdge(key):
        """
        Deserializes an edge database key
        @return A tuple (sourceId, targetId, type) deserialized from the database key
        
        >>> Edge._deserializeEdge("\\x1Fa\\x0Eb")
        ('a', 'b', '')
        >>> Edge._deserializeEdge("\\x1Fa\\x0Fb")
        ('b', 'a', '')
        >>> Edge._deserializeEdge("mytype\\x1Fa\\x0Eb")
        ('a', 'b', 'mytype')
        >>> Edge._deserializeEdge("mytype\\x1Fa\\x0Fb")
        ('b', 'a', 'mytype')
        """
        #Split the typeplanetexplorers.pathea.net
        typeSeparatorIndex = key.find("\x1F")
        if typeSeparatorIndex == -1:
            raise ParameterException("Could not find     type separator in edge key!")
        edgeType = key[0:typeSeparatorIndex]
        key = key[typeSeparatorIndex+1:]
        #Split the source and target node
        outgoingIndex = key.find("\x0E")
        incomingIndex = key.find("\x0F")
        if outgoingIndex == -1 and incomingIndex == -1:
            raise ParameterException("Could not find OUT or IN separator in edge key!")
        isIncoming = (outgoingIndex == -1)
        splitKey = (incomingIndex if isIncoming else outgoingIndex)
        firstNodeIndex = key[0:splitKey]
        secondNodeIndex = key[splitKey+1:]
        sourceNode = (secondNodeIndex if isIncoming else firstNodeIndex)
        targetNode = (firstNodeIndex if isIncoming else secondNodeIndex)
        #Create and return the tuple
        return (sourceNode, targetNode, edgeType)
    def __str__(self):
        formatTuple = (self.source, self.target, self.type, self.basicAttrs.getAttributes())
        return "Edge(source='%s', target='%s', type='%s', basicAttrs=%s)" % formatTuple

if __name__ == "__main__":
    import doctest
    doctest.testmod()<commit_msg>Fix edges<commit_after>#!/usr/bin/env python
# -*- coding: utf8 -*-

from YakDB.Graph.Exceptions import ConsistencyException
from YakDB.Exceptions import ParameterException
from BasicAttributes import BasicAttributes
from ExtendedAttributes import ExtendedAttributes

class Edge(object):
    """
    Represents a directed edge in a graph.
    """
    def __init__(self, source, target, graph, edgeType="", basicAttrs=None):
        """
        Create a new edge instance.
        Usually this constructor shall not be used directly
        
        @param source The node instance that acts as the source node.
        @param target The node instance that acts as the target node
        @param The edge type
        @param basicAttrs The basic attributes, or None to use empty set.
        """
        self.sourceNodeId = source
        self.targetNodeId = target
        self.graphAttr = graph
        self.edgeType = edgeType
        #Serialize the edge database keys
        self.activeKey = <KEY> (edgeType, self.sourceNodeId, self.targetNodeId)
        self.passiveKey = "%s\x1F%s\x0F%s" % (edgeType, self.targetNodeId, self.sourceNodeId)
        #Initialize basic and extended attributes
        self.basicAttrs = BasicAttributes(self, basicAttrs)
        self.extendedAttrs = ExtendedAttributes(self)
    @property
    def basicAttributes(self):
        """
        The basic attributes for the current node
        """
        return self.basicAttrs
    @property
    def extendedAttributes(self):
        """
        Get the extended attributes. Note that extended attributes
        are generally lazy-loaded and not persistently stored in API classes
        """
        return self.extendedAttrs
    @property
    def graph(self):
        """
        The graph this node relates to
        """
        return self.graphAttr
    @property
    def id(self):
        """
        This method is used by the extended attributes
        to get the database key. The database key for extended attributes
        is the active edge, so we return that here.
        """
        return self.activeKey
    @property
    def source(self):
        """
        The ID of the source node
        """
        return self.sourceNodeId
    @property
    def target(self):
        """
        The ID of the target node
        """
        return self.targetNodeId
    @property
    def type(self):
        """
        The edge type
        """
        return self.edgeType
    def _save(self):
        """
        Writes the edge (both passive and active versions)
        to the database
        """
        self.graph._writeEdge(self.activeKey,
                              self.passiveKey,
                              self.basicAttrs.serialize())
    @staticmethod
    def _getAllEdgesScanKeys(nodeId, type=""):
        """ee
        Get the scan keys to scan for ALL edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0E" % formatTuple, "%s\x1F%s\x10" % formatTuple)
    @staticmethod
    def _getIncomingEdgesScanKeys(nodeId, type=""):
        """
        Get the scan keys to scan for Incoming edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0F" % formatTuple, "%s\x1F%s\x10" % formatTuple)
    @staticmethod
    def _getOutgoingEdgesScanKeys(nodeId, type=""):
        """
        Get the scan keys to scan for OUtgoing edges for a given node.
        @param type The edge type
        @return (startKey, endKey) The scan start and end keys
        """
        formatTuple = (type, nodeId)
        return ("%s\x1F%s\x0E" % formatTuple, "%s\x1F%s\x0F" % formatTuple)
    @staticmethod
    def _deserializeEdge(key):
        """
        Deserializes an edge database key
        @return A tuple (sourceId, targetId, type) deserialized from the database key
        
        >>> Edge._deserializeEdge("\\x1Fa\\x0Eb")
        ('a', 'b', '')
        >>> Edge._deserializeEdge("\\x1Fa\\x0Fb")
        ('b', 'a', '')
        >>> Edge._deserializeEdge("mytype\\x1Fa\\x0Eb")
        ('a', 'b', 'mytype')
        >>> Edge._deserializeEdge("mytype\\x1Fa\\x0Fb")
        ('b', 'a', 'mytype')
        """
        #Split the typeplanetexplorers.pathea.net
        typeSeparatorIndex = key.find("\x1F")
        if typeSeparatorIndex == -1:
            raise ParameterException("Could not find     type separator in edge key!")
        edgeType = key[0:typeSeparatorIndex]
        key = key[typeSeparatorIndex+1:]
        #Split the source and target node
        outgoingIndex = key.find("\x0E")
        incomingIndex = key.find("\x0F")
        if outgoingIndex == -1 and incomingIndex == -1:
            raise ParameterException("Could not find OUT or IN separator in edge key!")
        isIncoming = (outgoingIndex == -1)
        splitKey = (incomingIndex if isIncoming else outgoingIndex)
        firstNodeIndex = key[0:splitKey]
        secondNodeIndex = key[splitKey+1:]
        sourceNode = (secondNodeIndex if isIncoming else firstNodeIndex)
        targetNode = (firstNodeIndex if isIncoming else secondNodeIndex)
        #Create and return the tuple
        return (sourceNode, targetNode, edgeType)
    def __str__(self):
        formatTuple = (self.sourceNodeId, self.targetNodeId, self.type, self.basicAttrs.getAttributes())
        return "Edge(source='%s', target='%s', type='%s', basicAttrs=%s)" % formatTuple
    def __repr__(self): return self.__str__()

if __name__ == "__main__":
    import doctest
    doctest.testmod()
{'fraction_non_alphanumeric': 0.09862002285312473, 'fraction_numerical': 0.006152764349125429, 'mean_word_length': 2.2583046964490263, 'pattern_counts': {'":': 2, '<': 6, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4302415', 'n_tokens_mistral': 3182, 'n_tokens_neox': 3161, 'n_words': 996}