<commit_before>from py2neo import neo4j, cypher, rest
from .properties import Property, AliasProperty
from .relationship import RelationshipManager, OUTGOING, RelationshipDefinition
from .exception import (UniqueProperty, DoesNotExist, RequiredProperty, CypherException,
        ReadOnlyError, NoSuchProperty, PropertyNotIndexed)
from lucenequerybuilder import Q
import types
import sys
from urlparse import urlparse
import os

DATABASE_URL = os.environ.get('NEO4J_REST_URL', 'http://localhost:7474/db/data/')

def connection():
    try:
        return connection.db
    except AttributeError:
        url = DATABASE_URL

        u = urlparse(url)
        if u.netloc.find('@') > -1:
            credentials, host = u.netloc.split('@')
            user, password, = credentials.split(':')
            neo4j.authenticate(host, user, password)
            url = ''.join([u.scheme, '://', host, u.path, u.query])

        connection.db = neo4j.GraphDatabaseService(url)
        return connection.db

class Client(object):
    @property
    def client(self):
        return connection()

class NodeIndexManager(Client):
    def __init__(self, node_class, index_name):
        self.node_class = node_class
        self.name = index_name

    def _check_params(self, params):
        """checked args are indexed and convert aliases"""
        for key in params.keys():
            prop = self.node_class.get_property(key)
            if not prop.is_indexed:
                raise PropertyNotIndexed(key)
            if isinstance(prop, AliasProperty):
                real_key = prop.aliased_to()
                if real_key in params:
                    msg = "Can't alias {0} to {1} in {2}, key {0} exists."
                    raise Exception(msg.format(key, real_key, repr(params)))
                params[real_key] = params[key]
                del params[key]

    def search(self, query=None, **kwargs):
        """ Load multiple nodes via index """
        self._check_params(kwargs)
        if not query:
            query = reduce(lambda x, y: x & y, [Q(k, v) for k, v in kwargs.iteritems()])

        return [self.node_class.inflate(n) for n in self.__index__.query(str(query))]

    def get(self, query=None, **kwargs):
        """ Load single node via index """
        nodes = self.search(query, **kwargs)
        if len(nodes) == 1:
            return nodes[0]
        elif len(nodes) > 1:
            raise Exception("Multiple nodes returned from query, expected one")
        else:
            raise self.node_class.DoesNotExist("Can't find node in index matching query")

    @property
    def __index__(self):
        return self.client.get_or_create_index(neo4j.Node, self.name)

class CypherMixin(Client):
    def cypher(self, query, params={}):
        assert hasattr(self, '__node__')
        params.update({'self': self.__node__.id})
        try:
            return cypher.execute(self.client, query, params)
        except cypher.CypherError as e:
            message, etype, jtrace = e.args
            raise CypherException(query, params, message, etype, jtrace)

    def start_cypher(self, query, params={}):
        sys.stderr.write("DEPRECATION 19/10/2012: start_cypher not supported, please use cypher\n")
        start = "START a=node({self}) "
        return self.cypher(start + query, params)

class StructuredNodeMeta(type):
    def __new__(cls, name, bases, dct):
        dct.update({'DoesNotExist': type('DoesNotExist', (DoesNotExist,), dct)})
        cls = super(StructuredNodeMeta, cls).__new__(cls, name, bases, dct)
        for key, value in dct.iteritems():
            if issubclass(value.__class__, Property):
                value.name = key
                value.owner = cls
                # support for 'magic' properties
                if hasattr(value, 'setup') and hasattr(value.setup, '__call__'):
                    value.setup()
        if cls.__name__ not in ['StructuredNode', 'ReadOnlyNode']:
            if '_index_name' in dct:
                name = dct['_index_name']
            cls.index = NodeIndexManager(cls, name)
        return cls

class StructuredNode(CypherMixin):
    """ Base class for nodes requiring formal declaration """

    __metaclass__ = StructuredNodeMeta

    @classmethod
    def get_property(cls, name):
        try:
            node_property = getattr(cls, name)
        except AttributeError:
            raise NoSuchProperty(name, cls)
        if not issubclass(node_property.__class__, Property)\
                or not issubclass(node_property.__class__, AliasProperty):
            NoSuchProperty(name, cls)
        return node_property

    @classmethod
    def category(cls):
        return category_factory(cls)

    @classmethod
    def inflate(cls, node):
        props = {}
        for scls in cls.mro():
            for key, prop in scls.__dict__.iteritems():
                if issubclass(prop.__class__, Property):
                    if key in node.__metadata__['data']:
                        props[key] = prop.inflate(node.__metadata__['data'][key], node_id=node.id)
                    else:
                        props[key] = None

        snode = cls(**props)
        snode.__node__ = node
        return snode

    def __init__(self, *args, **kwargs):
        self.__node__ = None

        for cls in self.__class__.mro():
            for key, val in cls.__dict__.iteritems():
                if val.__class__ is RelationshipDefinition:
                    self.__dict__[key] = val.build_manager(self, key)
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

    @property
    def properties(self):
        """ Return properties and values of a node """
        props = {}
        for key, value in self.__dict__.iteritems():
            if not key.startswith('_')\
                and not isinstance(value, types.MethodType)\
                and not isinstance(value, RelationshipManager)\
                and not isinstance(value, AliasProperty)\
                and value != None:
                    props[key] = value

        return props

    def _create(self, props):
        relation_name = self.__class__.__name__.upper()
        self.__node__, rel = self.client.create(props,
                (category_factory(self.__class__).__node__, relation_name, 0))
        if not self.__node__:
            Exception('Failed to create new ' + self.__class__.__name__)

        # Update indexes
        try:
            self._update_index(props)
        except Exception:
            exc_info = sys.exc_info()
            self.delete()
            raise exc_info[1], None, exc_info[2]

    def _update_index(self, props):
        for cls in self.__class__.mro():
            if cls.__name__ == 'StructuredNode' or cls.__name__ == 'ReadOnlyNode':
                break
            batch = neo4j.WriteBatch(self.client)
            for key, value in props.iteritems():
                if key in cls.__dict__.keys():
                    node_property = cls.get_property(key)
                    if node_property.unique_index:
                        try:
                            batch.add_indexed_node_or_fail(cls.index.__index__, key, value, self.__node__)
                        except NotImplementedError:
                            batch.get_or_add_indexed_node(cls.index.__index__, key, value, self.__node__)
                    elif node_property.index:
                        batch.add_indexed_node(cls.index.__index__, key, value, self.__node__)
            requests = batch.requests
            try:
                i = 0
                for r in batch._submit():
                    if r.status == 200:
                        raise UniqueProperty(requests[i], cls.index.name)
                    i = i + 1
            except rest.ResourceConflict as r:
                raise UniqueProperty(requests[r.id], cls.index.name)

    def _deflate(self):
        node_props = self.properties
        deflated = {}
        for cls in self.__class__.mro():
            for key, prop in cls.__dict__.iteritems():
                if issubclass(prop.__class__, Property):
                    if key in node_props and node_props[key] is not None:
                        node_id = self.__node__.id if self.__node__ else None
                        deflated[key] = prop.deflate(node_props[key], node_id=node_id)
                    elif prop.required:
                        raise RequiredProperty(key, self.__class__)
        return deflated

    def save(self):
        props = self._deflate()
        if self.__node__:
            self.__node__.set_properties(props)
            self.__class__.index.__index__.remove(entity=self.__node__)
            self._update_index(props)
        else:
            self._create(props)
        return self

    def delete(self):
        if self.__node__:
            to_delete = self.__node__.get_relationships()
            to_delete.append(self.__node__)
            self.client.delete(*to_delete)
            self.__node__ = None
        else:
            raise Exception("Node has not been saved so cannot be deleted")
        return True

class CategoryNode(CypherMixin):
    def __init__(self, name, *args, **kwargs):
        self.name = name
        super(CategoryNode, self).__init__(*args, **kwargs)

class InstanceManager(RelationshipManager):
    """Manage 'instance' rel of category nodes"""
    def connect(self, node):
        raise Exception("connect not available from category node")

    def disconnect(self, node):
        raise Exception("disconnect not available from category node")

def category_factory(instance_cls):
    """ Retrieve category node by name """
    name = instance_cls.__name__

    if not hasattr(category_factory, 'cache'):
        category_factory.cache = {}

    if not name in category_factory.cache:
        category_index = connection().get_or_create_index(neo4j.Node, 'Category')
        node = category_index.get_or_create('category', name, {'category': name})
        category = CategoryNode(name)
        category.__node__ = node
        category.instance = InstanceManager(OUTGOING, name.upper(), instance_cls, category)
        category_factory.cache[name] = category
    return category_factory.cache[name]

class ReadOnlyNode(StructuredNode):
    def delete(self):
        raise ReadOnlyError("You cannot delete read-only nodes")

    def save(self):
        raise ReadOnlyError("You cannot save read-only nodes")
<commit_msg>fix an AliasProprty bug<commit_after>from py2neo import neo4j, cypher, rest
from .properties import Property, AliasProperty
from .relationship import RelationshipManager, OUTGOING, RelationshipDefinition
from .exception import (UniqueProperty, DoesNotExist, RequiredProperty, CypherException,
        ReadOnlyError, NoSuchProperty, PropertyNotIndexed)
from lucenequerybuilder import Q
import types
import sys
from urlparse import urlparse
import os

DATABASE_URL = os.environ.get('NEO4J_REST_URL', 'http://localhost:7474/db/data/')

def connection():
    try:
        return connection.db
    except AttributeError:
        url = DATABASE_URL

        u = urlparse(url)
        if u.netloc.find('@') > -1:
            credentials, host = u.netloc.split('@')
            user, password, = credentials.split(':')
            neo4j.authenticate(host, user, password)
            url = ''.join([u.scheme, '://', host, u.path, u.query])

        connection.db = neo4j.GraphDatabaseService(url)
        return connection.db

class Client(object):
    @property
    def client(self):
        return connection()

class NodeIndexManager(Client):
    def __init__(self, node_class, index_name):
        self.node_class = node_class
        self.name = index_name

    def _check_params(self, params):
        """checked args are indexed and convert aliases"""
        for key in params.keys():
            prop = self.node_class.get_property(key)
            if not prop.is_indexed:
                raise PropertyNotIndexed(key)
            if isinstance(prop, AliasProperty):
                real_key = prop.aliased_to()
                if real_key in params:
                    msg = "Can't alias {0} to {1} in {2}, key {0} exists."
                    raise Exception(msg.format(key, real_key, repr(params)))
                params[real_key] = params[key]
                del params[key]

    def search(self, query=None, **kwargs):
        """ Load multiple nodes via index """
        self._check_params(kwargs)
        if not query:
            query = reduce(lambda x, y: x & y, [Q(k, v) for k, v in kwargs.iteritems()])

        return [self.node_class.inflate(n) for n in self.__index__.query(str(query))]

    def get(self, query=None, **kwargs):
        """ Load single node via index """
        nodes = self.search(query, **kwargs)
        if len(nodes) == 1:
            return nodes[0]
        elif len(nodes) > 1:
            raise Exception("Multiple nodes returned from query, expected one")
        else:
            raise self.node_class.DoesNotExist("Can't find node in index matching query")

    @property
    def __index__(self):
        return self.client.get_or_create_index(neo4j.Node, self.name)

