<commit_before>from uuid import uuid4
from datetime import datetime

# get the connection to the database
from .db import Base

# various sqlalchemy imports
from sqlalchemy import ForeignKey, desc
from sqlalchemy import Column, String, Text, Enum, Float
from sqlalchemy.orm import relationship, validates
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import func, select
from sqlalchemy.ext.associationproxy import association_proxy

import inspect

DATETIME_FMT = "%Y-%m-%dT%H:%M:%S.%f"


def new_uuid():
    return uuid4().hex


def timenow():
    time = datetime.now()
    return time.strftime(DATETIME_FMT)


class Node(Base):
    __tablename__ = "node"

    # the unique node id
    uuid = Column(String(32), primary_key=True, default=new_uuid)

    # the node type -- this allows for inheritance
    type = Column(String(50))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }

    # the time when the node was created
    creation_time = Column(String(26), nullable=False, default=timenow)

    # the status of the node
    status = Column(Enum("alive", "dead", "failed", name="node_status"),
                    nullable=False, default="alive")

    # the time when the node changed from alive->dead or alive->failed
    time_of_death = Column(String(26), nullable=True, default=None)

    # the information created by this node
    information = relationship(
        "Info", backref='origin', order_by="Info.creation_time")

    # the network that this node is a part of
    network_uuid = Column(
        String(32), ForeignKey('network.uuid'), nullable=True)

    # the participant uuid is the sha512 hash of the psiTurk uniqueId of the
    # participant who was this node.
    participant_uuid = Column(String(128), nullable=True)

    network = relationship("Network", foreign_keys=[network_uuid])

    def kill(self):
        self.status = "dead"
        self.time_of_death = timenow()

    def fail(self):
        self.status = "failed"
        self.time_of_death = timenow()

    # the predecessors and successors
    successors = relationship(
        "Node",
        secondary="vector",
        primaryjoin="Node.uuid==vector.c.origin_uuid",
        secondaryjoin="Node.uuid==vector.c.destination_uuid",
        backref="predecessors"
    )

    def get_incoming_vectors(self, status="alive"):
        if status == "all":
            incoming_vectors = Vector.query.filter_by(destination=self).all()
        elif status == "alive" or status == "dead":
            incoming_vectors = Vector.query.filter_by(destination=self).filter_by(status=status).all()
        else:
            raise(ValueError("Cannot get_incoming_vectors with status {} as it is not a valid status.".format(status)))
        return incoming_vectors

    def get_outgoing_vectors(self, status="alive"):
        if status == "all":
            outgoing_vectors = Vector.query.filter_by(origin=self).all()
        elif status == "alive" or status == "dead":
            outgoing_vectors = Vector.query.filter_by(origin=self).filter_by(status=status).all()
        else:
            raise(ValueError("Cannot get_outgoing_vectors with status {} as it is not a valid status.".format(status)))
        return outgoing_vectors

    def get_downstream_nodes(self, type=None, status="alive"):
        if type is None:
            type = Node
        if status == "all":
            return [v.destination for v in self.get_outgoing_vectors()
                    if isinstance(v.destination, type)]
        elif status == "alive" or status == "dead" or status == "failed":
            return [v.destination for v in self.get_outgoing_vectors()
                    if isinstance(v.destination, type) and v.destination.status == status]
        else:
            raise(ValueError("Cannot get_downstream_nodes with status {} as it is not a valid status.".format(status)))

    def get_upstream_nodes(self, type=None, status="alive"):
        if type is None:
            type = Node
        if status == "all":
            return [v.origin for v in self.get_incoming_vectors()
                    if isinstance(v.origin, type)]
        elif status == "alive" or status == "dead" or status == "failed":
            return [v.origin for v in self.get_incoming_vectors()
                    if isinstance(v.origin, type) and v.origin.status == status]
        else:
            raise(ValueError("Cannot get_upstream_nodes with status {} as it is not a valid status.".format(status)))

    def get_infos(self, type=None):
        if type is None:
            type = Info
        if not issubclass(type, Info):
            raise(TypeError("Cannot get-info of type {} as it is not a valid type.".format(type)))
        else:
            return type\
                .query\
                .order_by(type.creation_time)\
                .filter(type.origin == self)\
                .all()

    @property
    def successors2(self):
        print "successors2 is deprecated, use get_downstream_nodes(type=Agent) instead"
        return self.get_downstream_nodes(type=Agent)

    @property
    def downstream_nodes(self):
        print "downstream_nodes is deprecated, use get_downstream_nodes() instead"
        return self.get_downstream_nodes()

    @property
    def downstream_agents(self):
        print "downstream_agents is deprecated, use get_downstream_nodes(type=Agent) instead"
        return self.get_downstream_nodes(type=Agent)

    @property
    def upstream_nodes(self):
        print "upstream_nodes is deprecated, use get_upstream_nodes() instead"
        return self.get_upstream_nodes()

    @property
    def upstream_agents(self):
        print "upstream_agents is deprecated, use get_upstream_nodes(type=Agent) instead"
        return self.get_upstream_nodes(type=Agent)

    @property
    def predecessors2(self):
        print "predecessors2 is deprecated, use get_upstream_nodes(type=Agent) instead"
        return self.get_upstream_nodes(type=Agent)

    def __repr__(self):
        return "Node-{}-{}".format(self.uuid[:6], self.type)

    def connect_to(self, other_node):
        """Creates a directed edge from self to other_node
        other_node may be a list of nodes
        will raise an error if you try to conntect_to anything other than a node
        will also raise an error if you try to connect_to a source"""
        if isinstance(other_node, list):
            for node in other_node:
                self.connect_to(node)
        elif self == other_node:
            raise(ValueError("{} cannot connect to itself.".format(self)))
        elif isinstance(other_node, Source):
            raise(TypeError("{} cannot connect_to {} as it is a Source.".format(self, other_node)))
        elif not isinstance(other_node, Node):
            raise(TypeError('{} cannot connect to {} as it is a {}'.format(self, other_node, type(other_node))))
        elif self.network_uuid != other_node.network_uuid:
            raise(ValueError(("{} cannot connect to {} as they are not " +
                              "in the same network. {} is in network {}, " +
                              "but {} is in network {}.")
                             .format(self, other_node, self, self.network_uuid,
                                     other_node, other_node.network_uuid)))
        else:
            Vector(origin=self, destination=other_node, network=self.network)
            #vector = Vector(origin=self, destination=other_node)
            #return vector

    def connect_from(self, other_node):
        """Creates a directed edge from other_node to self
        other_node may be a list of nodes
        will raise an error if you try to connect_from anything other than a node"""
        if isinstance(other_node, list):
            for node in other_node:
                node.connect_to(self)
        else:
            other_node.connect_to(self)

    def transmit(self, what=None, to_whom=None):
        """<commit_msg>can't connect twice to the same node<commit_after>from uuid import uuid4
from datetime import datetime

# get the connection to the database
from .db import Base

# various sqlalchemy imports
from sqlalchemy import ForeignKey, desc
from sqlalchemy import Column, String, Text, Enum, Float
from sqlalchemy.orm import relationship, validates
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import func, select
from sqlalchemy.ext.associationproxy import association_proxy

import inspect

DATETIME_FMT = "%Y-%m-%dT%H:%M:%S.%f"


def new_uuid():
    return uuid4().hex


def timenow():
    time = datetime.now()
    return time.strftime(DATETIME_FMT)


class Node(Base):
    __tablename__ = "node"

    # the unique node id
    uuid = Column(String(32), primary_key=True, default=new_uuid)

    # the node type -- this allows for inheritance
    type = Column(String(50))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }

    # the time when the node was created
    creation_time = Column(String(26), nullable=False, default=timenow)

    # the status of the node
    status = Column(Enum("alive", "dead", "failed", name="node_status"),
                    nullable=False, default="alive")

    # the time when the node changed from alive->dead or alive->failed
    time_of_death = Column(String(26), nullable=True, default=None)

    # the information created by this node
    information = relationship(
        "Info", backref='origin', order_by="Info.creation_time")

    # the network that this node is a part of
    network_uuid = Column(
        String(32), ForeignKey('network.uuid'), nullable=True)

    # the participant uuid is the sha512 hash of the psiTurk uniqueId of the
    # participant who was this node.
    participant_uuid = Column(String(128), nullable=True)

    network = relationship("Network", foreign_keys=[network_uuid])

    def kill(self):
        self.status = "dead"
        self.time_of_death = timenow()

    def fail(self):
        self.status = "failed"
        self.time_of_death = timenow()

    # the predecessors and successors
    successors = relationship(
        "Node",
        secondary="vector",
        primaryjoin="Node.uuid==vector.c.origin_uuid",
        secondaryjoin="Node.uuid==vector.c.destination_uuid",
        backref="predecessors"
    )

    def get_incoming_vectors(self, status="alive"):
        if status == "all":
            incoming_vectors = Vector.query.filter_by(destination=self).all()
        elif status == "alive" or status == "dead":
            incoming_vectors = Vector.query.filter_by(destination=self).filter_by(status=status).all()
        else:
            raise(ValueError("Cannot get_incoming_vectors with status {} as it is not a valid status.".format(status)))
        return incoming_vectors

    def get_outgoing_vectors(self, status="alive"):
        if status == "all":
            outgoing_vectors = Vector.query.filter_by(origin=self).all()
        elif status == "alive" or status == "dead":
            outgoing_vectors = Vector.query.filter_by(origin=self).filter_by(status=status).all()
        else:
            raise(ValueError("Cannot get_outgoing_vectors with status {} as it is not a valid status.".format(status)))
        return outgoing_vectors

    def get_downstream_nodes(self, type=None, status="alive"):
        if type is None:
            type = Node
        if status == "all":
            return [v.destination for v in self.get_outgoing_vectors()
                    if isinstance(v.destination, type)]
        elif status == "alive" or status == "dead" or status == "failed":
            return [v.destination for v in self.get_outgoing_vectors()
                    if isinstance(v.destination, type) and v.destination.status == status]
        else:
            raise(ValueError("Cannot get_downstream_nodes with status {} as it is not a valid status.".format(status)))

    def get_upstream_nodes(self, type=None, status="alive"):
        if type is None:
            type = Node
        if status == "all":
            return [v.origin for v in self.get_incoming_vectors()
                    if isinstance(v.origin, type)]
        elif status == "alive" or status == "dead" or status == "failed":
            return [v.origin for v in self.get_incoming_vectors()
                    if isinstance(v.origin, type) and v.origin.status == status]
        else:
            raise(ValueError("Cannot get_upstream_nodes with status {} as it is not a valid status.".format(status)))

    def get_infos(self, type=None):
        if type is None:
            type = Info
        if not issubclass(type, Info):
            raise(TypeError("Cannot get-info of type {} as it is not a valid type.".format(type)))
        else:
            return type\
                .query\
                .order_by(type.creation_time)\
                .filter(type.origin == self)\
                .all()

    @property
    def successors2(self):
        print "successors2 is deprecated, use get_downstream_nodes(type=Agent) instead"
        return self.get_downstream_nodes(type=Agent)

    @property
    def downstream_nodes(self):
        print "downstream_nodes is deprecated, use get_downstream_nodes() instead"
        return self.get_downstream_nodes()

    @property
    def downstream_agents(self):
        print "downstream_agents is deprecated, use get_downstream_nodes(type=Agent) instead"
        return self.get_downstream_nodes(type=Agent)

    @property
    def upstream_nodes(self):
        print "upstream_nodes is deprecated, use get_upstream_nodes() instead"
        return self.get_upstream_nodes()

    @property
    def upstream_agents(self):
        print "upstream_agents is deprecated, use get_upstream_nodes(type=Agent) instead"
        return self.get_upstream_nodes(type=Agent)

    @property
    def predecessors2(self):
        print "predecessors2 is deprecated, use get_upstream_nodes(type=Agent) instead"
        return self.get_upstream_nodes(type=Agent)

    def __repr__(self):
        return "Node-{}-{}".format(self.uuid[:6], self.type)

    def connect_to(self, other_node):
        """Creates a directed edge from self to other_node
        other_node may be a list of nodes
        will raise an error if you try to conntect_to anything other than a node
        will also raise an error if you try to connect_to a source"""
        if isinstance(other_node, list):
            for node in other_node:
                self.connect_to(node)
        elif self.has_connection_to(other_node):
            print "Warning! {} is already connected to {}, cannot make another vector without killing the old one.".format(self, other_node)
        elif self == other_node:
            raise(ValueError("{} cannot connect to itself.".format(self)))
        elif isinstance(other_node, Source):
            raise(TypeError("{} cannot connect_to {} as it is a Source.".format(self, other_node)))
        elif not isinstance(other_node, Node):
            raise(TypeError('{} cannot connect to {} as it is a {}'.format(self, other_node, type(other_node))))
        elif self.network_uuid != other_node.network_uuid:
            raise(ValueError(("{} cannot connect to {} as they are not " +
                              "in the same network. {} is in network {}, " +
                              "but {} is in network {}.")
                             .format(self, other_node, self, self.network_uuid,
                                     other_node, other_node.network_uuid)))
        else:
            Vector(origin=self, destination=other_node, network=self.network)
            #vector = Vector(origin=self, destination=other_node)
            #return vector

    def connect_from(self, other_node):
        """Creates a directed edge from other_node to self
        other_node may be a list of nodes
        will raise an error if you try to connect_from anything other than a node"""
        if isinstance(other_node, list):
            for node in other_node:
                node.connect_to(self)
        else:
            other_node.connect_to(self)

    def transmit(self, what=None, to_whom=None):
        """
{'fraction_non_alphanumeric': 0.09863756858393441, 'fraction_numerical': 0.002835830096788114, 'mean_word_length': 2.6355894217839535, 'pattern_counts': {'":': 16, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6392026', 'n_tokens_mistral': 4639, 'n_tokens_neox': 4459, 'n_words': 1333}