class CypherMixin(Client):
    def cypher(self, query, params={}):
        assert hasattr(self, '__node__')
        params.update({'self': self.__node__.id})
        try:
            return cypher.execute(self.client, query, params)
        except cypher.CypherError as e:
            message, etype, jtrace = e.args
            raise CypherException(query, params, message, etype, jtrace)

    def start_cypher(self, query, params={}):
        sys.stderr.write("DEPRECATION 19/10/2012: start_cypher not supported, please use cypher\n")
        start = "START a=node({self}) "
        return self.cypher(start + query, params)

class StructuredNodeMeta(type):
    def __new__(cls, name, bases, dct):
        dct.update({'DoesNotExist': type('DoesNotExist', (DoesNotExist,), dct)})
        cls = super(StructuredNodeMeta, cls).__new__(cls, name, bases, dct)
        for key, value in dct.iteritems():
            if issubclass(value.__class__, Property):
                value.name = key
                value.owner = cls
                # support for 'magic' properties
                if hasattr(value, 'setup') and hasattr(value.setup, '__call__'):
                    value.setup()
        if cls.__name__ not in ['StructuredNode', 'ReadOnlyNode']:
            if '_index_name' in dct:
                name = dct['_index_name']
            cls.index = NodeIndexManager(cls, name)
        return cls

class StructuredNode(CypherMixin):
    """ Base class for nodes requiring formal declaration """

    __metaclass__ = StructuredNodeMeta

    @classmethod
    def get_property(cls, name):
        try:
            node_property = getattr(cls, name)
        except AttributeError:
            raise NoSuchProperty(name, cls)
        if not issubclass(node_property.__class__, Property)\
                or not issubclass(node_property.__class__, AliasProperty):
            NoSuchProperty(name, cls)
        return node_property

    @classmethod
    def category(cls):
        return category_factory(cls)

    @classmethod
    def inflate(cls, node):
        props = {}
        for scls in cls.mro():
            for key, prop in scls.__dict__.iteritems():
                if issubclass(prop.__class__, Property):
                    if key in node.__metadata__['data']:
                        props[key] = prop.inflate(node.__metadata__['data'][key], node_id=node.id)
                    else:
                        props[key] = None

        snode = cls(**props)
        snode.__node__ = node
        return snode

    def __init__(self, *args, **kwargs):
        self.__node__ = None

        for cls in self.__class__.mro():
            for key, val in cls.__dict__.iteritems():
                if val.__class__ is RelationshipDefinition:
                    self.__dict__[key] = val.build_manager(self, key)
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

    @property
    def properties(self):
        """ Return properties and values of a node """
        props = {}
        for key, value in self.__dict__.iteritems():
            if not key.startswith('_')\
                and not isinstance(value, types.MethodType)\
                and not isinstance(value, RelationshipManager)\
                and not isinstance(value, AliasProperty)\
                and value != None:
                    props[key] = value

        return props

    def _create(self, props):
        relation_name = self.__class__.__name__.upper()
        self.__node__, rel = self.client.create(props,
                (category_factory(self.__class__).__node__, relation_name, 0))
        if not self.__node__:
            Exception('Failed to create new ' + self.__class__.__name__)

        # Update indexes
        try:
            self._update_index(props)
        except Exception:
            exc_info = sys.exc_info()
            self.delete()
            raise exc_info[1], None, exc_info[2]

    def _update_index(self, props):
        for cls in self.__class__.mro():
            if cls.__name__ == 'StructuredNode' or cls.__name__ == 'ReadOnlyNode':
                break
            batch = neo4j.WriteBatch(self.client)
            for key, value in props.iteritems():
                if key in cls.__dict__.keys():
                    node_property = cls.get_property(key)
                    if node_property.unique_index:
                        try:
                            batch.add_indexed_node_or_fail(cls.index.__index__, key, value, self.__node__)
                        except NotImplementedError:
                            batch.get_or_add_indexed_node(cls.index.__index__, key, value, self.__node__)
                    elif node_property.index:
                        batch.add_indexed_node(cls.index.__index__, key, value, self.__node__)
            requests = batch.requests
            try:
                i = 0
                for r in batch._submit():
                    if r.status == 200:
                        raise UniqueProperty(requests[i], cls.index.name)
                    i = i + 1
            except rest.ResourceConflict as r:
                raise UniqueProperty(requests[r.id], cls.index.name)

    def _deflate(self):
        node_props = self.properties
        deflated = {}
        for cls in self.__class__.mro():
            for key, prop in cls.__dict__.iteritems():
                if (not isinstance(prop, AliasProperty)
                    and issubclass(prop.__class__, Property)):
                    if key in node_props and node_props[key] is not None:
                        node_id = self.__node__.id if self.__node__ else None
                        deflated[key] = prop.deflate(node_props[key], node_id=node_id)
                    elif prop.required:
                        raise RequiredProperty(key, self.__class__)
        return deflated

    def save(self):
        props = self._deflate()
        if self.__node__:
            self.__node__.set_properties(props)
            self.__class__.index.__index__.remove(entity=self.__node__)
            self._update_index(props)
        else:
            self._create(props)
        return self

    def delete(self):
        if self.__node__:
            to_delete = self.__node__.get_relationships()
            to_delete.append(self.__node__)
            self.client.delete(*to_delete)
            self.__node__ = None
        else:
            raise Exception("Node has not been saved so cannot be deleted")
        return True

class CategoryNode(CypherMixin):
    def __init__(self, name, *args, **kwargs):
        self.name = name
        super(CategoryNode, self).__init__(*args, **kwargs)

class InstanceManager(RelationshipManager):
    """Manage 'instance' rel of category nodes"""
    def connect(self, node):
        raise Exception("connect not available from category node")

    def disconnect(self, node):
        raise Exception("disconnect not available from category node")

def category_factory(instance_cls):
    """ Retrieve category node by name """
    name = instance_cls.__name__

    if not hasattr(category_factory, 'cache'):
        category_factory.cache = {}

    if not name in category_factory.cache:
        category_index = connection().get_or_create_index(neo4j.Node, 'Category')
        node = category_index.get_or_create('category', name, {'category': name})
        category = CategoryNode(name)
        category.__node__ = node
        category.instance = InstanceManager(OUTGOING, name.upper(), instance_cls, category)
        category_factory.cache[name] = category
    return category_factory.cache[name]

class ReadOnlyNode(StructuredNode):
    def delete(self):
        raise ReadOnlyError("You cannot delete read-only nodes")

    def save(self):
        raise ReadOnlyError("You cannot save read-only nodes")
<|endoftext|>


<commit_before>import logging
import gobject
from datetime import datetime, timedelta
from functools import partial

# Victron packages
from ve_utils import exit_on_error
from delegates.base import SystemCalcDelegate

# Path constants
BLPATH = "/Settings/CGwacs/BatteryLife";
STATE_PATH = BLPATH + "/State";
FLAGS_PATH = BLPATH + "/Flags";
SOC_LIMIT_PATH = BLPATH + "/SocLimit";
MIN_SOC_LIMIT_PATH = BLPATH + "/MinimumSocLimit";
DISCHARGED_TIME_PATH = BLPATH + "/DischargedTime";
DISCHARGED_SOC_PATH = BLPATH + "/DischargedSoc"

class State(object):
	BLDisabled = 0
	BLRestart = 1
	BLDefault = 2
	BLAbsorption = 3
	BLFloat = 4
	BLDischarged = 5
	BLForceCharge = 6
	BLSustain = 7
	# Not used any more, but we keep it, because the /BatteryLife/State setting
	# this value.
	BLLowSocCharge = 8
	KeepCharged = 9
	SocGuardDefault = 10
	SocGuardDischarged = 11
	# Not used any more, but we keep it, because the /BatteryLife/State setting
	# this value.
	SocGuardLowSocCharge = 12

class Flags(object):
	Float = 0x01
	Absorption = 0x02
	Discharged = 0x04

class Constants(object):
	SocSwitchOffset = 3.0
	SocSwitchIncrement = 5.0
	SocSwitchDefaultMin = 10.0
	LowSocChargeOffset = 2.0 # Should be < SocSwitchOffset
	AbsorptionLevel = 85.0
	FloatLevel = 95.0
	SocSwitchMax = AbsorptionLevel - SocSwitchIncrement
	ForceChargeCurrent = 5.0
	ForceChargeInterval = 24 * 60 * 60 # 5 days

def bound(low, v, high):
	return max(low, min(v, high))

def dt_to_stamp(dt):
	""" Return UTC timestamp for datetime dt. """
	return (dt - datetime(1970, 1, 1)).total_seconds()

class BatteryLife(SystemCalcDelegate):
	""" Calculates the ESS CGwacs state. """

	# Items we want to track from systemcalc
	_tracked_attrs = {'soc': '/Dc/Battery/Soc', 'vebus': '/VebusService'}

	def __init__(self):
		super(BatteryLife, self).__init__()
		self._tracked_values = {}
		self._timer = gobject.timeout_add(900000, exit_on_error, self._on_timer)

	def get_input(self):
		# We need to check the assistantid to know if we should even be active.
		# We also need to check the sustain flag.
		return [
			('com.victronenergy.vebus', [
				'/Hub4/AssistantId',
				'/Hub4/Sustain']),
			('com.victronenergy.settings', [
				STATE_PATH, FLAGS_PATH, DISCHARGED_TIME_PATH,
				DISCHARGED_SOC_PATH, SOC_LIMIT_PATH, MIN_SOC_LIMIT_PATH])
		]

	def get_output(self):
		return []

	def get_settings(self):
		return [
			('state', STATE_PATH, 1, 0, 0),
			('flags', FLAGS_PATH, 0, 0, 0, 1),
			('dischargedtime', DISCHARGED_TIME_PATH, 0, 0, 0, 1),
			('dischargedsoc', DISCHARGED_SOC_PATH, -1, -1, 100, 1),
			('soclimit', SOC_LIMIT_PATH, 10.0, 0, 100, 1),
			('minsoclimit', MIN_SOC_LIMIT_PATH, 10.0, 0, 100),
		]

	_get_time = datetime.now

	@property
	def state(self):
		return self._settings['state']

	@state.setter
	def state(self, v):
		self._settings['state'] = v

	@property
	def flags(self):
		return self._settings['flags']

	@flags.setter
	def flags(self, v):
		self._settings['flags'] = v

	def _disabled(self):
		if self._dbusmonitor.get_value(self.vebus, '/Hub4/AssistantId') is not None:
			return State.BLRestart

	def _restart(self):
		# Do the same as in the default case
		return self._default(False)

	def _default(self, adjust=True):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged(adjust)
		elif self.soc >= Constants.FloatLevel:
			return self.on_float(adjust)
		elif self.soc >= Constants.AbsorptionLevel:
			return self.on_absorption(adjust)

		# Remain in default state
		return State.BLDefault

	def _discharged(self):
		if not self.sustain and (self.soc > self.switch_on_soc or self.soc >= 100):
			return State.BLDefault

	def _forcecharge(self):
		if not self.sustain and (self.soc > self.active_soclimit or self.soc >= 100):
			self.dischargedtime = dt_to_stamp(self._get_time())
			return State.BLDischarged

	def _absorption(self):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged(True)
		elif self.soc > Constants.FloatLevel:
			return self.on_float(True)
		elif self.soc < Constants.FloatLevel - Constants.SocSwitchOffset:
			return State.BLDefault

	def _float(self):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged()
		elif self.soc < Constants.FloatLevel - Constants.SocSwitchOffset:
			return State.BLAbsorption

	def _socguard_default(self):
		if self.soc < 100 and self.soc <= self.minsoclimit:
			return State.SocGuardDischarged

	def _socguard_discharged(self):
		if self.soc >= 100 or (self.soc > self.minsoclimit + Constants.LowSocChargeOffset):
			return State.SocGuardDefault

	def adjust_soc_limit(self, delta):
		limit = max(self._settings['minsoclimit'],
			self._settings['soclimit']) + delta
		self._settings['soclimit'] = bound(0.0, limit, Constants.SocSwitchMax)

	def on_discharged(self, adjust):
		# set dischargedsoc to the active limit just before going to the
		# discharged state. If the soc drops further, we will recharge back
		# to this level.
		limit = self.active_soclimit
		if self.dischargedsoc < 0 or self.dischargedsoc > limit:
			self.dischargedsoc = limit

		if adjust:
			if not self.flags & Flags.Discharged:
				self.flags |= Flags.Discharged
				self.adjust_soc_limit(Constants.SocSwitchIncrement)
			self.dischargedtime = dt_to_stamp(self._get_time())
		return State.BLSustain if self.sustain else State.BLDischarged

	def on_absorption(self, adjust):
		if adjust and not self.flags & Flags.Absorption:
			self.flags |= Flags.Absorption
			self.adjust_soc_limit(-Constants.SocSwitchIncrement)
		return State.BLAbsorption

	def on_float(self, adjust):
		offset = 0
		flags = self.flags
		if adjust:
			if not (flags & Flags.Absorption):
				offset -= Constants.SocSwitchIncrement
				flags |= Flags.Absorption
			if not (flags & Flags.Float):
				offset -= Constants.SocSwitchIncrement
				flags |= Flags.Float
			self.flags = flags
			self.adjust_soc_limit(offset)
			return State.BLFloat

	_map = {
		State.BLDisabled: _disabled,
		State.BLRestart: _restart,
		State.BLDefault: _default,
		State.BLAbsorption: _absorption,
		State.BLFloat: _float,
		State.BLDischarged: _discharged,
		State.BLForceCharge: _forcecharge,
		State.BLSustain: _discharged,
		State.BLLowSocCharge: lambda s: State.BLDischarged,
		State.KeepCharged: lambda s: s.state,
		State.SocGuardDefault: _socguard_default,
		State.SocGuardDischarged: _socguard_discharged,
		State.SocGuardLowSocCharge: lambda s: State.SocGuardDischarged,
	}

	@property
	def sustain(self):
		return self._dbusmonitor.get_value(self.vebus, '/Hub4/Sustain')

	@property
	def soclimit(self):
		return self._settings['soclimit']

	@property
	def minsoclimit(self):
		return self._settings['minsoclimit']

	@property
	def active_soclimit(self):
		m = self._settings['minsoclimit']
		l = self._settings['soclimit']
		if m > Constants.SocSwitchMax:
			return m
		return bound(0, max(m, l), Constants.SocSwitchMax)

	@property
	def switch_on_soc(self):
		m = self._settings['minsoclimit']
		if m > Constants.SocSwitchMax:
			return m + Constants.LowSocChargeOffset
		return self.active_soclimit + Constants.SocSwitchOffset

	@property
	def dischargedtime(self):
		return self._settings['dischargedtime']

	@dischargedtime.setter
	def dischargedtime(self, v):
		self._settings['dischargedtime'] = int(v)

	@property
	def dischargedsoc(self):
		return self._settings['dischargedsoc']

	@dischargedsoc.setter
	def dischargedsoc(self, v):
		self._settings['dischargedsoc'] = v

	def __getattr__(self, k):
		""" Make our tracked values available as attributes, makes the
			code look neater. """
		try:
			return self._tracked_values[k]
		except KeyError:
			raise AttributeError(k)

	def update_values(self, newvalues):
		# Update tracked attributes
		for k, v in self._tracked_attrs.iteritems():
			self._tracked_values[k] = newvalues.get(v)

		# Cannot start without a multi or an soc
		if self.vebus is None or self.soc is None:
			logging.debug("[BatteryLife] No vebus or no valid SoC")
			return

		# Cannot start without ESS available
		if self._dbusmonitor.get_value(self.vebus, '/Hub4/AssistantId') is None:
			logging.debug("[BatteryLife] No ESS Assistant found")
			self.state = State.BLDisabled
			return

		newstate = self._map.get(self.state, lambda s: State.BLDefault)(self)
		if newstate is not None:
			self.state = newstate

	def _on_timer(self):
		now = self._get_time()

		# Test for the first 15-minute window of the day, and clear the flags
		if now.hour == 0 and now.minute < 15:
			self.flags = 0

		if self.state in (State.BLDischarged, State.BLSustain):
			# load dischargedtime, it's a unix timestamp, ie UTC
			if self.dischargedtime:
				dt = datetime.fromtimestamp(self.dischargedtime)
				if now - dt > timedelta(seconds=Constants.ForceChargeInterval):
					self.adjust_soc_limit(Constants.SocSwitchIncrement)
					self.state = State.BLForceCharge
			else:
				self.dischargedtime = dt_to_stamp(now)

		return True
<commit_msg>Typo bugfix where using wrong constant.<commit_after>import logging
import gobject
from datetime import datetime, timedelta

# Victron packages
from ve_utils import exit_on_error
from delegates.base import SystemCalcDelegate

# Path constants
BLPATH = "/Settings/CGwacs/BatteryLife";
STATE_PATH = BLPATH + "/State";
FLAGS_PATH = BLPATH + "/Flags";
SOC_LIMIT_PATH = BLPATH + "/SocLimit";
MIN_SOC_LIMIT_PATH = BLPATH + "/MinimumSocLimit";
DISCHARGED_TIME_PATH = BLPATH + "/DischargedTime";
DISCHARGED_SOC_PATH = BLPATH + "/DischargedSoc"

class State(object):
	BLDisabled = 0
	BLRestart = 1
	BLDefault = 2
	BLAbsorption = 3
	BLFloat = 4
	BLDischarged = 5
	BLForceCharge = 6
	BLSustain = 7
	# Not used any more, but we keep it, because the /BatteryLife/State setting
	# this value.
	BLLowSocCharge = 8
	KeepCharged = 9
	SocGuardDefault = 10
	SocGuardDischarged = 11
	# Not used any more, but we keep it, because the /BatteryLife/State setting
	# this value.
	SocGuardLowSocCharge = 12

class Flags(object):
	Float = 0x01
	Absorption = 0x02
	Discharged = 0x04

class Constants(object):
	SocSwitchOffset = 3.0
	SocSwitchIncrement = 5.0
	SocSwitchDefaultMin = 10.0
	LowSocChargeOffset = 2.0 # Should be < SocSwitchOffset
	AbsorptionLevel = 85.0
	FloatLevel = 95.0
	SocSwitchMax = AbsorptionLevel - SocSwitchIncrement
	ForceChargeCurrent = 5.0
	ForceChargeInterval = 24 * 60 * 60 # 5 days

def bound(low, v, high):
	return max(low, min(v, high))

def dt_to_stamp(dt):
	""" Return UTC timestamp for datetime dt. """
	return (dt - datetime(1970, 1, 1)).total_seconds()

class BatteryLife(SystemCalcDelegate):
	""" Calculates the ESS CGwacs state. """

	# Items we want to track from systemcalc
	_tracked_attrs = {'soc': '/Dc/Battery/Soc', 'vebus': '/VebusService'}

	def __init__(self):
		super(BatteryLife, self).__init__()
		self._tracked_values = {}
		self._timer = gobject.timeout_add(900000, exit_on_error, self._on_timer)

	def get_input(self):
		# We need to check the assistantid to know if we should even be active.
		# We also need to check the sustain flag.
		return [
			('com.victronenergy.vebus', [
				'/Hub4/AssistantId',
				'/Hub4/Sustain']),
			('com.victronenergy.settings', [
				STATE_PATH, FLAGS_PATH, DISCHARGED_TIME_PATH,
				DISCHARGED_SOC_PATH, SOC_LIMIT_PATH, MIN_SOC_LIMIT_PATH])
		]

	def get_output(self):
		return []

	def get_settings(self):
		return [
			('state', STATE_PATH, 1, 0, 0),
			('flags', FLAGS_PATH, 0, 0, 0, 1),
			('dischargedtime', DISCHARGED_TIME_PATH, 0, 0, 0, 1),
			('dischargedsoc', DISCHARGED_SOC_PATH, -1, -1, 100, 1),
			('soclimit', SOC_LIMIT_PATH, 10.0, 0, 100, 1),
			('minsoclimit', MIN_SOC_LIMIT_PATH, 10.0, 0, 100),
		]

	_get_time = datetime.now

	@property
	def state(self):
		return self._settings['state']

	@state.setter
	def state(self, v):
		self._settings['state'] = v

	@property
	def flags(self):
		return self._settings['flags']

	@flags.setter
	def flags(self, v):
		self._settings['flags'] = v

	def _disabled(self):
		if self._dbusmonitor.get_value(self.vebus, '/Hub4/AssistantId') is not None:
			return State.BLRestart

	def _restart(self):
		# Do the same as in the default case
		return self._default(False)

	def _default(self, adjust=True):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged(adjust)
		elif self.soc >= Constants.FloatLevel:
			return self.on_float(adjust)
		elif self.soc >= Constants.AbsorptionLevel:
			return self.on_absorption(adjust)

		# Remain in default state
		return State.BLDefault

	def _discharged(self):
		if not self.sustain and (self.soc > self.switch_on_soc or self.soc >= 100):
			return State.BLDefault

	def _forcecharge(self):
		if not self.sustain and (self.soc > self.active_soclimit or self.soc >= 100):
			self.dischargedtime = dt_to_stamp(self._get_time())
			return State.BLDischarged

	def _absorption(self):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged(True)
		elif self.soc > Constants.FloatLevel:
			return self.on_float(True)
		elif self.soc < Constants.AbsorptionLevel - Constants.SocSwitchOffset:
			return State.BLDefault

	def _float(self):
		if self.sustain or (self.soc <= self.active_soclimit and self.soc < 100):
			return self.on_discharged(True)
		elif self.soc < Constants.FloatLevel - Constants.SocSwitchOffset:
			return State.BLAbsorption

	def _socguard_default(self):
		if self.soc < 100 and self.soc <= self.minsoclimit:
			return State.SocGuardDischarged

	def _socguard_discharged(self):
		if self.soc >= 100 or (self.soc > self.minsoclimit + Constants.LowSocChargeOffset):
			return State.SocGuardDefault

	def adjust_soc_limit(self, delta):
		limit = max(self._settings['minsoclimit'],
			self._settings['soclimit']) + delta
		self._settings['soclimit'] = bound(0.0, limit, Constants.SocSwitchMax)

	def on_discharged(self, adjust):
		# set dischargedsoc to the active limit just before going to the
		# discharged state. If the soc drops further, we will recharge back
		# to this level.
		limit = self.active_soclimit
		if self.dischargedsoc < 0 or self.dischargedsoc > limit:
			self.dischargedsoc = limit

		if adjust:
			if not self.flags & Flags.Discharged:
				self.flags |= Flags.Discharged
				self.adjust_soc_limit(Constants.SocSwitchIncrement)
			self.dischargedtime = dt_to_stamp(self._get_time())
		return State.BLSustain if self.sustain else State.BLDischarged

	def on_absorption(self, adjust):
		if adjust and not self.flags & Flags.Absorption:
			self.flags |= Flags.Absorption
			self.adjust_soc_limit(-Constants.SocSwitchIncrement)
		return State.BLAbsorption

	def on_float(self, adjust):
		offset = 0
		flags = self.flags
		if adjust:
			if not (flags & Flags.Absorption):
				offset -= Constants.SocSwitchIncrement
				flags |= Flags.Absorption
			if not (flags & Flags.Float):
				offset -= Constants.SocSwitchIncrement
				flags |= Flags.Float
			self.flags = flags
			self.adjust_soc_limit(offset)
			return State.BLFloat

	_map = {
		State.BLDisabled: _disabled,
		State.BLRestart: _restart,
		State.BLDefault: _default,
		State.BLAbsorption: _absorption,
		State.BLFloat: _float,
		State.BLDischarged: _discharged,
		State.BLForceCharge: _forcecharge,
		State.BLSustain: _discharged,
		State.BLLowSocCharge: lambda s: State.BLDischarged,
		State.KeepCharged: lambda s: s.state,
		State.SocGuardDefault: _socguard_default,
		State.SocGuardDischarged: _socguard_discharged,
		State.SocGuardLowSocCharge: lambda s: State.SocGuardDischarged,
	}

	@property
	def sustain(self):
		return self._dbusmonitor.get_value(self.vebus, '/Hub4/Sustain')

	@property
	def soclimit(self):
		return self._settings['soclimit']

	@property
	def minsoclimit(self):
		return self._settings['minsoclimit']

	@property
	def active_soclimit(self):
		m = self._settings['minsoclimit']
		l = self._settings['soclimit']
		if m > Constants.SocSwitchMax:
			return m
		return bound(0, max(m, l), Constants.SocSwitchMax)

	@property
	def switch_on_soc(self):
		m = self._settings['minsoclimit']
		if m > Constants.SocSwitchMax:
			return m + Constants.LowSocChargeOffset
		return self.active_soclimit + Constants.SocSwitchOffset

	@property
	def dischargedtime(self):
		return self._settings['dischargedtime']

	@dischargedtime.setter
	def dischargedtime(self, v):
		self._settings['dischargedtime'] = int(v)

	@property
	def dischargedsoc(self):
		return self._settings['dischargedsoc']

	@dischargedsoc.setter
	def dischargedsoc(self, v):
		self._settings['dischargedsoc'] = v

	def __getattr__(self, k):
		""" Make our tracked values available as attributes, makes the
			code look neater. """
		try:
			return self._tracked_values[k]
		except KeyError:
			raise AttributeError(k)

	def update_values(self, newvalues):
		# Update tracked attributes
		for k, v in self._tracked_attrs.iteritems():
			self._tracked_values[k] = newvalues.get(v)

		# Cannot start without a multi or an soc
		if self.vebus is None or self.soc is None:
			logging.debug("[BatteryLife] No vebus or no valid SoC")
			return

		# Cannot start without ESS available
		if self._dbusmonitor.get_value(self.vebus, '/Hub4/AssistantId') is None:
			logging.debug("[BatteryLife] No ESS Assistant found")
			self.state = State.BLDisabled
			return

		newstate = self._map.get(self.state, lambda s: State.BLDefault)(self)
		if newstate is not None:
			self.state = newstate

	def _on_timer(self):
		now = self._get_time()

		# Test for the first 15-minute window of the day, and clear the flags
		if now.hour == 0 and now.minute < 15:
			self.flags = 0

		if self.state in (State.BLDischarged, State.BLSustain):
			# load dischargedtime, it's a unix timestamp, ie UTC
			if self.dischargedtime:
				dt = datetime.fromtimestamp(self.dischargedtime)
				if now - dt > timedelta(seconds=Constants.ForceChargeInterval):
					self.adjust_soc_limit(Constants.SocSwitchIncrement)
					self.state = State.BLForceCharge
			else:
				self.dischargedtime = dt_to_stamp(now)

		return True
<|endoftext|>


<commit_before># The MIT License (MIT)

# Copyright (c) 2014-2015 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import ConfigParser
from camomile import Camomile

if __name__ == '__main__':
    # read config
    Config = ConfigParser.ConfigParser()
    Config.read("config.ini")

    server = Config.get('mainSection', 'url')+":"+Config.get('mainSection', 'port')
    userAdmin = Config.get('userSection', 'userAdmin')
    pwdAdmin = Config.get('userSection', 'pwdAdmin')

    client = Camomile(server)
    client.login(userAdmin, pwdAdmin)

    for l in client.getLayers():
        annotations = client.getAnnotations(l._id)
        print('annotations : %d' % len(annotations))

        tmp = [tuple(a['fragment'].items()) + tuple(u'_id' + a['_id']) + tuple(u'data' + a['data']) for a in annotations]

        # for i in range(0, len(annotations)):
        #     print tuple(annotations[i]['fragment'].items())
        #     if annotations[i][fragments] not in fragments:
        #         fragments.append(tuple(annotations[i][fragments].items()))

        print('tmp : %d' % len(tmp))
        print tmp[0]

        # cleaned = [dict(t) for t in set([tuple(d.items()) for d in annotations])]

        # duplicates = []

        # for i in range(0, len(annotations)-1):
        #     start_i = annotations[i]['fragment']['start']
        #     end_i   = annotations[i]['fragment']['end']
        #     for j in range(i+1, len(annotations)):
        #         start_j = annotations[j]['fragment']['start']
        #         end_j   = annotations[j]['fragment']['end']
        #         if start_i == start_j and end_i == end_j:
        #             duplicates.append(annotations[i])

        # print('duplicates : %d' % len(duplicates))

        # cleaned = [dict(t) for t in set([tuple(d.items()) for d in annotations])]

        # seen = set()
        # new_l = []
        # for d in annotations:
        #     t = tuple(d.items())
        #     if t not in seen:
        #         seen.add(t)
        #         new_l.append(d)

        # print len(new_l)

<commit_msg>debug: tests<commit_after># The MIT License (MIT)

# Copyright (c) 2014-2015 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import ConfigParser
from camomile import Camomile

if __name__ == '__main__':
    # read config
    Config = ConfigParser.ConfigParser()
    Config.read("config.ini")

    server = Config.get('mainSection', 'url')+":"+Config.get('mainSection', 'port')
    userAdmin = Config.get('userSection', 'userAdmin')
    pwdAdmin = Config.get('userSection', 'pwdAdmin')

    client = Camomile(server)
    client.login(userAdmin, pwdAdmin)

    for l in client.getLayers():
        annotations = client.getAnnotations(l._id)
        print('annotations : %d' % len(annotations))

        tmp = [tuple(a['fragment'].items()) + tuple(tuple(u'_id',) + tuple(a['_id'],)) + tuple(tuple(u'data',) + tuple(a['data'],)) for a in annotations]

        # for i in range(0, len(annotations)):
        #     print tuple(annotations[i]['fragment'].items())
        #     if annotations[i][fragments] not in fragments:
        #         fragments.append(tuple(annotations[i][fragments].items()))

        print('tmp : %d' % len(tmp))
        print tmp[0]

        # cleaned = [dict(t) for t in set([tuple(d.items()) for d in annotations])]

        # duplicates = []

        # for i in range(0, len(annotations)-1):
        #     start_i = annotations[i]['fragment']['start']
        #     end_i   = annotations[i]['fragment']['end']
        #     for j in range(i+1, len(annotations)):
        #         start_j = annotations[j]['fragment']['start']
        #         end_j   = annotations[j]['fragment']['end']
        #         if start_i == start_j and end_i == end_j:
        #             duplicates.append(annotations[i])

        # print('duplicates : %d' % len(duplicates))

        # cleaned = [dict(t) for t in set([tuple(d.items()) for d in annotations])]

        # seen = set()
        # new_l = []
        # for d in annotations:
        #     t = tuple(d.items())
        #     if t not in seen:
        #         seen.add(t)
        #         new_l.append(d)

        # print len(new_l)

<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
# Module iagdist

from numpy import *
from iasecross import iasecross

def iagdist(f, g, Bc=iasecross(), METRIC=None):
    from ianeg import ianeg
    from iagray import iagray
    from iaintersec import iaintersec
    from iaisequal import iaisequal
    from iacero import iacero
    from iaaddm import iaaddm
    from iaunion import iaunion

    assert METRIC is None,'Does not support EUCLIDEAN'
    fneg,gneg = ianeg(f),ianeg(g)
    y = iagray(gneg,'uint16',1)
    ero = iaintersec(y,0)
    aux = y
    i = 1
    while (ero != aux).any():
        aux = ero
        ero = iacero(gneg,fneg,Bc,i)
        y   = iaaddm(y,iagray(ero,'uint16',1))
        i   = i + 1
    y = iaunion(y,iagray(ero,'uint16'))

    return y

<commit_msg>Delete iagdist.py<commit_after><|endoftext|>


<commit_before><commit_msg>Added __init__ file, to export TimeTravelLoop.<commit_after>from time_travel_util import TimeTravelLoop

# Export the TimeTravelLoop class:
__all__ = (TimeTravelLoop)

from .version import version as __version__
<|endoftext|>


<commit_before>from pygments import lexers, token, util
import networkx
import os
import subprocess

supported_languages = []

def init(cursor):
    global supported_languages

    ctags_process = subprocess.Popen(
        ['ctags', '--list-languages'],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

    lines, _ = [x.decode('utf-8') for x in ctags_process.communicate()]
    for line in lines.split('\n'):
        supported_languages.append(line.lower())

def run(project_id, repo_path, cursor, **options):
    language_sizes = get_loc(repo_path)
    size = sum([value['sloc'] for value in language_sizes.values()])

    # Immediately fail the attribute if `minimumSize` is not met.
    if size < options.get('minimumSize', 1000):
        return False, 0

    cursor.execute('''
        SELECT
            language
        FROM
            projects
        WHERE
            id = {0}
        '''.format(project_id))

    record = cursor.fetchone()
    language = record[0]

    language = language.lower() if language else language

    # Edge case if the repository language is not supported by us.
    if language not in supported_languages:
        return False, 0

    ctags_process = subprocess.Popen(
        ['ctags', '-Rx', "--languages={0}".format(language), repo_path],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

    # TODO: Handle errors emitted by `ctags`.
    lines, _ = [
        x.decode(errors='replace') for x in ctags_process.communicate()
    ]

    file_names = set()
    for line in lines.split('\n'):
        fields = line.split()
        for field in fields:
            if os.path.isfile(field):
                file_names.add(field)

    graph = networkx.Graph()
    lexer = lexers.get_lexer_by_name(language)
    build_graph(file_names, graph, lexer)

    result = get_connectedness(graph)
    return result > options.get('connectedness', 0.75), result

def build_graph(file_names, graph, lexer):
    """
    for each file in the set of files
        create a node and add it to the graph
        open the file
        read the contents into memory
        get a list of tokens from the lexer
        for each token in the resulting tokens
            check if the token is defining a symbol
            if true, add the symbol to the file node

    for each file in the set of files
        open the file
        read the contents into memory
        get a list of token from the lexer
        for each token in the resulting tokens
            check if the token is using a symbol
            if true:
                search the graph for the node that has the symbol definition
                create a relationship from the current file to the node with
                the symbol definition
    """
    for file_name in file_names:
        node = Node(file_name)
        graph.add_node(node)
        try:
            with open(file_name, 'r', encoding='utf-8') as file:
                contents = file.read()

            tokens = lexer.get_tokens(contents)
            for item in tokens:
                token_type = item[0]
                symbol = item[1]
                if token_type in [token.Name.Function, token.Name.Class]:
                    node.defines.add(symbol)
                else:
                    node.references.add(symbol)
            if 'DEBUG' in os.environ:
                print(node)
        except FileNotFoundError as e:
            continue
        except UnicodeDecodeError:
            continue

    for caller in graph.nodes_iter():
        for reference in caller.references:
            for callee in graph.nodes_iter():
                if callee is not caller and reference in callee.defines:
                    graph.add_edge(caller, callee, symbol=reference)

def get_connectedness(graph):
    node_degrees = graph.degree()
    zero_degrees = len(
        [degree for key, degree in node_degrees.items() if degree is 0]
    )

    if len(node_degrees) > 0:
        return 1 - (zero_degrees / len(node_degrees))
    else:
        return 0

class Node():
    def __init__(self, path):
        self.path = path
        self.defines = set()
        self.references = set()

    def __hash__(self):
        return hash(self.path)

    def __eq__(self, other):
        return self.path == other.path

    def __str__(self):
        symbol_str = '\r' + '\n'.join(self.defines)
        return "{0}\n{1}\n{2}".format(
            self.path, '=' * len(self.path), symbol_str
        )

if __name__ == '__main__':
    import importlib
    import json
    import mysql.connector
    import sys
    sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
    from lib.utilities import get_loc

    os.environ['DEBUG'] = '1'

    with open('../../config.json', 'r') as file:
        config = json.load(file)

    mysql_config = config['options']['datasource']

    connection = mysql.connector.connect(**mysql_config)
    connection.connect()

    cursor = connection.cursor()
    init(None)
    result = run(sys.argv[1], sys.argv[2], cursor, threshold=0.75)
    cursor.close()

    connection.close()

    print(result)
else:
    from lib.utilities import get_loc
<commit_msg>Modified the method of computing connectedness<commit_after>from pygments import lexers, token, util
import networkx
import os
import subprocess

supported_languages = []

def init(cursor):
    global supported_languages

    ctags_process = subprocess.Popen(
        ['ctags', '--list-languages'],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

    lines, _ = [x.decode('utf-8') for x in ctags_process.communicate()]
    for line in lines.split('\n'):
        supported_languages.append(line.lower())

def run(project_id, repo_path, cursor, **options):
    language_sizes = get_loc(repo_path)
    size = sum([value['sloc'] for value in language_sizes.values()])

    # Immediately fail the attribute if `minimumSize` is not met.
    if size < options.get('minimumSize', 1000):
        return False, 0

    cursor.execute('''
        SELECT
            language
        FROM
            projects
        WHERE
            id = {0}
        '''.format(project_id))

    record = cursor.fetchone()
    language = record[0]

    language = language.lower() if language else language

    # Edge case if the repository language is not supported by us.
    if language not in supported_languages:
        return False, 0

    ctags_process = subprocess.Popen(
        ['ctags', '-Rx', "--languages={0}".format(language), repo_path],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

    # TODO: Handle errors emitted by `ctags`.
    lines, _ = [
        x.decode(errors='replace') for x in ctags_process.communicate()
    ]

    file_names = set()
    for line in lines.split('\n'):
        fields = line.split()
        for field in fields:
            if os.path.isfile(field):
                file_names.add(field)

    graph = networkx.Graph()
    lexer = lexers.get_lexer_by_name(language)
    build_graph(file_names, graph, lexer)

    result = get_connectedness(graph)
    return result > options.get('connectedness', 0.75), result

def build_graph(file_names, graph, lexer):
    """
    for each file in the set of files
        create a node and add it to the graph
        open the file
        read the contents into memory
        get a list of tokens from the lexer
        for each token in the resulting tokens
            check if the token is defining a symbol
            if true, add the symbol to the file node

    for each file in the set of files
        open the file
        read the contents into memory
        get a list of token from the lexer
        for each token in the resulting tokens
            check if the token is using a symbol
            if true:
                search the graph for the node that has the symbol definition
                create a relationship from the current file to the node with
                the symbol definition
    """
    for file_name in file_names:
        node = Node(file_name)
        graph.add_node(node)
        try:
            with open(file_name, 'r', encoding='utf-8') as file:
                contents = file.read()

            tokens = lexer.get_tokens(contents)
            for item in tokens:
                token_type = item[0]
                symbol = item[1]
                if token_type in [token.Name.Function, token.Name.Class]:
                    node.defines.add(symbol)
                else:
                    node.references.add(symbol)
            if 'DEBUG' in os.environ:
                print(node)
        except FileNotFoundError as e:
            continue
        except UnicodeDecodeError:
            continue

    for caller in graph.nodes_iter():
        for reference in caller.references:
            for callee in graph.nodes_iter():
                if callee is not caller and reference in callee.defines:
                    graph.add_edge(caller, callee, symbol=reference)

def get_connectedness(graph):
    components = list(networkx.connected_component_subgraphs(graph))
    components.sort(key=lambda i: len(i.nodes()), reverse=True)
    largest_component = components[0]

    connectedness = 0
    if graph.nodes() and len(graph.nodes()) > 0:
        connectedness = len(largest_component.nodes()) / len(graph.nodes())

    return connectedness

class Node():
    def __init__(self, path):
        self.path = path
        self.defines = set()
        self.references = set()

    def __hash__(self):
        return hash(self.path)

    def __eq__(self, other):
        return self.path == other.path

    def __str__(self):
        symbol_str = '\r' + '\n'.join(self.defines)
        return "{0}\n{1}\n{2}".format(
            self.path, '=' * len(self.path), symbol_str
        )

if __name__ == '__main__':
    import importlib
    import json
    import mysql.connector
    import sys
    sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
    from lib.utilities import get_loc

    os.environ['DEBUG'] = '1'

    with open('../../config.json', 'r') as file:
        config = json.load(file)

    mysql_config = config['options']['datasource']

    connection = mysql.connector.connect(**mysql_config)
    connection.connect()

    cursor = connection.cursor()
    init(None)
    result = run(sys.argv[1], sys.argv[2], cursor, threshold=0.75)
    cursor.close()

    connection.close()

    print(result)
else:
    from lib.utilities import get_loc
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# © 2015 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.tests.common import TransactionCase

class TestAuditlog(object):

    def test_LogCreation(self):
        """First test, caching some data."""
        auditlog_log = self.env['auditlog.log']
        group = self.env['res.groups'].create({
            'name': 'testgroup1',
        })
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', group.id),
        ]).ensure_one())
        group.write({'name': 'Testgroup1'})
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'write'),
            ('res_id', '=', group.id),
        ]).ensure_one())
        group.unlink()
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'unlink'),
            ('res_id', '=', group.id),
        ]).ensure_one())

    def test_LogCreation2(self):
        """Second test, using cached data of the first one."""
        auditlog_log = self.env['auditlog.log']
        testgroup2 = self.env['res.groups'].create({
            'name': 'testgroup2',
        })
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup2.id),
        ]).ensure_one())

    def test_LogCreation3(self):
        """Third test, two groups, the latter being the parent of the former.
        Then we remove it right after (with (2, X) tuple) to test the creation
        of a 'write' log with a deleted resource (so with no text
        representation).
        """
        auditlog_log = self.env['auditlog.log']
        testgroup3 = testgroup3 = self.env['res.groups'].create({
            'name': 'testgroup3',
        })
        testgroup4 = self.env['res.groups'].create({
            'name': 'testgroup4',
            'implied_ids': [(4, testgroup3.id)],
        })
        testgroup4.write({'implied_ids': [(2, testgroup3.id)]})
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup3.id),
        ]).ensure_one())
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup4.id),
        ]).ensure_one())
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'write'),
            ('res_id', '=', testgroup4.id),
        ]).ensure_one())

class TestAuditlogFull(TransactionCase, TestAuditlog):

    def setUp(self):
        super(TestAuditlogFull, self).setUp()
        self.groups_model_id = self.env.ref('base.model_res_groups').id
        self.groups_rule = self.env['auditlog.rule'].create({
            'name': 'testrule for groups',
            'model_id': self.groups_model_id,
            'log_read': True,
            'log_create': True,
            'log_write': True,
            'log_unlink': True,
            'state': 'subscribed',
            'log_type': 'full',
        })

    def tearDown(self):
        self.groups_rule.unlink()
        super(TestAuditlogFull, self).tearDown()

class TestAuditlogFast(TransactionCase, TestAuditlog):

    def setUp(self):
        super(TestAuditlogFast, self).setUp()
        self.groups_model_id = self.env.ref('base.model_res_groups').id
        self.groups_rule = self.env['auditlog.rule'].create({
            'name': 'testrule for groups',
            'model_id': self.groups_model_id,
            'log_read': True,
            'log_create': True,
            'log_write': True,
            'log_unlink': True,
            'state': 'subscribed',
            'log_type': 'fast',
        })

    def tearDown(self):
        self.groups_rule.unlink()
        super(TestAuditlogFast, self).tearDown()

class TestMethods(TransactionCase):
    def setUp(self):
        super(TestMethods, self).setUp()

        # Clear all existing logging lines
        existing_audit_logs = self.env['auditlog.log'].search([])
        existing_audit_logs.unlink()

        # Create account period to test
        self.partner = self.env['res.partner'].create({
            'name': 'Test User'
        })

        self.partner_model = self.env['ir.model'].search([
            ('model', '=', 'res.partner')])

        self.auditlog_rule = self.env['auditlog.rule'].create({
            'name': 'res.partner',
            'model_id': self.partner_model.id,
            'log_type': 'fast',
            'log_read': False,
            'log_create': False,
            'log_write': False,
            'log_unlink': False,
            'log_custom': True,
            'custom_method_ids': [(0, 0, {
                'name': 'copy',
                'message': 'execute_copy',
            })]
        })

        self.auditlog_rule.subscribe()

    def tearDown(self):
        self.auditlog_rule.unsubscribe()
        super(TestMethods, self).tearDown()

    def test_01_subscribe_unsubscribe(self):
        """The test is subscribed by default, so let's try both"""
        self.auditlog_rule.unsubscribe()
        self.auditlog_rule.subscribe()

    def test_02_copy_res_partner_logging(self):
        self.partner.copy()

        logs = self.env['auditlog.log'].search([
            ('res_id', '=', self.partner.id),
            ('model_id', '=', self.partner_model.id),
            ('method', '=', 'execute_copy')
        ])

        self.assertEqual(len(logs), 1)
        <commit_msg>[RFR] Rewrite correct line terminations<commit_after># -*- coding: utf-8 -*-
# © 2015 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.tests.common import TransactionCase

class TestAuditlog(object):

    def test_LogCreation(self):
        """First test, caching some data."""
        auditlog_log = self.env['auditlog.log']
        group = self.env['res.groups'].create({
            'name': 'testgroup1',
        })
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', group.id),
        ]).ensure_one())
        group.write({'name': 'Testgroup1'})
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'write'),
            ('res_id', '=', group.id),
        ]).ensure_one())
        group.unlink()
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'unlink'),
            ('res_id', '=', group.id),
        ]).ensure_one())

    def test_LogCreation2(self):
        """Second test, using cached data of the first one."""
        auditlog_log = self.env['auditlog.log']
        testgroup2 = self.env['res.groups'].create({
            'name': 'testgroup2',
        })
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup2.id),
        ]).ensure_one())

    def test_LogCreation3(self):
        """Third test, two groups, the latter being the parent of the former.
        Then we remove it right after (with (2, X) tuple) to test the creation
        of a 'write' log with a deleted resource (so with no text
        representation).
        """
        auditlog_log = self.env['auditlog.log']
        testgroup3 = testgroup3 = self.env['res.groups'].create({
            'name': 'testgroup3',
        })
        testgroup4 = self.env['res.groups'].create({
            'name': 'testgroup4',
            'implied_ids': [(4, testgroup3.id)],
        })
        testgroup4.write({'implied_ids': [(2, testgroup3.id)]})
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup3.id),
        ]).ensure_one())
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'create'),
            ('res_id', '=', testgroup4.id),
        ]).ensure_one())
        self.assertTrue(auditlog_log.search([
            ('model_id', '=', self.groups_model_id),
            ('method', '=', 'write'),
            ('res_id', '=', testgroup4.id),
        ]).ensure_one())

class TestAuditlogFull(TransactionCase, TestAuditlog):

    def setUp(self):
        super(TestAuditlogFull, self).setUp()
        self.groups_model_id = self.env.ref('base.model_res_groups').id
        self.groups_rule = self.env['auditlog.rule'].create({
            'name': 'testrule for groups',
            'model_id': self.groups_model_id,
            'log_read': True,
            'log_create': True,
            'log_write': True,
            'log_unlink': True,
            'state': 'subscribed',
            'log_type': 'full',
        })

    def tearDown(self):
        self.groups_rule.unlink()
        super(TestAuditlogFull, self).tearDown()

class TestAuditlogFast(TransactionCase, TestAuditlog):

    def setUp(self):
        super(TestAuditlogFast, self).setUp()
        self.groups_model_id = self.env.ref('base.model_res_groups').id
        self.groups_rule = self.env['auditlog.rule'].create({
            'name': 'testrule for groups',
            'model_id': self.groups_model_id,
            'log_read': True,
            'log_create': True,
            'log_write': True,
            'log_unlink': True,
            'state': 'subscribed',
            'log_type': 'fast',
        })

    def tearDown(self):
        self.groups_rule.unlink()
        super(TestAuditlogFast, self).tearDown()

class TestMethods(TransactionCase):
    def setUp(self):
        super(TestMethods, self).setUp()

        # Clear all existing logging lines
        existing_audit_logs = self.env['auditlog.log'].search([])
        existing_audit_logs.unlink()

        # Create account period to test
        self.partner = self.env['res.partner'].create({
            'name': 'Test User'
        })

        self.partner_model = self.env['ir.model'].search([
            ('model', '=', 'res.partner')])

        self.auditlog_rule = self.env['auditlog.rule'].create({
            'name': 'res.partner',
            'model_id': self.partner_model.id,
            'log_type': 'fast',
            'log_read': False,
            'log_create': False,
            'log_write': False,
            'log_unlink': False,
            'log_custom': True,
            'custom_method_ids': [(0, 0, {
                'name': 'copy',
                'message': 'execute_copy',
            })]
        })

        self.auditlog_rule.subscribe()

    def tearDown(self):
        self.auditlog_rule.unsubscribe()
        super(TestMethods, self).tearDown()

    def test_01_subscribe_unsubscribe(self):
        """The test is subscribed by default, so let's try both"""
        self.auditlog_rule.unsubscribe()
        self.auditlog_rule.subscribe()

    def test_02_copy_res_partner_logging(self):
        self.partner.copy()

        logs = self.env['auditlog.log'].search([
            ('res_id', '=', self.partner.id),
            ('model_id', '=', self.partner_model.id),
            ('method', '=', 'execute_copy')
        ])

        self.assertEqual(len(logs), 1)
<|endoftext|>


<commit_before>"""
===========
NewTabmagic
===========

Magics for opening new browser tabs.

============
Sample Usage
============

    Start magic:

        In [1]: %load_ext newtabmagic

        In [2]: %newtab --browser firefox

        In [3]: %newtab --port 8889

        In [4]: %newtab --server start
        Starting job # 0 in a separate thread.
        Server running at http://127.0.0.1:8889/

    Open help pages in new browser tabs:

        In [5]: %newtab IPython.core.debugger.Tracer

        In [6]: import IPython
        In [7]: Tracer = IPython.core.debugger.Tracer()
        In [8]: %newtab Tracer

    Show state using optional 'reproducible' argument:

        In [11]: %newtab --show reproducible
        browser: firefox
        content-type: html
        server poll: None
        server running: True
        server port: 8889
        server root url: http://127.0.0.1:8889/

    Stop server:

        In [14]: %newtab --server stop
        Server process is terminated.

    Note: shutting down IPython stops the server.

"""
from __future__ import print_function

import inspect
import os
import pydoc
import sys
import subprocess
import time
import tornado.ioloop
import tornado.web

from IPython import get_ipython
from IPython.core.error import UsageError
from IPython.core.magic import (
    Magics,
    magics_class,
    line_magic)
from IPython.core.magic_arguments import (
    argument,
    magic_arguments,
    parse_argstring)

@magics_class
class NewTabMagics(Magics):
    """Magic class for opening new browser tabs."""

    def __init__(self, shell):
        super(NewTabMagics, self).__init__(shell)
        self._content_type = 'html'
        self._content_types = ['html', 'text']
        self._browser = None
        self._server = ServerProcess()
        self.new_tabs_enabled = True
        self._cmds = []

    @line_magic
    @magic_arguments()
    @argument(
        'names',
        help='Variable names and dotted paths, separated by spaces.',
        nargs='*'
    )
    @argument(
        '--browser',
        help='Set name of browser used to open new tabs.',
        nargs='+'
    )
    @argument(
        '--port',
        type=int,
        help='Set port used by the newtabmagic server.'
    )
    @argument(
        '--server',
        help='Control the newtabmagic server.',
        choices=['stop', 'start']
    )
    @argument(
        '--show',
        choices=['all', 'reproducible'],
        const='all',
        help='Show state for newtabmagic.',
        nargs='?'
    )
    @argument(
        '--content-type',
        choices=['text', 'html'],
        help='Set content type of files opened in new tabs.'
    )
    def newtab(self, line):
        """Line magic for opening new browser tabs."""

        args = parse_argstring(self.newtab, line)

        if args.port:
            self._server.port = args.port

        if args.server:
            if args.server == 'start':
                self._server.start()
            elif args.server == 'stop':
                self._server.stop()

        if args.browser:
            self.browser = args.browser

        if args.content_type:
            self._content_type = args.content_type

        if args.names:
            if self._browser:
                self._open_new_tabs(args.names)
            else:
                msg = "Browser not initialized\n"
                raise UsageError(msg)

        if args.show:
            self.show(args.show)

    def _open_new_tabs(self, names):
        """Open browser tabs for a list of variable names and paths."""
        self._cmds = []
        for name in names:
            url, msg = self.get_url(name)
            if not url:
                print(msg)
            else:
                cmd = [self._browser, url]
                self._cmds.append(cmd)
                self._open_new_tab(cmd)

    def get_url(self, name):
        """Get url associated with name, returning None if not found"""
        return self.get_pydoc_url(name)

    def get_pydoc_url(self, name):
        """Get pydoc url for name of variable or path."""
        root = self._server.url()
        msg = ''
        page = self.get_pydoc_page_name(name)
        if page:
            if self._content_type == 'html':
                url = root + page + '.html'
            else:
                url = root + page + '.txt'
        else:
            url = None
            msg = 'Documentation not found: {}'.format(name)
        return url, msg

    def get_pydoc_page_name(self, path):
        """Return name of pydoc page, or None if path is not valid."""
        obj = self.get_object(path)
        if obj:
            page_name = fully_qualified_name(obj)
        else:
            page_name = None
        return page_name

    def get_object(self, path):
        """Return object, or None if the object does not exist."""
        parts = [part for part in path.split('.') if part]
        if parts[0] in self.shell.user_ns:
            obj = self.shell.user_ns[parts[0]]
            if parts[1:]:
                obj = getattr_path(obj, parts[1:])
        else:
            obj = pydoc.locate(path)
        return obj

    @property
    def command_lines(self):
        """Command lines used (most recently) to invoke subproccesses."""
        return self._cmds

    @property
    def browser(self):
        """Name of browser used to open new tabs."""
        return self._browser

    @browser.setter
    def browser(self, browser_args):
        """Set browser name."""
        browser_name = ' '.join(browser_args)
        self._browser = browser_name.strip('"\'')

    def _open_new_tab(self, cmd):
        """Open a new browser tab by invoking a subprocess."""
        try:
            if self.new_tabs_enabled:
                # chromium browser writes to screen, so redirect
                subprocess.Popen(cmd, stdout=subprocess.PIPE)
        except OSError:
            msg = "Browser named {} failed to open new tab\n".format(
                self._browser)
            raise UsageError(msg)

    def show(self, arg):
        """Show state of magic."""

        reproducible = (arg == 'reproducible')

        if self._browser:
            msg = 'browser: {}\n'.format(self._browser)
        else:
            msg = 'browser: None\n'

        msg += 'content-type: {}\n'.format(self._content_type)

        print(msg, end='')

        self._server.show(reproducible)

    def server_running(self):
        """Interrogate server to see if it is still running."""
        return self._server.running()

    def base_url(self):
        """Base Url for newtabmagic server."""
        return self._server.url()

class ServerProcess(object):
    """Class for the newtabmagic server process."""

    def __init__(self):
        self._process = None
        self._port = 8888

    def start(self):
        """Start server if not previously started."""
        msg = ''
        if not self.running():
            # Using script cell magic so that shutting down
            # IPython stops the server process.

            module = 'newtabmagic'
            path = os.path.dirname(os.path.realpath(__file__))

            lines = ('import sys; sys.path += [{0}];'
                     'import {1}; {1}.start_server({2})')
            cell = lines.format(repr(path), module, self._port)

            line = "python --proc proc --bg"
            ip = get_ipython()
            ip.run_cell_magic("script", line, cell)
            self._process = ip.user_ns['proc']

        else:
            msg = 'Server already started\n'
        msg += 'Server running at {}'.format(self.url())
        print(msg)

    def stop(self):
        """Stop server process."""

        msg = ''
        if self._process:
            _stop_process(self._process, 'Server process')
        else:
            msg += 'Server not started.\n'
        if msg:
            print(msg, end='')

    def running(self):
        """If the server has been started, is it still running?"""
        return self._process is not None and self._process.poll() is None

    def show(self, reproducible=False):
        """Show state."""

        msg = ''
        if self._process:
            if not reproducible:
                msg += 'server pid: {}\n'.format(self._process.pid)
            msg += 'server poll: {}\n'.format(self._process.poll())
        msg += 'server running: {}\n'.format(self.running())
        msg += 'server port: {}\n'.format(self._port)
        msg += 'server root url: {}\n'.format(self.url())

        print(msg, end='')

    def url(self):
        """Base url. Includes protocol, host, and port number."""
        proto = 'http'
        ip = '127.0.0.1'
        return '{}://{}:{}/'.format(proto, ip, self._port)

    @property
    def port(self):
        """Port number server listens on."""
        return self._port

    @port.setter
    def port(self, port):
        """Set port number if server is not running."""
        if not self.running():
            self._port = port
        else:
            print('Server already running. Port number not changed')

def fully_qualified_name(obj):
    """Returns fully qualified name, including module name, except for the
    built-in module."""

    builtins = ['builtins', '__builtin__']

    if inspect.ismodule(obj):
        name = obj.__name__
    else:
        module_name = get_module_name(obj)
        if not module_name or module_name in builtins:
            name = qualname(obj, module_name)
        else:
            name = module_name + '.' + qualname(obj, module_name)
    return name

def qualname(obj, module_name):
    """Qualified name not including module name."""

    if sys.version_info >= (3, 3):
        return qualname33(obj)
    elif sys.version_info >= (3, 0):
        return qualname32(obj)
    else:
        return qualname27(obj, module_name)

def qualname33(obj):
    """Qualified name, not including module name, for Python 3.3+."""
    try:
        return obj.undecorated.__qualname__
    except AttributeError:
        try:
            return obj.__qualname__
        except AttributeError:
            return type(obj).__name__

def qualname32(obj):
    """Qualified name, not including module name, for Python 3.0-3.2."""
    try:
        return obj.__objclass__.__name__ + "." + obj.__name__
    except AttributeError:
        try:
            if inspect.ismodule(obj.__self__):
                return obj.__name__
            else:
                return obj.__self__.__class__.__name__ + "." + obj.__name__
        except AttributeError:
            try:
                return obj.__name__
            except AttributeError:
                return type(obj).__name__

def qualname27(obj, module_name):
    """Qualified name, not including module name, Python 2.7."""
    try:
        return obj.__objclass__.__name__ + "." + obj.__name__
    except AttributeError:
        try:
            attr = obj.__name__
            # loop through base classes looking for class where
            # attr is defined.
            for base_class in obj.im_class.mro():
                qual_name = base_class.__name__ + '.' + attr
                if pydoc.locate(module_name + '.' + qual_name):
                    return qual_name
        except AttributeError:  # no im_class
            try:
                if obj.__self__ is not None:
                    return obj.__self__.__class__.__name__ + "." + obj.__name__
            except AttributeError: # no __self__
                pass
            try:
                return obj.__name__
            except AttributeError:
                return type(obj).__name__

def getattr_path(obj, attrs):
    """Get a named attribute from an object, returning None if the
    attribute does not exist.

    Keyword Arguments:
    attrs -- a list of attributes obtained from a path by applying
                String.split('.')
    """

    try:
        for attr in attrs:
            obj = getattr(obj, attr)
    except AttributeError:
        obj = None
    return obj

def get_module_name(object_):
    """Return module name of object, or module name of object's class."""
    try:
        name = object_.__module__
    except AttributeError:
        name = object_.__class__.__module__
    return name

def _stop_process(p, name):
    """Stop process, by applying terminate and kill."""
    # Based on code in IPython.core.magics.script.ScriptMagics.shebang
    if p.poll() is not None:
        print("{} is already stopped.".format(name))
        return
    p.terminate()
    time.sleep(0.1)
    if p.poll() is not None:
        print("{} is terminated.".format(name))
        return
    p.kill()
    print("{} is killed.".format(name))

class HtmlHandler(tornado.web.RequestHandler):
    """Handler for html pages."""
    def get(self):
        page = get_html(self.request.uri)
        self.write(page)

class TextHandler(tornado.web.RequestHandler):
    """Handler for plain text pages."""
    def get(self):
        page = get_text(self.request.uri)
        self.set_header("Content-Type", "text/plain")
        self.write(page)

def get_html(url):
    """Get method for HTML pages."""
    if sys.version_info[0] >= 3:
        page = pydoc._url_handler(url)  # pylint: disable=W0212
    else:
        if url.endswith('.html'):
            page = pydoc_html_help(url[1:-5])
        elif url == '/':
            # index page is not available for Python 2
            page = "Index page not available"
    return page

def get_text(uri):
    """Get method for plain text pages."""
    path = uri[1:-4]  # remove leading '/' and trailing '.txt'
    page = pydoc_text_help(path)
    return page

def pydoc_html_help(path):
    """Returns a pydoc help page in html for the object referred
    to by path.

    Keyword Arguments:
    path -- an object name or a dotted path to an object

    Reference: pydoc.writedoc

    """
    try:
        obj, name = pydoc.resolve(path)
        html_doc = pydoc.html.document(obj, name)
        page = pydoc.html.page(pydoc.describe(obj), html_doc)
    except (ImportError, pydoc.ErrorDuringImport):
        raise tornado.web.HTTPError(404)
    return page

def pydoc_text_help(path):
    """Returns a pydoc help page in plain text for the object
    referred to by path.

    Keyword Arguments:
    path -- an object name or a dotted path to an object
    """
    try:
        if sys.version_info[0] == 2:
            page = pydoc.plain(pydoc.render_doc(path))
        else:
            page = pydoc.render_doc(path, renderer=pydoc.plaintext)
    except (ImportError, pydoc.ErrorDuringImport):
        raise tornado.web.HTTPError(404)
    return page.encode('utf-8')

def start_server(port):
    """Starts the newtab server on the given port."""
    application = tornado.web.Application([
        (r'/', HtmlHandler),
        (r'/[\w\.]*.html', HtmlHandler),
        (r'/[\w\.]*.txt', TextHandler),
    ])
    application.listen(port)

    tornado.ioloop.IOLoop.instance().start()

def load_ipython_extension(ip):
    """Load NewTabMagics extension."""
    ip.register_magics(NewTabMagics)
<commit_msg>Simplifying show methods<commit_after>"""
===========
NewTabmagic
===========

Magics for opening new browser tabs.

============
Sample Usage
============

    Start magic:

        In [1]: %load_ext newtabmagic

        In [2]: %newtab --browser firefox

        In [3]: %newtab --port 8889

        In [4]: %newtab --server start
        Starting job # 0 in a separate thread.
        Server running at http://127.0.0.1:8889/

    Open help pages in new browser tabs:

        In [5]: %newtab IPython.core.debugger.Tracer

        In [6]: import IPython
        In [7]: Tracer = IPython.core.debugger.Tracer()
        In [8]: %newtab Tracer

    Show state:

        In [9]: # random \
        ...: %newtab --show
        browser: firefox
        content-type: html
        server pid: 3096
        server poll: None
        server running: True
        server port: 8889
        server root url: http://127.0.0.1:8889/

    Stop server:

        In [10]: %newtab --server stop
        Server process is terminated.

    Note: shutting down IPython stops the server.

"""
from __future__ import print_function

import inspect
import os
import pydoc
import sys
import subprocess
import time
import tornado.ioloop
import tornado.web

from IPython import get_ipython
from IPython.core.error import UsageError
from IPython.core.magic import (
    Magics,
    magics_class,
    line_magic)
from IPython.core.magic_arguments import (
    argument,
    magic_arguments,
    parse_argstring)

@magics_class
class NewTabMagics(Magics):
    """Magic class for opening new browser tabs."""

    def __init__(self, shell):
        super(NewTabMagics, self).__init__(shell)
        self._content_type = 'html'
        self._content_types = ['html', 'text']
        self._browser = None
        self._server = ServerProcess()
        self.new_tabs_enabled = True
        self._cmds = []

    @line_magic
    @magic_arguments()
    @argument(
        'names',
        help='Variable names and dotted paths, separated by spaces.',
        nargs='*'
    )
    @argument(
        '--browser',
        help='Set name of browser used to open new tabs.',
        nargs='+'
    )
    @argument(
        '--port',
        type=int,
        help='Set port used by the newtabmagic server.'
    )
    @argument(
        '--server',
        help='Control the newtabmagic server.',
        choices=['stop', 'start']
    )
    @argument(
        '--show',
        help='Show state of magic.',
        action='store_true'
    )
    @argument(
        '--content-type',
        choices=['text', 'html'],
        help='Set content type of files opened in new tabs.'
    )
    def newtab(self, line):
        """Line magic for opening new browser tabs."""

        args = parse_argstring(self.newtab, line)

        if args.port:
            self._server.port = args.port

        if args.server:
            if args.server == 'start':
                self._server.start()
            elif args.server == 'stop':
                self._server.stop()

        if args.browser:
            self.browser = args.browser

        if args.content_type:
            self._content_type = args.content_type

        if args.names:
            if self._browser:
                self._open_new_tabs(args.names)
            else:
                msg = "Browser not initialized\n"
                raise UsageError(msg)

        if args.show:
            self._show()

    def _open_new_tabs(self, names):
        """Open browser tabs for a list of variable names and paths."""
        self._cmds = []
        for name in names:
            url, msg = self.get_url(name)
            if not url:
                print(msg)
            else:
                cmd = [self._browser, url]
                self._cmds.append(cmd)
                self._open_new_tab(cmd)

    def get_url(self, name):
        """Get url associated with name, returning None if not found"""
        return self.get_pydoc_url(name)

    def get_pydoc_url(self, name):
        """Get pydoc url for name of variable or path."""
        root = self._server.url()
        msg = ''
        page = self.get_pydoc_page_name(name)
        if page:
            if self._content_type == 'html':
                url = root + page + '.html'
            else:
                url = root + page + '.txt'
        else:
            url = None
            msg = 'Documentation not found: {}'.format(name)
        return url, msg

    def get_pydoc_page_name(self, path):
        """Return name of pydoc page, or None if path is not valid."""
        obj = self.get_object(path)
        if obj:
            page_name = fully_qualified_name(obj)
        else:
            page_name = None
        return page_name

    def get_object(self, path):
        """Return object, or None if the object does not exist."""
        parts = [part for part in path.split('.') if part]
        if parts[0] in self.shell.user_ns:
            obj = self.shell.user_ns[parts[0]]
            if parts[1:]:
                obj = getattr_path(obj, parts[1:])
        else:
            obj = pydoc.locate(path)
        return obj

    @property
    def command_lines(self):
        """Command lines used (most recently) to invoke subproccesses."""
        return self._cmds

    @property
    def browser(self):
        """Name of browser used to open new tabs."""
        return self._browser

    @browser.setter
    def browser(self, browser_args):
        """Set browser name."""
        browser_name = ' '.join(browser_args)
        self._browser = browser_name.strip('"\'')

    def _open_new_tab(self, cmd):
        """Open a new browser tab by invoking a subprocess."""
        try:
            if self.new_tabs_enabled:
                # chromium browser writes to screen, so redirect
                subprocess.Popen(cmd, stdout=subprocess.PIPE)
        except OSError:
            msg = "Browser named {} failed to open new tab\n".format(
                self._browser)
            raise UsageError(msg)

    def _show(self):
        """Show state of magic."""
        msg = ''
        if self._browser:
            msg += 'browser: {}\n'.format(self._browser)
        else:
            msg += 'browser: None\n'
        msg += 'content-type: {}\n'.format(self._content_type)
        print(msg, end='')
        self._server.show()

    def server_running(self):
        """Interrogate server to see if it is still running."""
        return self._server.running()

    def base_url(self):
        """Base Url for newtabmagic server."""
        return self._server.url()

class ServerProcess(object):
    """Class for the newtabmagic server process."""

    def __init__(self):
        self._process = None
        self._port = 8888

    def start(self):
        """Start server if not previously started."""
        msg = ''
        if not self.running():
            # Using script cell magic so that shutting down
            # IPython stops the server process.

            module = 'newtabmagic'
            path = os.path.dirname(os.path.realpath(__file__))

            lines = ('import sys; sys.path += [{0}];'
                     'import {1}; {1}.start_server({2})')
            cell = lines.format(repr(path), module, self._port)

            line = "python --proc proc --bg"
            ip = get_ipython()
            ip.run_cell_magic("script", line, cell)
            self._process = ip.user_ns['proc']

        else:
            msg = 'Server already started\n'
        msg += 'Server running at {}'.format(self.url())
        print(msg)

    def stop(self):
        """Stop server process."""

        msg = ''
        if self._process:
            _stop_process(self._process, 'Server process')
        else:
            msg += 'Server not started.\n'
        if msg:
            print(msg, end='')

    def running(self):
        """If the server has been started, is it still running?"""
        return self._process is not None and self._process.poll() is None

    def show(self):
        """Show state."""
        msg = ''
        if self._process:
            msg += 'server pid: {}\n'.format(self._process.pid)
            msg += 'server poll: {}\n'.format(self._process.poll())
        msg += 'server running: {}\n'.format(self.running())
        msg += 'server port: {}\n'.format(self._port)
        msg += 'server root url: {}\n'.format(self.url())
        print(msg, end='')

    def url(self):
        """Base url. Includes protocol, host, and port number."""
        proto = 'http'
        ip = '127.0.0.1'
        return '{}://{}:{}/'.format(proto, ip, self._port)

    @property
    def port(self):
        """Port number server listens on."""
        return self._port

    @port.setter
    def port(self, port):
        """Set port number if server is not running."""
        if not self.running():
            self._port = port
        else:
            print('Server already running. Port number not changed')

def fully_qualified_name(obj):
    """Returns fully qualified name, including module name, except for the
    built-in module."""

    builtins = ['builtins', '__builtin__']

    if inspect.ismodule(obj):
        name = obj.__name__
    else:
        module_name = get_module_name(obj)
        if not module_name or module_name in builtins:
            name = qualname(obj, module_name)
        else:
            name = module_name + '.' + qualname(obj, module_name)
    return name

def qualname(obj, module_name):
    """Qualified name not including module name."""

    if sys.version_info >= (3, 3):
        return qualname33(obj)
    elif sys.version_info >= (3, 0):
        return qualname32(obj)
    else:
        return qualname27(obj, module_name)

def qualname33(obj):
    """Qualified name, not including module name, for Python 3.3+."""
    try:
        return obj.undecorated.__qualname__
    except AttributeError:
        try:
            return obj.__qualname__
        except AttributeError:
            return type(obj).__name__

def qualname32(obj):
    """Qualified name, not including module name, for Python 3.0-3.2."""
    try:
        return obj.__objclass__.__name__ + "." + obj.__name__
    except AttributeError:
        try:
            if inspect.ismodule(obj.__self__):
                return obj.__name__
            else:
                return obj.__self__.__class__.__name__ + "." + obj.__name__
        except AttributeError:
            try:
                return obj.__name__
            except AttributeError:
                return type(obj).__name__

def qualname27(obj, module_name):
    """Qualified name, not including module name, Python 2.7."""
    try:
        return obj.__objclass__.__name__ + "." + obj.__name__
    except AttributeError:
        try:
            attr = obj.__name__
            # loop through base classes looking for class where
            # attr is defined.
            for base_class in obj.im_class.mro():
                qual_name = base_class.__name__ + '.' + attr
                if pydoc.locate(module_name + '.' + qual_name):
                    return qual_name
        except AttributeError:  # no im_class
            try:
                if obj.__self__ is not None:
                    return obj.__self__.__class__.__name__ + "." + obj.__name__
            except AttributeError: # no __self__
                pass
            try:
                return obj.__name__
            except AttributeError:
                return type(obj).__name__

def getattr_path(obj, attrs):
    """Get a named attribute from an object, returning None if the
    attribute does not exist.

    Keyword Arguments:
    attrs -- a list of attributes obtained from a path by applying
                String.split('.')
    """

    try:
        for attr in attrs:
            obj = getattr(obj, attr)
    except AttributeError:
        obj = None
    return obj

def get_module_name(object_):
    """Return module name of object, or module name of object's class."""
    try:
        name = object_.__module__
    except AttributeError:
        name = object_.__class__.__module__
    return name

def _stop_process(p, name):
    """Stop process, by applying terminate and kill."""
    # Based on code in IPython.core.magics.script.ScriptMagics.shebang
    if p.poll() is not None:
        print("{} is already stopped.".format(name))
        return
    p.terminate()
    time.sleep(0.1)
    if p.poll() is not None:
        print("{} is terminated.".format(name))
        return
    p.kill()
    print("{} is killed.".format(name))

class HtmlHandler(tornado.web.RequestHandler):
    """Handler for html pages."""
    def get(self):
        page = get_html(self.request.uri)
        self.write(page)

class TextHandler(tornado.web.RequestHandler):
    """Handler for plain text pages."""
    def get(self):
        page = get_text(self.request.uri)
        self.set_header("Content-Type", "text/plain")
        self.write(page)

def get_html(url):
    """Get method for HTML pages."""
    if sys.version_info[0] >= 3:
        page = pydoc._url_handler(url)  # pylint: disable=W0212
    else:
        if url.endswith('.html'):
            page = pydoc_html_help(url[1:-5])
        elif url == '/':
            # index page is not available for Python 2
            page = "Index page not available"
    return page

def get_text(uri):
    """Get method for plain text pages."""
    path = uri[1:-4]  # remove leading '/' and trailing '.txt'
    page = pydoc_text_help(path)
    return page

def pydoc_html_help(path):
    """Returns a pydoc help page in html for the object referred
    to by path.

    Keyword Arguments:
    path -- an object name or a dotted path to an object

    Reference: pydoc.writedoc

    """
    try:
        obj, name = pydoc.resolve(path)
        html_doc = pydoc.html.document(obj, name)
        page = pydoc.html.page(pydoc.describe(obj), html_doc)
    except (ImportError, pydoc.ErrorDuringImport):
        raise tornado.web.HTTPError(404)
    return page

def pydoc_text_help(path):
    """Returns a pydoc help page in plain text for the object
    referred to by path.

    Keyword Arguments:
    path -- an object name or a dotted path to an object
    """
    try:
        if sys.version_info[0] == 2:
            page = pydoc.plain(pydoc.render_doc(path))
        else:
            page = pydoc.render_doc(path, renderer=pydoc.plaintext)
    except (ImportError, pydoc.ErrorDuringImport):
        raise tornado.web.HTTPError(404)
    return page.encode('utf-8')

def start_server(port):
    """Starts the newtab server on the given port."""
    application = tornado.web.Application([
        (r'/', HtmlHandler),
        (r'/[\w\.]*.html', HtmlHandler),
        (r'/[\w\.]*.txt', TextHandler),
    ])
    application.listen(port)

    tornado.ioloop.IOLoop.instance().start()

def load_ipython_extension(ip):
    """Load NewTabMagics extension."""
    ip.register_magics(NewTabMagics)
<|endoftext|>