<commit_before># -*- coding: utf-8 -*-
"""
    vcs.nodes
    ~~~~~~~~~

    Module holding everything related to vcs nodes.

    :created_on: Apr 8, 2010
    :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
"""
import stat
import posixpath
import mimetypes

from vcs.utils.lazy import LazyProperty
from vcs.utils import safe_unicode
from vcs.exceptions import NodeError
from vcs.exceptions import RemovedFileNodeError

from pygments import lexers

class NodeKind:
    DIR = 1
    FILE = 2

class NodeState:
    ADDED = u'added'
    CHANGED = u'changed'
    NOT_CHANGED = u'not changed'
    REMOVED = u'removed'

class NodeGeneratorBase(object):
    """
    Base class for removed added and changed filenodes, it's a lazy generator
    class that will create filenodes only on iteration or call

    The len method doesn't need to create filenodes at all
    """

    def __init__(self, current_paths, cs):
        self.cs = cs
        self.current_paths = current_paths

    def __call__(self):
        return [n for n in self]

    def __getslice__(self, i, j):
        for p in self.current_paths[i:j]:
            yield self.cs.get_node(p)

    def __len__(self):
        return len(self.current_paths)

    def __iter__(self):
        for p in self.current_paths:
            yield self.cs.get_node(p)

class AddedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding Added files for current changeset
    """
    pass

class ChangedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding Changed files for current changeset
    """
    pass

class RemovedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding removed files for current changeset
    """
    def __iter__(self):
        for p in self.current_paths:
            yield RemovedFileNode(path=p)

    def __getslice__(self, i, j):
        for p in self.current_paths[i:j]:
            yield RemovedFileNode(path=p)

class Node(object):
    """
    Simplest class representing file or directory on repository.  SCM backends
    should use ``FileNode`` and ``DirNode`` subclasses rather than ``Node``
    directly.

    Node's ``path`` cannot start with slash as we operate on *relative* paths
    only. Moreover, every single node is identified by the ``path`` attribute,
    so it cannot end with slash, too. Otherwise, path could lead to mistakes.
    """

    def __init__(self, path, kind):
        if path.startswith('/'):
            raise NodeError("Cannot initialize Node objects with slash at "
                "the beginning as only relative paths are supported")
        self.path = path.rstrip('/')
        if path == '' and kind != NodeKind.DIR:
            raise NodeError("Only DirNode and its subclasses may be "
                            "initialized with empty path")
        self.kind = kind
        #self.dirs, self.files = [], []
        if self.is_root() and not self.is_dir():
            raise NodeError("Root node cannot be FILE kind")

    @LazyProperty
    def parent(self):
        parent_path = self.get_parent_path()
        if parent_path:
            if self.changeset:
                return self.changeset.get_node(parent_path)
            return DirNode(parent_path)
        return None

    @LazyProperty
    def name(self):
        """
        Returns name of the node so if its path
        then only last part is returned.
        """
        return safe_unicode(self.path.rstrip('/').split('/')[-1])

    def _get_kind(self):
        return self._kind

    def _set_kind(self, kind):
        if hasattr(self, '_kind'):
            raise NodeError("Cannot change node's kind")
        else:
            self._kind = kind
            # Post setter check (path's trailing slash)
            if self.path.endswith('/'):
                raise NodeError("Node's path cannot end with slash")

    kind = property(_get_kind, _set_kind)

    def __cmp__(self, other):
        """
        Comparator using name of the node, needed for quick list sorting.
        """
        kind_cmp = cmp(self.kind, other.kind)
        if kind_cmp:
            return kind_cmp
        return cmp(self.name, other.name)

    def __eq__(self, other):
        for attr in ['name', 'path', 'kind']:
            if getattr(self, attr) != getattr(other, attr):
                return False
        if self.is_file():
            if self.content != other.content:
                return False
        else:
            # For DirNode's check without entering each dir
            self_nodes_paths = list(sorted(n.path for n in self.nodes))
            other_nodes_paths = list(sorted(n.path for n in self.nodes))
            if self_nodes_paths != other_nodes_paths:
                return False
        return True

    def __nq__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return '<%s %r>' % (self.__class__.__name__, self.path)

    def __str__(self):
        return self.__repr__()

    def __unicode__(self):
        return self.name

    def get_parent_path(self):
        """
        Returns node's parent path or empty string if node is root.
        """
        if self.is_root():
            return ''
        return posixpath.dirname(self.path.rstrip('/')) + '/'

    def is_file(self):
        """
        Returns ``True`` if node's kind is ``NodeKind.FILE``, ``False``
        otherwise.
        """
        return self.kind == NodeKind.FILE

    def is_dir(self):
        """
        Returns ``True`` if node's kind is ``NodeKind.DIR``, ``False``
        otherwise.
        """
        return self.kind == NodeKind.DIR

    def is_root(self):
        """
        Returns ``True`` if node is a root node and ``False`` otherwise.
        """
        return self.kind == NodeKind.DIR and self.path == ''

    @LazyProperty
    def added(self):
        return self.state is NodeState.ADDED

    @LazyProperty
    def changed(self):
        return self.state is NodeState.CHANGED

    @LazyProperty
    def not_changed(self):
        return self.state is NodeState.NOT_CHANGED

    @LazyProperty
    def removed(self):
        return self.state is NodeState.REMOVED

class FileNode(Node):
    """
    Class representing file nodes.

    :attribute: path: path to the node, relative to repostiory's root
    :attribute: content: if given arbitrary sets content of the file
    :attribute: changeset: if given, first time content is accessed, callback
    :attribute: mode: octal stat mode for a node. Default is 0100644.
    """

    def __init__(self, path, content=None, changeset=None, mode=None):
        """
        Only one of ``content`` and ``changeset`` may be given. Passing both
        would raise ``NodeError`` exception.

        :param path: relative path to the node
        :param content: content may be passed to constructor
        :param changeset: if given, will use it to lazily fetch content
        :param mode: octal representation of ST_MODE (i.e. 0100644)
        """

        if content and changeset:
            raise NodeError("Cannot use both content and changeset")
        super(FileNode, self).__init__(path, kind=NodeKind.FILE)
        self.changeset = changeset
        self._content = content
        self._mode = mode or 0100644

    @LazyProperty
    def mode(self):
        """
        Returns lazily mode of the FileNode. If ``changeset`` is not set, would
        use value given at initialization or 0100644 (default).
        """
        if self.changeset:
            mode = self.changeset.get_file_mode(self.path)
        else:
            mode = self._mode
        return mode

    @LazyProperty
    def content(self):
        """
        Returns lazily content of the FileNode. If possible, would try to
        decode content from UTF-8.
        """
        if self.changeset:
            content = self.changeset.get_file_content(self.path)
        else:
            content = self._content
        if bool(content and '\0' in content):
            return content
        return safe_unicode(content)

    @LazyProperty
    def size(self):
        if self.changeset:
            return self.changeset.get_file_size(self.path)
        raise NodeError("Cannot retrieve size of the file without related "
            "changeset attribute")

    @LazyProperty
    def message(self):
        if self.changeset:
            return self.last_changeset.message
        raise NodeError("Cannot retrieve message of the file without related "
            "changeset attribute")

    @LazyProperty
    def last_changeset(self):
        if self.changeset:
            return self.changeset.get_file_changeset(self.path)
        raise NodeError("Cannot retrieve last changeset of the file without "
            "related changeset attribute")

    @LazyProperty
    def mimetype(self):

        """
        Mimetype is calculated based on the file's content. If ``_mimetype``
        attribute is available, it will be returned (backends which store
        mimetypes or can easily recognize them, should set this private
        attribute to indicate that type should *NOT* be calculated).
        """
        if hasattr(self, '_mimetype'):
            return self._mimetype

        mtype = mimetypes.guess_type(self.name)[0]
        return mtype or 'application/octet-stream'

    @LazyProperty
    def mimetype_main(self):
        return self.mimetype.split('/')[0]

    @LazyProperty
    def lexer(self):
        """
        Returns pygment's lexer class. Would try to guess lexer taking file's
        content, name and mimetype.
        """
        try:
            lexer = lexers.guess_lexer_for_filename(self.name, self.content)
        except lexers.ClassNotFound:
            lexer = lexers.TextLexer()
        # returns first alias
        return lexer

    @LazyProperty
    def lexer_alias(self):
        """
        Returns first alias of the lexer guessed for this file.
        """
        return self.lexer.aliases[0]

    @LazyProperty
    def history(self):
        """
        Returns a list of changeset for this file in which the file was changed
        """
        if self.changeset is None:
            raise NodeError('Unable to get changeset for this FileNode')
        return self.changeset.get_file_history(self.path)

    @LazyProperty
    def annotate(self):
        """
        Returns a list of three element tuples with lineno,changeset and line
        """
        if self.changeset is None:
            raise NodeError('Unable to get changeset for this FileNode')
        return self.changeset.get_file_annotate(self.path)

    @LazyProperty
    def state(self):
        if not self.changeset:
            raise NodeError("Cannot check state of the node if it's not "
                "linked with changeset")
        elif self.path in (node.path for node in self.changeset.added):
            return NodeState.ADDED
        elif self.path in (node.path for node in self.changeset.changed):
            return NodeState.CHANGED
        else:
            return NodeState.NOT_CHANGED

    @LazyProperty
    def is_binary(self):
        """
        Returns True if file has binary content.
        """
        return bool(self.content and '\0' in self.content)

    @LazyProperty
    def extension(self):
        """Returns filenode extension"""
        return self.name.split('.')[-1]

    def is_executable(self):
        """
        Returns ``True`` if file has executable flag turned on.
        """
        return bool(self.mode & stat.S_IXUSR)

class RemovedFileNode(FileNode):
    """
    Dummy FileNode class - trying to access any public attribute except path,
    name, kind or state (or methods/attributes checking those two) would raise
    RemovedFileNodeError.
    """
    ALLOWED_ATTRIBUTES = ['name', 'path', 'state', 'is_root', 'is_file',
        'is_dir', 'kind', 'added', 'changed', 'not_changed', 'removed']

    def __init__(self, path):
        """
        :param path: relative path to the node
        """
        super(RemovedFileNode, self).__init__(path=path)

    def __getattribute__(self, attr):
        if attr.startswith('_') or attr in RemovedFileNode.ALLOWED_ATTRIBUTES:
            return super(RemovedFileNode, self).__getattribute__(attr)
        raise RemovedFileNodeError("Cannot access attribute %s on "
            "RemovedFileNode" % attr)

    @LazyProperty
    def state(self):
        return NodeState.REMOVED

class DirNode(Node):
    """
    DirNode stores list of files and directories within this node.
    Nodes may be used standalone but within repository context they
    lazily fetch data within same repositorty's changeset.
    """

    def __init__(self, path, nodes=(), changeset=None):
        """
        Only one of ``nodes`` and ``changeset`` may be given. Passing both
        would raise ``NodeError`` exception.

        :param path: relative path to the node
        :param nodes: content may be passed to constructor
        :param changeset: if given, will use it to lazily fetch content
        :param size: always 0 for ``DirNode``
        """
        if nodes and changeset:
            raise NodeError("Cannot use both nodes and changeset")
        super(DirNode, self).__init__(path, NodeKind.DIR)
        self.changeset = changeset
        self._nodes = nodes

    @LazyProperty
    def content(self):
        raise NodeError("%s represents a dir and has no ``content`` attribute"
            % self)

    @LazyProperty
    def nodes(self):
        if self.changeset:
            nodes = self.changeset.get_nodes(self.path)
        else:
            nodes = self._nodes
        self._nodes_dict = dict((node.path, node) for node in nodes)
        return sorted(nodes)

    @LazyProperty
    def files(self):
        return sorted((node for node in self.nodes if node.is_file()))

    @LazyProperty
    def dirs(self):
        return sorted((node for node in self.nodes if node.is_dir()))

    def __iter__(self):
        for node in self.nodes:
            yield node

    def get_node(self, path):
        """
        Returns node from within this particular ``DirNode``, so it is now
        allowed to fetch, i.e. node located at 'docs/api/index.rst' from node
        'docs'. In order to access deeper nodes one must fetch nodes between
        them first - this would work::

           docs = root.get_node('docs')
           docs.get_node('api').get_node('index.rst')

        :param: path - relative to the current node

        .. note::
           To access lazily (as in example above) node have to be initialized
           with related changeset object - without it node is out of
           context and may know nothing about anything else than nearest
           (located at same level) nodes.
        """
        try:
            path = path.rstrip('/')
            if path == '':
                raise NodeError("Cannot retrieve node without path")
            self.nodes  # access nodes first in order to set _nodes_dict
            paths = path.split('/')
            if len(paths) == 1:
                if not self.is_root():
                    path = '/'.join((self.path, paths[0]))
                else:
                    path = paths[0]
                return self._nodes_dict[path]
            elif len(paths) > 1:
                if self.changeset is None:
                    raise NodeError("Cannot access deeper "
                                    "nodes without changeset")
                else:
                    path1, path2 = paths[0], '/'.join(paths[1:])
                    return self.get_node(path1).get_node(path2)
            else:
                raise KeyError
        except KeyError:
            raise NodeError("Node does not exist at %s" % path)

    @LazyProperty
    def state(self):
        raise NodeError("Cannot access state of DirNode")

    @LazyProperty
    def size(self):
        size = 0
        for root, dirs, files in self.changeset.walk(self.path):
            for f in files:
                size += f.size

        return size

class RootNode(DirNode):
    """
    DirNode being the root node of the repository.
    """

    def __init__(self, nodes=(), changeset=None):
        super(RootNode, self).__init__(path='', nodes=nodes,
            changeset=changeset)

    def __repr__(self):
        return '<%s>' % self.__class__.__name__
<commit_msg>don't double check for binary, just set the flag after seeing the content<commit_after># -*- coding: utf-8 -*-
"""
    vcs.nodes
    ~~~~~~~~~

    Module holding everything related to vcs nodes.

    :created_on: Apr 8, 2010
    :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
"""
import stat
import posixpath
import mimetypes

from vcs.utils.lazy import LazyProperty
from vcs.utils import safe_unicode
from vcs.exceptions import NodeError
from vcs.exceptions import RemovedFileNodeError

from pygments import lexers

class NodeKind:
    DIR = 1
    FILE = 2

class NodeState:
    ADDED = u'added'
    CHANGED = u'changed'
    NOT_CHANGED = u'not changed'
    REMOVED = u'removed'

class NodeGeneratorBase(object):
    """
    Base class for removed added and changed filenodes, it's a lazy generator
    class that will create filenodes only on iteration or call

    The len method doesn't need to create filenodes at all
    """

    def __init__(self, current_paths, cs):
        self.cs = cs
        self.current_paths = current_paths

    def __call__(self):
        return [n for n in self]

    def __getslice__(self, i, j):
        for p in self.current_paths[i:j]:
            yield self.cs.get_node(p)

    def __len__(self):
        return len(self.current_paths)

    def __iter__(self):
        for p in self.current_paths:
            yield self.cs.get_node(p)

class AddedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding Added files for current changeset
    """
    pass

class ChangedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding Changed files for current changeset
    """
    pass

class RemovedFileNodesGenerator(NodeGeneratorBase):
    """
    Class holding removed files for current changeset
    """
    def __iter__(self):
        for p in self.current_paths:
            yield RemovedFileNode(path=p)

    def __getslice__(self, i, j):
        for p in self.current_paths[i:j]:
            yield RemovedFileNode(path=p)

class Node(object):
    """
    Simplest class representing file or directory on repository.  SCM backends
    should use ``FileNode`` and ``DirNode`` subclasses rather than ``Node``
    directly.

    Node's ``path`` cannot start with slash as we operate on *relative* paths
    only. Moreover, every single node is identified by the ``path`` attribute,
    so it cannot end with slash, too. Otherwise, path could lead to mistakes.
    """

    def __init__(self, path, kind):
        if path.startswith('/'):
            raise NodeError("Cannot initialize Node objects with slash at "
                "the beginning as only relative paths are supported")
        self.path = path.rstrip('/')
        if path == '' and kind != NodeKind.DIR:
            raise NodeError("Only DirNode and its subclasses may be "
                            "initialized with empty path")
        self.kind = kind
        #self.dirs, self.files = [], []
        if self.is_root() and not self.is_dir():
            raise NodeError("Root node cannot be FILE kind")

    @LazyProperty
    def parent(self):
        parent_path = self.get_parent_path()
        if parent_path:
            if self.changeset:
                return self.changeset.get_node(parent_path)
            return DirNode(parent_path)
        return None

    @LazyProperty
    def name(self):
        """
        Returns name of the node so if its path
        then only last part is returned.
        """
        return safe_unicode(self.path.rstrip('/').split('/')[-1])

    def _get_kind(self):
        return self._kind

    def _set_kind(self, kind):
        if hasattr(self, '_kind'):
            raise NodeError("Cannot change node's kind")
        else:
            self._kind = kind
            # Post setter check (path's trailing slash)
            if self.path.endswith('/'):
                raise NodeError("Node's path cannot end with slash")

    kind = property(_get_kind, _set_kind)

    def __cmp__(self, other):
        """
        Comparator using name of the node, needed for quick list sorting.
        """
        kind_cmp = cmp(self.kind, other.kind)
        if kind_cmp:
            return kind_cmp
        return cmp(self.name, other.name)

    def __eq__(self, other):
        for attr in ['name', 'path', 'kind']:
            if getattr(self, attr) != getattr(other, attr):
                return False
        if self.is_file():
            if self.content != other.content:
                return False
        else:
            # For DirNode's check without entering each dir
            self_nodes_paths = list(sorted(n.path for n in self.nodes))
            other_nodes_paths = list(sorted(n.path for n in self.nodes))
            if self_nodes_paths != other_nodes_paths:
                return False
        return True

    def __nq__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return '<%s %r>' % (self.__class__.__name__, self.path)

    def __str__(self):
        return self.__repr__()

    def __unicode__(self):
        return self.name

    def get_parent_path(self):
        """
        Returns node's parent path or empty string if node is root.
        """
        if self.is_root():
            return ''
        return posixpath.dirname(self.path.rstrip('/')) + '/'

    def is_file(self):
        """
        Returns ``True`` if node's kind is ``NodeKind.FILE``, ``False``
        otherwise.
        """
        return self.kind == NodeKind.FILE

    def is_dir(self):
        """
        Returns ``True`` if node's kind is ``NodeKind.DIR``, ``False``
        otherwise.
        """
        return self.kind == NodeKind.DIR

    def is_root(self):
        """
        Returns ``True`` if node is a root node and ``False`` otherwise.
        """
        return self.kind == NodeKind.DIR and self.path == ''

    @LazyProperty
    def added(self):
        return self.state is NodeState.ADDED

    @LazyProperty
    def changed(self):
        return self.state is NodeState.CHANGED

    @LazyProperty
    def not_changed(self):
        return self.state is NodeState.NOT_CHANGED

    @LazyProperty
    def removed(self):
        return self.state is NodeState.REMOVED

class FileNode(Node):
    """
    Class representing file nodes.

    :attribute: path: path to the node, relative to repostiory's root
    :attribute: content: if given arbitrary sets content of the file
    :attribute: changeset: if given, first time content is accessed, callback
    :attribute: mode: octal stat mode for a node. Default is 0100644.
    """

    def __init__(self, path, content=None, changeset=None, mode=None):
        """
        Only one of ``content`` and ``changeset`` may be given. Passing both
        would raise ``NodeError`` exception.

        :param path: relative path to the node
        :param content: content may be passed to constructor
        :param changeset: if given, will use it to lazily fetch content
        :param mode: octal representation of ST_MODE (i.e. 0100644)
        """

        if content and changeset:
            raise NodeError("Cannot use both content and changeset")
        super(FileNode, self).__init__(path, kind=NodeKind.FILE)
        self.changeset = changeset
        self._content = content
        self._mode = mode or 0100644
        self._binary_flag = None

    @LazyProperty
    def mode(self):
        """
        Returns lazily mode of the FileNode. If ``changeset`` is not set, would
        use value given at initialization or 0100644 (default).
        """
        if self.changeset:
            mode = self.changeset.get_file_mode(self.path)
        else:
            mode = self._mode
        return mode

    @LazyProperty
    def content(self):
        """
        Returns lazily content of the FileNode. If possible, would try to
        decode content from UTF-8.
        """
        if self.changeset:
            content = self.changeset.get_file_content(self.path)
        else:
            content = self._content
        if bool(content and '\0' in content):
            self._binary_flag = True
            return content
        self._binary_flag = False
        return safe_unicode(content)

    @LazyProperty
    def size(self):
        if self.changeset:
            return self.changeset.get_file_size(self.path)
        raise NodeError("Cannot retrieve size of the file without related "
            "changeset attribute")

    @LazyProperty
    def message(self):
        if self.changeset:
            return self.last_changeset.message
        raise NodeError("Cannot retrieve message of the file without related "
            "changeset attribute")

    @LazyProperty
    def last_changeset(self):
        if self.changeset:
            return self.changeset.get_file_changeset(self.path)
        raise NodeError("Cannot retrieve last changeset of the file without "
            "related changeset attribute")

    @LazyProperty
    def mimetype(self):
        """
        Mimetype is calculated based on the file's content. If ``_mimetype``
        attribute is available, it will be returned (backends which store
        mimetypes or can easily recognize them, should set this private
        attribute to indicate that type should *NOT* be calculated).
        """
        if hasattr(self, '_mimetype'):
            return self._mimetype

        mtype = mimetypes.guess_type(self.name)[0]

        if mtype is None:
            if self.is_binary:
                mtype = 'application/octet-stream'
            else:
                mtype = 'text/plain'
        return mtype

    @LazyProperty
    def mimetype_main(self):
        return self.mimetype.split('/')[0]

    @LazyProperty
    def lexer(self):
        """
        Returns pygment's lexer class. Would try to guess lexer taking file's
        content, name and mimetype.
        """
        try:
            lexer = lexers.guess_lexer_for_filename(self.name, self.content)
        except lexers.ClassNotFound:
            lexer = lexers.TextLexer()
        # returns first alias
        return lexer

    @LazyProperty
    def lexer_alias(self):
        """
        Returns first alias of the lexer guessed for this file.
        """
        return self.lexer.aliases[0]

    @LazyProperty
    def history(self):
        """
        Returns a list of changeset for this file in which the file was changed
        """
        if self.changeset is None:
            raise NodeError('Unable to get changeset for this FileNode')
        return self.changeset.get_file_history(self.path)

    @LazyProperty
    def annotate(self):
        """
        Returns a list of three element tuples with lineno,changeset and line
        """
        if self.changeset is None:
            raise NodeError('Unable to get changeset for this FileNode')
        return self.changeset.get_file_annotate(self.path)

    @LazyProperty
    def state(self):
        if not self.changeset:
            raise NodeError("Cannot check state of the node if it's not "
                "linked with changeset")
        elif self.path in (node.path for node in self.changeset.added):
            return NodeState.ADDED
        elif self.path in (node.path for node in self.changeset.changed):
            return NodeState.CHANGED
        else:
            return NodeState.NOT_CHANGED

    @LazyProperty
    def is_binary(self):
        """
        Returns True if file has binary content.
        """
        return bool(self.content and self._binary_flag)

    @LazyProperty
    def extension(self):
        """Returns filenode extension"""
        return self.name.split('.')[-1]

    def is_executable(self):
        """
        Returns ``True`` if file has executable flag turned on.
        """
        return bool(self.mode & stat.S_IXUSR)

class RemovedFileNode(FileNode):
    """
    Dummy FileNode class - trying to access any public attribute except path,
    name, kind or state (or methods/attributes checking those two) would raise
    RemovedFileNodeError.
    """
    ALLOWED_ATTRIBUTES = ['name', 'path', 'state', 'is_root', 'is_file',
        'is_dir', 'kind', 'added', 'changed', 'not_changed', 'removed']

    def __init__(self, path):
        """
        :param path: relative path to the node
        """
        super(RemovedFileNode, self).__init__(path=path)

    def __getattribute__(self, attr):
        if attr.startswith('_') or attr in RemovedFileNode.ALLOWED_ATTRIBUTES:
            return super(RemovedFileNode, self).__getattribute__(attr)
        raise RemovedFileNodeError("Cannot access attribute %s on "
            "RemovedFileNode" % attr)

    @LazyProperty
    def state(self):
        return NodeState.REMOVED

class DirNode(Node):
    """
    DirNode stores list of files and directories within this node.
    Nodes may be used standalone but within repository context they
    lazily fetch data within same repositorty's changeset.
    """

    def __init__(self, path, nodes=(), changeset=None):
        """
        Only one of ``nodes`` and ``changeset`` may be given. Passing both
        would raise ``NodeError`` exception.

        :param path: relative path to the node
        :param nodes: content may be passed to constructor
        :param changeset: if given, will use it to lazily fetch content
        :param size: always 0 for ``DirNode``
        """
        if nodes and changeset:
            raise NodeError("Cannot use both nodes and changeset")
        super(DirNode, self).__init__(path, NodeKind.DIR)
        self.changeset = changeset
        self._nodes = nodes

    @LazyProperty
    def content(self):
        raise NodeError("%s represents a dir and has no ``content`` attribute"
            % self)

    @LazyProperty
    def nodes(self):
        if self.changeset:
            nodes = self.changeset.get_nodes(self.path)
        else:
            nodes = self._nodes
        self._nodes_dict = dict((node.path, node) for node in nodes)
        return sorted(nodes)

    @LazyProperty
    def files(self):
        return sorted((node for node in self.nodes if node.is_file()))

    @LazyProperty
    def dirs(self):
        return sorted((node for node in self.nodes if node.is_dir()))

    def __iter__(self):
        for node in self.nodes:
            yield node

    def get_node(self, path):
        """
        Returns node from within this particular ``DirNode``, so it is now
        allowed to fetch, i.e. node located at 'docs/api/index.rst' from node
        'docs'. In order to access deeper nodes one must fetch nodes between
        them first - this would work::

           docs = root.get_node('docs')
           docs.get_node('api').get_node('index.rst')

        :param: path - relative to the current node

        .. note::
           To access lazily (as in example above) node have to be initialized
           with related changeset object - without it node is out of
           context and may know nothing about anything else than nearest
           (located at same level) nodes.
        """
        try:
            path = path.rstrip('/')
            if path == '':
                raise NodeError("Cannot retrieve node without path")
            self.nodes  # access nodes first in order to set _nodes_dict
            paths = path.split('/')
            if len(paths) == 1:
                if not self.is_root():
                    path = '/'.join((self.path, paths[0]))
                else:
                    path = paths[0]
                return self._nodes_dict[path]
            elif len(paths) > 1:
                if self.changeset is None:
                    raise NodeError("Cannot access deeper "
                                    "nodes without changeset")
                else:
                    path1, path2 = paths[0], '/'.join(paths[1:])
                    return self.get_node(path1).get_node(path2)
            else:
                raise KeyError
        except KeyError:
            raise NodeError("Node does not exist at %s" % path)

    @LazyProperty
    def state(self):
        raise NodeError("Cannot access state of DirNode")

    @LazyProperty
    def size(self):
        size = 0
        for root, dirs, files in self.changeset.walk(self.path):
            for f in files:
                size += f.size

        return size

class RootNode(DirNode):
    """
    DirNode being the root node of the repository.
    """

    def __init__(self, nodes=(), changeset=None):
        super(RootNode, self).__init__(path='', nodes=nodes,
            changeset=changeset)

    def __repr__(self):
        return '<%s>' % self.__class__.__name__
<|endoftext|>


<commit_before>"""
Everything you need to solve inverse problems!

This package provides the basic building blocks to implement inverse problem
solvers. The main class for this is :class:`~fatiando.inversion.base.Misfit`.
It represents a data-misfit function and contains various tools to fit a
model to some data. All you have to do is implement methods to calculate the
predicted (or modeled) data and (optionally) the Jacobian (or sensitivity)
matrix. With only that, you have access to a range of optimization methods,
regularization, joint inversion, etc.

Modules
-------

* :mod:`~fatiando.inversion.base`: Base classes for building inverse problem
  solvers
* :mod:`~fatiando.inversion.regularization`: Classes for common regularizing
  functions and base classes for building new ones
* :mod:`~fatiando.inversion.solvers`: Functions for several optimization
  methods (used by :class:`~fatiando.inversion.base.Misfit`)

Have a look at the examples bellow on how to use the package. More geophysical
examples include :mod:`fatiando.seismic.srtomo`,
:mod:`fatiando.seismic.profile`,
:mod:`fatiando.gravmag.basin2d`,
:mod:`fatiando.gravmag.eqlayer`,
and
:mod:`fatiando.gravmag.euler`.

Examples
--------

Linear Regression
+++++++++++++++++

Here is an example of how to implement a simple linear regression using the
:class:`~fatiando.inversion.base.Misfit` class.

What we want to do is fit :math:`f(a, b, x) = y = ax + b` to find a and b.
Putting a and b in a parameter vector `p = [a, b]` we get:

.. math::

    \mathbf{d} = \mathbf{A} \mathbf{p}

where :math:`\mathbf{d}` is the data vector containing all the values of y
and :math:`\mathbf{A}` is the Jacobian matrix with the values of x in the first
column and 1 in the second column.

All we have to do to implement a solver for this problem is write the
`predicted` (to calculate y from values of a and b) and `jacobian` (to
calculate the Jacobian matrix):

>>> import numpy as np
>>> from fatiando.inversion.base import Misfit
>>> class Regression(Misfit):
...     "Perform a linear regression"
...     def __init__(self, x, y):
...         super(Regression, self).__init__(data=y, nparams=2, islinear=True)
...         self.x = x
...     def predicted(self, p):
...         a, b = p
...         return a*self.x + b
...     def jacobian(self, p):
...         jac = np.ones((self.ndata, self.nparams))
...         jac[:, 0] = self.x
...         return jac
>>> x = np.linspace(0, 5, 6)
>>> x
array([ 0.,  1.,  2.,  3.,  4.,  5.])
>>> y = 2*x + 5
>>> y
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> solver = Regression(x, y)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> solver.predicted()
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.jacobian(solver.p_)
array([[ 0.,  1.],
       [ 1.,  1.],
       [ 2.,  1.],
       [ 3.,  1.],
       [ 4.,  1.],
       [ 5.,  1.]])

Polynomial fit
++++++++++++++

A more complicated example would be to implement a generic polynomial fit.

>>> class PolynomialRegression(Misfit):
...     "Perform a polynomial regression"
...     def __init__(self, x, y, degree):
...         super(PolynomialRegression, self).__init__(
...             data=y, nparams=degree + 1, islinear=True)
...         self.x = x
...         self.degree = degree
...     def predicted(self, p):
...         return sum(p[i]*self.x**i  for i in xrange(self.nparams))
...     def jacobian(self, p):
...         jac = np.ones((self.ndata, self.nparams))
...         for i in xrange(self.nparams):
...             jac[:, i] = self.x**i
...         return jac
>>> solver = PolynomialRegression(x, y, 1)
>>> solver.fit().estimate_
array([ 5.,  2.])
>>> solver.jacobian(solver.p_)
array([[ 1.,  0.],
       [ 1.,  1.],
       [ 1.,  2.],
       [ 1.,  3.],
       [ 1.,  4.],
       [ 1.,  5.]])
>>> # Use a second order polynomial
>>> y = 0.1*x**2 + 3*x + 6
>>> solver = PolynomialRegression(x, y, 2).fit()
>>> solver.estimate_
array([ 6. ,  3. ,  0.1])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.jacobian(solver.p_)
array([[  1.,   0.,   0.],
       [  1.,   1.,   1.],
       [  1.,   2.,   4.],
       [  1.,   3.,   9.],
       [  1.,   4.,  16.],
       [  1.,   5.,  25.]])

You can also configure the solver to use a different (non-linear) optimization
method:

>>> # Configure solver to use the Levemberg-Marquardt method
>>> solver.config('levmarq', initial=[1, 1, 1]).fit().estimate_
array([ 6. ,  3. ,  0.1])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> # Configure solver to use Ant Colony Optimization
>>> solver.config('acor', bounds=[0, 10, 0, 10, 0, 1], seed=0).fit().estimate_
array([ 6.03600966,  2.95744428,  0.10781796])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)

Non-linear Gaussian fit
+++++++++++++++++++++++

In this example, I want to fit an equation of the form

.. math::

    f(x) = a\exp(-b(x + c)^{2})

Function *f* is non-linear with respect to inversion parameters *a, b, c*.
Thus, we need to configure the solver and choose an optimization method before
we can call ``fit()``.

>>> class GaussianFit(Misfit):
...     def __init__(self, x, y):
...         super(GaussianFit, self).__init__(
...             data=y, nparams=3, islinear=False)
...         self.x = x
...     def predicted(self, p):
...         a, b, c = p
...         return a*np.exp(-b*(self.x + c)**2)
...     def jacobian(self, p):
...         a, b, c = p
...         jac = np.ones((self.ndata, self.nparams))
...         var = self.x + c
...         exponential = np.exp(-b*var**2)
...         jac[:, 0] = exponential
...         jac[:, 1] = -a*exponential*(var**2)
...         jac[:, 2] = -a*exponential*2*b*var
...         return jac
>>> x = np.linspace(0, 10, 1000)
>>> a, b, c = 100, 0.1, -2
>>> y = a*np.exp(-b*(x + c)**2)
>>> # Non-linear solvers have to be configured. Lets use Levemberg-Marquardt.
>>> solver = GaussianFit(x, y).config('levmarq', initial=[1, 1, 1]).fit()
>>> solver.estimate_
array([ 100. ,    0.1,   -2. ])
>>> np.all(np.abs(solver.residuals()) < 10**-10)
True

Multiple data sets (joint inversion)
++++++++++++++++++++++++++++++++++++

Sometimes multiple data types depend on the same parameters (e.g., gravity
and gravity gradients depend of density). In these cases, the inversion of both
datasets can be performed simultaneously by simply adding the Misfits together.

Lets go back to the regression example and pretend that the same linear
equation can represent 2 types of data, ``y1`` and ``y2``:

>>> x1 = np.linspace(0, 5, 6)
>>> y1 = 2*x1 + 5
>>> y1
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> x2 = np.linspace(100, 1000, 4)
>>> y2 = 2*x2 + 5
>>> y2
array([  205.,   805.,  1405.,  2005.])
>>> # Simply sum the 2 classes! Note: the order matters!
>>> solver = Regression(x1, y1) + Regression(x2, y2)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> # index the solver to get each Regression
>>> solver[0].predicted()
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> solver[1].predicted()
array([  205.,   805.,  1405.,  2005.])
>>> np.abs(solver[0].residuals()) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> np.abs(solver[1].residuals()) < 10**-10
array([ True,  True,  True,  True], dtype=bool)
>>> # We can configure the joint solver just like any other
>>> solver.config('levmarq', initial=[1, 1]).fit().estimate_
array([ 2.,  5.])

This can also be expanded to 3 or more data types:

>>> x3 = np.linspace(10, 11, 6)
>>> y3 = 2*x3 + 5
>>> y3
array([ 25. ,  25.4,  25.8,  26.2,  26.6,  27. ])
>>> solver = Regression(x1, y1) + Regression(x2, y2) + Regression(x3, y3)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> y1pred, y2pred, y3pred = [s.predicted() for s in solver]
>>> y1pred
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> y2pred
array([  205.,   805.,  1405.,  2005.])
>>> y3pred
array([ 25. ,  25.4,  25.8,  26.2,  26.6,  27. ])
>>> res1, res2, res3 = [s.residuals() for s in solver]
>>> np.abs(res1) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> np.abs(res2) < 10**-10
array([ True,  True,  True,  True], dtype=bool)
>>> np.abs(res3) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.config('levmarq', initial=[1, 1]).fit().estimate_
array([ 2.,  5.])

----

"""
<commit_msg>Capitalize some comments<commit_after>"""
Everything you need to solve inverse problems!

This package provides the basic building blocks to implement inverse problem
solvers. The main class for this is :class:`~fatiando.inversion.base.Misfit`.
It represents a data-misfit function and contains various tools to fit a
model to some data. All you have to do is implement methods to calculate the
predicted (or modeled) data and (optionally) the Jacobian (or sensitivity)
matrix. With only that, you have access to a range of optimization methods,
regularization, joint inversion, etc.

Modules
-------

* :mod:`~fatiando.inversion.base`: Base classes for building inverse problem
  solvers
* :mod:`~fatiando.inversion.regularization`: Classes for common regularizing
  functions and base classes for building new ones
* :mod:`~fatiando.inversion.solvers`: Functions for several optimization
  methods (used by :class:`~fatiando.inversion.base.Misfit`)

Have a look at the examples bellow on how to use the package. More geophysical
examples include :mod:`fatiando.seismic.srtomo`,
:mod:`fatiando.seismic.profile`,
:mod:`fatiando.gravmag.basin2d`,
:mod:`fatiando.gravmag.eqlayer`,
and
:mod:`fatiando.gravmag.euler`.

Examples
--------

Linear Regression
+++++++++++++++++

Here is an example of how to implement a simple linear regression using the
:class:`~fatiando.inversion.base.Misfit` class.

What we want to do is fit :math:`f(a, b, x) = y = ax + b` to find a and b.
Putting a and b in a parameter vector `p = [a, b]` we get:

.. math::

    \mathbf{d} = \mathbf{A} \mathbf{p}

where :math:`\mathbf{d}` is the data vector containing all the values of y
and :math:`\mathbf{A}` is the Jacobian matrix with the values of x in the first
column and 1 in the second column.

All we have to do to implement a solver for this problem is write the
`predicted` (to calculate y from values of a and b) and `jacobian` (to
calculate the Jacobian matrix):

>>> import numpy as np
>>> from fatiando.inversion.base import Misfit
>>> class Regression(Misfit):
...     "Perform a linear regression"
...     def __init__(self, x, y):
...         super(Regression, self).__init__(data=y, nparams=2, islinear=True)
...         self.x = x
...     def predicted(self, p):
...         a, b = p
...         return a*self.x + b
...     def jacobian(self, p):
...         jac = np.ones((self.ndata, self.nparams))
...         jac[:, 0] = self.x
...         return jac
>>> x = np.linspace(0, 5, 6)
>>> x
array([ 0.,  1.,  2.,  3.,  4.,  5.])
>>> y = 2*x + 5
>>> y
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> solver = Regression(x, y)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> solver.predicted()
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.jacobian(solver.p_)
array([[ 0.,  1.],
       [ 1.,  1.],
       [ 2.,  1.],
       [ 3.,  1.],
       [ 4.,  1.],
       [ 5.,  1.]])

Polynomial fit
++++++++++++++

A more complicated example would be to implement a generic polynomial fit.

>>> class PolynomialRegression(Misfit):
...     "Perform a polynomial regression"
...     def __init__(self, x, y, degree):
...         super(PolynomialRegression, self).__init__(
...             data=y, nparams=degree + 1, islinear=True)
...         self.x = x
...         self.degree = degree
...     def predicted(self, p):
...         return sum(p[i]*self.x**i  for i in xrange(self.nparams))
...     def jacobian(self, p):
...         jac = np.ones((self.ndata, self.nparams))
...         for i in xrange(self.nparams):
...             jac[:, i] = self.x**i
...         return jac
>>> solver = PolynomialRegression(x, y, 1)
>>> solver.fit().estimate_
array([ 5.,  2.])
>>> solver.jacobian(solver.p_)
array([[ 1.,  0.],
       [ 1.,  1.],
       [ 1.,  2.],
       [ 1.,  3.],
       [ 1.,  4.],
       [ 1.,  5.]])
>>> # Use a second order polynomial
>>> y = 0.1*x**2 + 3*x + 6
>>> solver = PolynomialRegression(x, y, 2).fit()
>>> solver.estimate_
array([ 6. ,  3. ,  0.1])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.jacobian(solver.p_)
array([[  1.,   0.,   0.],
       [  1.,   1.,   1.],
       [  1.,   2.,   4.],
       [  1.,   3.,   9.],
       [  1.,   4.,  16.],
       [  1.,   5.,  25.]])

You can also configure the solver to use a different (non-linear) optimization
method:

>>> # Configure solver to use the Levemberg-Marquardt method
>>> solver.config('levmarq', initial=[1, 1, 1]).fit().estimate_
array([ 6. ,  3. ,  0.1])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> # Configure solver to use Ant Colony Optimization
>>> solver.config('acor', bounds=[0, 10, 0, 10, 0, 1], seed=0).fit().estimate_
array([ 6.03600966,  2.95744428,  0.10781796])
>>> np.abs(solver.residuals()) < 10**10
array([ True,  True,  True,  True,  True,  True], dtype=bool)

Non-linear Gaussian fit
+++++++++++++++++++++++

In this example, I want to fit an equation of the form

.. math::

    f(x) = a\exp(-b(x + c)^{2})

Function *f* is non-linear with respect to inversion parameters *a, b, c*.
Thus, we need to configure the solver and choose an optimization method before
we can call ``fit()``.

>>> class GaussianFit(Misfit):
...     def __init__(self, x, y):
...         super(GaussianFit, self).__init__(
...             data=y, nparams=3, islinear=False)
...         self.x = x
...     def predicted(self, p):
...         a, b, c = p
...         return a*np.exp(-b*(self.x + c)**2)
...     def jacobian(self, p):
...         a, b, c = p
...         jac = np.ones((self.ndata, self.nparams))
...         var = self.x + c
...         exponential = np.exp(-b*var**2)
...         jac[:, 0] = exponential
...         jac[:, 1] = -a*exponential*(var**2)
...         jac[:, 2] = -a*exponential*2*b*var
...         return jac
>>> x = np.linspace(0, 10, 1000)
>>> a, b, c = 100, 0.1, -2
>>> y = a*np.exp(-b*(x + c)**2)
>>> # Non-linear solvers have to be configured. Lets use Levemberg-Marquardt.
>>> solver = GaussianFit(x, y).config('levmarq', initial=[1, 1, 1]).fit()
>>> solver.estimate_
array([ 100. ,    0.1,   -2. ])
>>> np.all(np.abs(solver.residuals()) < 10**-10)
True

Multiple data sets (joint inversion)
++++++++++++++++++++++++++++++++++++

Sometimes multiple data types depend on the same parameters (e.g., gravity
and gravity gradients depend of density). In these cases, the inversion of both
datasets can be performed simultaneously by simply adding the Misfits together.

Lets go back to the regression example and pretend that the same linear
equation can represent 2 types of data, ``y1`` and ``y2``:

>>> x1 = np.linspace(0, 5, 6)
>>> y1 = 2*x1 + 5
>>> y1
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> x2 = np.linspace(100, 1000, 4)
>>> y2 = 2*x2 + 5
>>> y2
array([  205.,   805.,  1405.,  2005.])
>>> # Simply sum the 2 classes
>>> solver = Regression(x1, y1) + Regression(x2, y2)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> # Index the solver to get each Regression
>>> solver[0].predicted()
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> solver[1].predicted()
array([  205.,   805.,  1405.,  2005.])
>>> np.abs(solver[0].residuals()) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> np.abs(solver[1].residuals()) < 10**-10
array([ True,  True,  True,  True], dtype=bool)
>>> # We can configure the joint solver just like any other
>>> solver.config('levmarq', initial=[1, 1]).fit().estimate_
array([ 2.,  5.])

This can also be expanded to 3 or more data types:

>>> x3 = np.linspace(10, 11, 6)
>>> y3 = 2*x3 + 5
>>> y3
array([ 25. ,  25.4,  25.8,  26.2,  26.6,  27. ])
>>> solver = Regression(x1, y1) + Regression(x2, y2) + Regression(x3, y3)
>>> solver.fit().estimate_
array([ 2.,  5.])
>>> y1pred, y2pred, y3pred = [s.predicted() for s in solver]
>>> y1pred
array([  5.,   7.,   9.,  11.,  13.,  15.])
>>> y2pred
array([  205.,   805.,  1405.,  2005.])
>>> y3pred
array([ 25. ,  25.4,  25.8,  26.2,  26.6,  27. ])
>>> res1, res2, res3 = [s.residuals() for s in solver]
>>> np.abs(res1) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> np.abs(res2) < 10**-10
array([ True,  True,  True,  True], dtype=bool)
>>> np.abs(res3) < 10**-10
array([ True,  True,  True,  True,  True,  True], dtype=bool)
>>> solver.config('levmarq', initial=[1, 1]).fit().estimate_
array([ 2.,  5.])

----

"""
<|endoftext|>


<commit_before>#!/usr/bin/python

import socket
import os
import sys
import struct
import time

ETYPE_VALUE_MAX = 8000

def main():
	SERVER_ADDR = "125.7.128.xx"
	SERVER_PORT = 8283
	print "   try connecting .... "

	sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
	ret_conn = sock.connect( ( SERVER_ADDR, SERVER_PORT ) )

	readBuffer = ""
	receivedLines = []
	sendLines = []

	while True:
		# provide buffer
		recv = sock.recv( 1024 )
		readBuffer += recv

		print recv

		# consume buffer
		l = readBuffer.split( ":" )

		if ( len(l) < 2 ):
			pass # nothing to do
		else:
			# dump
			#print >> sys.stderr, "*readBuffer='" + readBuffer + "'"

			#
			readBuffer = ""

			# check last
			e = l[-1]
			l = l[:-1]
			if e != "":
				readBuffer = e
				# dump
				#print >> sys.stderr, "+readBuffer='" + readBuffer + "'"

			# check first
			s = l[0]
			if s.startswith( "Welcome" ):
				s = s[ len( "Welcome"): ]
				l[0] = s

			#
			receivedLines += l

			#
			#for s in l:
			#	print >> sys.stderr, "+line='" + s + "'"

		# make packets
		for s in receivedLines:
			#
			if len( s ) < 48:
				print >> sys.stderr, "wrong data:" + s
				print >> sys.stderr, "dump receivedLines"
				for ss in receivedLines:
					print >> sys.stderr, ss

			assert len(s)>=48

			# common
			head = s[:20]
			type = s[20:24]
			serialID = s[24:36]
			nodeID = s[36:40]
			seq = s[40:44]
			battery = s[44:48]

			#
			if type == "0064": # TH
				temperature = bigEndian( s[48:52] )
				humidity = bigEndian( s[52:56] )
				light = bigEndian( s[56:60] )

				# T
				v1 = -39.6 + 0.01 * temperature

				# H
				tmp = -4 + 0.0405 * humidity + (-0.0000028) * humidity * humidity
                            	v2 = (v1 - 25) * (0.01 + 0.00008 * humidity) + tmp

				# L
				tmp = (light * 100) / 75
                            	v3 = tmp * 10

				#
				t = int( time.time() )
				print "thl.temperature %d %f nodeid=%d" % ( t, v1, bigEndian( nodeID ) )
				print "thl.humidity %d %f nodeid=%d" % ( t, v2, bigEndian( nodeID ) )
				print "thl.light %d %f nodeid=%d" % ( t, v3, bigEndian( nodeID ) )

			elif type == "0065":# PIR
				pass # ignore this.

			elif type == "0066":# CO2
				ppm = s[48:52]

				t = int( time.time() )
				tmp = float( bigEndian( ppm ) )
				value = float( 1.5 * ( tmp  / 4086 ) * 2 * 1000 )

				print "co2.ppm %d %f nodeid=%d" % ( t, value, bigEndian( nodeID ) )

			elif type == "006D" or type == "006d": # SPlug
				#current = s[48:54]
				#t_current = s[60:66]
				rawData = s[54:60]
				tmp = bigEndian( rawData )
				if tmp > 15728640:
					tmp = 0
				else:
					tmp = float( tmp / 4.127 / 10 )

				watt = tmp
				t = int( time.time() )
				print "splug.watt %d %f nodeid=%d" % ( t, watt, bigEndian( nodeID ) )

			elif type == "00D3" or type == "00d3": # etype
				# length check
				if len( s ) < 72:
					print >> sys.stderr, "ignore too short data for etype:" + s
					continue

				t_current = s[48:56]
				current = s[64:72]

				current = toFloat( swapBytes( current ) )
				t_current = littleEndian( swapBytes( t_current ) )
				nodeID = bigEndian( nodeID )

			else:
				print >> sys.stderr, "Invalid type:" + type
				pass

		# clear
		receivedLines = []
		sys.stdout.flush()

if __name__ == "__main__":
	main()

<commit_msg>Update sock_conn_test.py<commit_after>#!/usr/bin/python

import socket
import os
import sys
import struct
import time

ETYPE_VALUE_MAX = 8000

def main():
	SERVER_ADDR = "125.xx.xx.41"
	SERVER_PORT = 8283
	print "   try connecting .... "

	sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
	ret_conn = sock.connect( ( SERVER_ADDR, SERVER_PORT ) )

	readBuffer = ""
	receivedLines = []
	sendLines = []

	while True:
		# provide buffer
		recv = sock.recv( 1024 )
		readBuffer += recv

		print recv

		# consume buffer
		l = readBuffer.split( ":" )

		if ( len(l) < 2 ):
			pass # nothing to do
		else:
			# dump
			#print >> sys.stderr, "*readBuffer='" + readBuffer + "'"

			#
			readBuffer = ""

			# check last
			e = l[-1]
			l = l[:-1]
			if e != "":
				readBuffer = e
				# dump
				#print >> sys.stderr, "+readBuffer='" + readBuffer + "'"

			# check first
			s = l[0]
			if s.startswith( "Welcome" ):
				s = s[ len( "Welcome"): ]
				l[0] = s

			#
			receivedLines += l

			#
			#for s in l:
			#	print >> sys.stderr, "+line='" + s + "'"

		# make packets
		for s in receivedLines:
			#
			if len( s ) < 48:
				print >> sys.stderr, "wrong data:" + s
				print >> sys.stderr, "dump receivedLines"
				for ss in receivedLines:
					print >> sys.stderr, ss

			assert len(s)>=48

			# common
			head = s[:20]
			type = s[20:24]
			serialID = s[24:36]
			nodeID = s[36:40]
			seq = s[40:44]
			battery = s[44:48]

			#
			if type == "0064": # TH
				temperature = bigEndian( s[48:52] )
				humidity = bigEndian( s[52:56] )
				light = bigEndian( s[56:60] )

				# T
				v1 = -39.6 + 0.01 * temperature

				# H
				tmp = -4 + 0.0405 * humidity + (-0.0000028) * humidity * humidity
                            	v2 = (v1 - 25) * (0.01 + 0.00008 * humidity) + tmp

				# L
				tmp = (light * 100) / 75
                            	v3 = tmp * 10

				#
				t = int( time.time() )
				print "thl.temperature %d %f nodeid=%d" % ( t, v1, bigEndian( nodeID ) )
				print "thl.humidity %d %f nodeid=%d" % ( t, v2, bigEndian( nodeID ) )
				print "thl.light %d %f nodeid=%d" % ( t, v3, bigEndian( nodeID ) )

			elif type == "0065":# PIR
				pass # ignore this.

			elif type == "0066":# CO2
				ppm = s[48:52]

				t = int( time.time() )
				tmp = float( bigEndian( ppm ) )
				value = float( 1.5 * ( tmp  / 4086 ) * 2 * 1000 )

				print "co2.ppm %d %f nodeid=%d" % ( t, value, bigEndian( nodeID ) )

			elif type == "006D" or type == "006d": # SPlug
				#current = s[48:54]
				#t_current = s[60:66]
				rawData = s[54:60]
				tmp = bigEndian( rawData )
				if tmp > 15728640:
					tmp = 0
				else:
					tmp = float( tmp / 4.127 / 10 )

				watt = tmp
				t = int( time.time() )
				print "splug.watt %d %f nodeid=%d" % ( t, watt, bigEndian( nodeID ) )

			elif type == "00D3" or type == "00d3": # etype
				# length check
				if len( s ) < 72:
					print >> sys.stderr, "ignore too short data for etype:" + s
					continue

				t_current = s[48:56]
				current = s[64:72]

				current = toFloat( swapBytes( current ) )
				t_current = littleEndian( swapBytes( t_current ) )
				nodeID = bigEndian( nodeID )

			else:
				print >> sys.stderr, "Invalid type:" + type
				pass

		# clear
		receivedLines = []
		sys.stdout.flush()

if __name__ == "__main__":
	main()

<|endoftext|>


<commit_before>#!/usr/bin/env python
#***************************************************************************
#
#   Copyright (c) 2015 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
#    used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#***************************************************************************/

#
# @author Andreas Antener <andreas@uaventure.com>
#
PKG = 'px4'

import sys
import unittest
import rospy
import math

from numpy import linalg
import numpy as np

from px4.msg import vehicle_control_mode
from std_msgs.msg import Header
from std_msgs.msg import Float64
from geometry_msgs.msg import PoseStamped, Quaternion
from tf.transformations import quaternion_from_euler
from mavros.srv import CommandBool

from manual_input import ManualInput

#
# Tests flying a path in offboard control by sending position setpoints
# over MAVROS.
#
# For the test to be successful it needs to reach all setpoints in a certain time.
# FIXME: add flight path assertion (needs transformation from ROS frame to NED)
#
class MavrosOffboardAttctlTest(unittest.TestCase):

    def setUp(self):
        rospy.init_node('test_node', anonymous=True)
        rospy.wait_for_service('iris/mavros/cmd/arming', 30)
        rospy.Subscriber('iris/vehicle_control_mode', vehicle_control_mode, self.vehicle_control_mode_callback)
        rospy.Subscriber("iris/mavros/position/local", PoseStamped, self.position_callback)
        self.pub_att = rospy.Publisher('iris/mavros/setpoint/attitude', PoseStamped, queue_size=10)
        self.pub_thr = rospy.Publisher('iris/mavros/setpoint/att_throttle', Float64, queue_size=10)
        self.rate = rospy.Rate(20) # 10hz
        self.has_pos = False
        self.control_mode = vehicle_control_mode()
        self.local_position = PoseStamped()

    #
    # General callback functions used in tests
    #
    def position_callback(self, data):
        self.has_pos = True
        self.local_position = data

    def vehicle_control_mode_callback(self, data):
        self.control_mode = data

    #
    # Test offboard position control
    #
    def test_attctl(self):
        # set some attitude and thrust
        att = PoseStamped()
        att.header = Header()
        att.header.frame_id = "base_footprint"
        att.header.stamp = rospy.Time.now()
        quaternion = quaternion_from_euler(0.15, 0.15, 0)
        att.pose.orientation = Quaternion(*quaternion)

        throttle = Float64()
        throttle.data = 0.6

        # does it cross expected boundaries in X seconds?
        count = 0
        timeout = 120
        while count < timeout:
            # update timestamp for each published SP
            att.header.stamp = rospy.Time.now()
            self.pub_att.publish(att)
            self.rate.sleep()
            self.pub_thr.publish(throttle)
            self.rate.sleep()

            if (self.local_position.pose.position.x > 5
                and self.local_position.pose.position.z > 5
                and self.local_position.pose.position.y < -5):
                break
            count = count + 1

        self.assertTrue(self.control_mode.flag_armed, "flag_armed is not set")
        self.assertTrue(self.control_mode.flag_control_attitude_enabled, "flag_control_attitude_enabled is not set")
        self.assertTrue(self.control_mode.flag_control_offboard_enabled, "flag_control_offboard_enabled is not set")
        self.assertTrue(count < timeout, "took too long to cross boundaries")

if __name__ == '__main__':
    import rostest
    rostest.rosrun(PKG, 'mavros_offboard_attctl_test', MavrosOffboardAttctlTest)
    #unittest.main()
<commit_msg>fixed some warnings and updated comments<commit_after>#!/usr/bin/env python
#***************************************************************************
#
#   Copyright (c) 2015 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
#    used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#***************************************************************************/

#
# @author Andreas Antener <andreas@uaventure.com>
#
PKG = 'px4'

import unittest
import rospy

from px4.msg import vehicle_control_mode
from std_msgs.msg import Header
from std_msgs.msg import Float64
from geometry_msgs.msg import PoseStamped, Quaternion
from tf.transformations import quaternion_from_euler

#
# Tests flying a path in offboard control by sending attitude and thrust setpoints
# over MAVROS.
#
# For the test to be successful it needs to cross a certain boundary in time.
#
class MavrosOffboardAttctlTest(unittest.TestCase):

    def setUp(self):
        rospy.init_node('test_node', anonymous=True)
        rospy.wait_for_service('iris/mavros/cmd/arming', 30)
        rospy.Subscriber('iris/vehicle_control_mode', vehicle_control_mode, self.vehicle_control_mode_callback)
        rospy.Subscriber("iris/mavros/position/local", PoseStamped, self.position_callback)
        self.pub_att = rospy.Publisher('iris/mavros/setpoint/attitude', PoseStamped, queue_size=10)
        self.pub_thr = rospy.Publisher('iris/mavros/setpoint/att_throttle', Float64, queue_size=10)
        self.rate = rospy.Rate(20) # 10hz
        self.has_pos = False
        self.control_mode = vehicle_control_mode()
        self.local_position = PoseStamped()

    #
    # General callback functions used in tests
    #
    def position_callback(self, data):
        self.has_pos = True
        self.local_position = data

    def vehicle_control_mode_callback(self, data):
        self.control_mode = data

    #
    # Test offboard position control
    #
    def test_attctl(self):
        # set some attitude and thrust
        att = PoseStamped()
        att.header = Header()
        att.header.frame_id = "base_footprint"
        att.header.stamp = rospy.Time.now()
        quaternion = quaternion_from_euler(0.15, 0.15, 0)
        att.pose.orientation = Quaternion(*quaternion)

        throttle = Float64()
        throttle.data = 0.6

        # does it cross expected boundaries in X seconds?
        count = 0
        timeout = 120
        while count < timeout:
            # update timestamp for each published SP
            att.header.stamp = rospy.Time.now()

            self.pub_att.publish(att)
            self.rate.sleep() # I'm guessing this is necessary to prevent timing issues
            self.pub_thr.publish(throttle)
            self.rate.sleep()

            if (self.local_position.pose.position.x > 5
                    and self.local_position.pose.position.z > 5
                    and self.local_position.pose.position.y < -5):
                break
            count = count + 1

        self.assertTrue(self.control_mode.flag_armed, "flag_armed is not set")
        self.assertTrue(self.control_mode.flag_control_attitude_enabled, "flag_control_attitude_enabled is not set")
        self.assertTrue(self.control_mode.flag_control_offboard_enabled, "flag_control_offboard_enabled is not set")
        self.assertTrue(count < timeout, "took too long to cross boundaries")

if __name__ == '__main__':
    import rostest
    rostest.rosrun(PKG, 'mavros_offboard_attctl_test', MavrosOffboardAttctlTest)
    #unittest.main()
<|endoftext|>


<commit_before>import logging

import discord
from discord.ext import commands
import mcstatus
from ..tools import Cog, get_json
import re

log = logging.getLogger(__name__)

REMOVE_SECTION = re.compile("(\U000000a7.)")

class Minecraft(Cog):
    @commands.command()
    async def mcStatus(self, ctx: commands.Context, *, server: str = None):
        # a!mcStatus
        # -> guild.Minecraft
        if server is None and ctx.guild is not None:
            gd = await self.bot.db.get_guild_data(ctx.guild.id)
            server = gd.config.minecraft
            if server is None:
                return await ctx.send(
                    "please provide a server via argument or with `config set guild.Minecraft <SERVER>`"
                )
        try:

            mcserver = mcstatus.MinecraftServer.lookup(server)
            status = await mcserver.async_status()
        except Exception:
            return await ctx.send("an error occured, the server may be down..")

        embed = discord.Embed(description=REMOVE_SECTION.sub('', status.description['text']))

        embed.title = f"{mcserver.host}:{mcserver.port}"

        if len(status.players.sample):
            embed.add_field(name="players", value='\n'.join([player.name for player in status.players.sample]))
        embed.add_field(name="Online / Max", value=f"{status.players.online} / {status.players.max}")

        await ctx.send(embed=embed)

def setup(bot):
    bot.add_cog(Minecraft(bot))
<commit_msg>auto: format python code<commit_after>import logging
import re

import discord
import mcstatus
from discord.ext import commands

from ..tools import Cog, get_json

log = logging.getLogger(__name__)

REMOVE_SECTION = re.compile("(\U000000a7.)")

class Minecraft(Cog):
    @commands.command()
    async def mcStatus(self, ctx: commands.Context, *, server: str = None):
        # a!mcStatus
        # -> guild.Minecraft
        if server is None and ctx.guild is not None:
            gd = await self.bot.db.get_guild_data(ctx.guild.id)
            server = gd.config.minecraft
            if server is None:
                return await ctx.send(
                    "please provide a server via argument or with `config set guild.Minecraft <SERVER>`"
                )
        try:

            mcserver = mcstatus.MinecraftServer.lookup(server)
            status = await mcserver.async_status()
        except Exception:
            return await ctx.send("an error occured, the server may be down..")

        embed = discord.Embed(description=REMOVE_SECTION.sub('', status.description['text']))

        embed.title = f"{mcserver.host}:{mcserver.port}"

        if len(status.players.sample):
            embed.add_field(name="players", value='\n'.join([player.name for player in status.players.sample]))
        embed.add_field(name="Online / Max", value=f"{status.players.online} / {status.players.max}")

        await ctx.send(embed=embed)

def setup(bot):
    bot.add_cog(Minecraft(bot))
<|endoftext|>


<commit_before>##############################################################################
#
# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved.
#                    Fabien Pinckaers <fp@tiny.Be>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

import ir_sequence
import ir_ui_menu
import ir_ui_view
import ir_actions
import ir_default
import ir_model
import ir_report_custom
import ir_attachment
import ir_cron
import ir_values
import ir_translation
import ir_exports
import workflow
import ir_board
<commit_msg>Dashboard for project users and managers Statistics and planning module on analytic account Statistics on timesheets<commit_after>##############################################################################
#
# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved.
#                    Fabien Pinckaers <fp@tiny.Be>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

import ir_sequence
import ir_ui_menu
import ir_ui_view
import ir_actions
import ir_default
import ir_model
import ir_report_custom
import ir_attachment
import ir_cron
import ir_values
import ir_translation
import ir_exports
import workflow
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright 2014 matrix.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from twisted.internet import defer

from synapse.api.constants import Membership
from synapse.api.events.room import RoomTopicEvent
from synapse.api.errors import RoomError
from synapse.streams.config import PaginationConfig
from ._base import BaseRoomHandler

import logging

logger = logging.getLogger(__name__)

class MessageHandler(BaseRoomHandler):

    def __init__(self, hs):
        super(MessageHandler, self).__init__(hs)
        self.hs = hs
        self.clock = hs.get_clock()
        self.event_factory = hs.get_event_factory()

    @defer.inlineCallbacks
    def get_message(self, msg_id=None, room_id=None, sender_id=None,
                    user_id=None):
        """ Retrieve a message.

        Args:
            msg_id (str): The message ID to obtain.
            room_id (str): The room where the message resides.
            sender_id (str): The user ID of the user who sent the message.
            user_id (str): The user ID of the user making this request.
        Returns:
            The message, or None if no message exists.
        Raises:
            SynapseError if something went wrong.
        """
        yield self.auth.check_joined_room(room_id, user_id)

        # Pull out the message from the db
#        msg = yield self.store.get_message(
#            room_id=room_id,
#            msg_id=msg_id,
#            user_id=sender_id
#        )

        # TODO (erikj): Once we work out the correct c-s api we need to think on how to do this.

        defer.returnValue(None)

    @defer.inlineCallbacks
    def send_message(self, event=None, suppress_auth=False, stamp_event=True):
        """ Send a message.

        Args:
            event : The message event to store.
            suppress_auth (bool) : True to suppress auth for this message. This
            is primarily so the home server can inject messages into rooms at
            will.
            stamp_event (bool) : True to stamp event content with server keys.
        Raises:
            SynapseError if something went wrong.
        """
        # TODO(paul): Why does 'event' not have a 'user' object?
        user = self.hs.parse_userid(event.user_id)
        assert(user.is_mine)

        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        if not suppress_auth:
            yield self.auth.check(event, snapshot, raises=True)

        yield self._on_new_room_event(event, snapshot)

        self.hs.get_handlers().presence_handler.bump_presence_active_time(
            user
        )

    @defer.inlineCallbacks
    def get_messages(self, user_id=None, room_id=None, pagin_config=None,
                     feedback=False):
        """Get messages in a room.

        Args:
            user_id (str): The user requesting messages.
            room_id (str): The room they want messages from.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config rules to apply, if any.
            feedback (bool): True to get compressed feedback with the messages
        Returns:
            dict: Pagination API results
        """
        yield self.auth.check_joined_room(room_id, user_id)

        data_source = self.hs.get_event_sources().sources["room"]

        if not pagin_config.from_token:
            pagin_config.from_token = yield self.hs.get_event_sources().get_current_token()

        user = self.hs.parse_userid(user_id)

        events, next_token = yield data_source.get_pagination_rows(
            user, pagin_config, room_id
        )

        chunk = {
            "chunk": [e.get_dict() for e in events],
            "start": pagin_config.from_token.to_string(),
            "end": next_token.to_string(),
        }

        defer.returnValue(chunk)

    @defer.inlineCallbacks
    def store_room_data(self, event=None, stamp_event=True):
        """ Stores data for a room.

        Args:
            event : The room path event
            stamp_event (bool) : True to stamp event content with server keys.
        Raises:
            SynapseError if something went wrong.
        """

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        yield self.auth.check(event, snapshot, raises=True)

        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        yield self.state_handler.handle_new_event(event, snapshot)

        yield self._on_new_room_event(event, snapshot)

    @defer.inlineCallbacks
    def get_room_data(self, user_id=None, room_id=None,
                      event_type=None, state_key="",
                      public_room_rules=[],
                      private_room_rules=["join"]):
        """ Get data from a room.

        Args:
            event : The room path event
            public_room_rules : A list of membership states the user can be in,
            in order to read this data IN A PUBLIC ROOM. An empty list means
            'any state'.
            private_room_rules : A list of membership states the user can be
            in, in order to read this data IN A PRIVATE ROOM. An empty list
            means 'any state'.
        Returns:
            The path data content.
        Raises:
            SynapseError if something went wrong.
        """
        if event_type == RoomTopicEvent.TYPE:
            # anyone invited/joined can read the topic
            private_room_rules = ["invite", "join"]

        # does this room exist
        room = yield self.store.get_room(room_id)
        if not room:
            raise RoomError(403, "Room does not exist.")

        # does this user exist in this room
        member = yield self.store.get_room_member(
            room_id=room_id,
            user_id="" if not user_id else user_id)

        member_state = member.membership if member else None

        if room.is_public and public_room_rules:
            # make sure the user meets public room rules
            if member_state not in public_room_rules:
                raise RoomError(403, "Member does not meet public room rules.")
        elif not room.is_public and private_room_rules:
            # make sure the user meets private room rules
            if member_state not in private_room_rules:
                raise RoomError(
                    403, "Member does not meet private room rules.")

        data = yield self.store.get_current_state(
            room_id, event_type, state_key
        )
        defer.returnValue(data)

    @defer.inlineCallbacks
    def get_feedback(self, event_id):
        # yield self.auth.check_joined_room(room_id, user_id)

        # Pull out the feedback from the db
        fb = yield self.store.get_feedback(event_id)

        if fb:
            defer.returnValue(fb)
        defer.returnValue(None)

    @defer.inlineCallbacks
    def send_feedback(self, event, stamp_event=True):
        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        yield self.auth.check(event, snapshot, raises=True)

        # store message in db
        yield self._on_new_room_event(event, snapshot)

    @defer.inlineCallbacks
    def snapshot_all_rooms(self, user_id=None, pagin_config=None,
                           feedback=False):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            feedback (bool): True to get feedback along with these messages.
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id,
            membership_list=[Membership.INVITE, Membership.JOIN]
        )

        user = self.hs.parse_userid(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config, None
        )

        limit = pagin_config.limit
        if not limit:
            limit = 10

        for event in room_list:
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
            }

            if event.membership == Membership.INVITE:
                d["inviter"] = event.user_id

            rooms_ret.append(d)

            if event.membership != Membership.JOIN:
                continue
            try:
                messages, token = yield self.store.get_recent_events_for_room(
                    event.room_id,
                    limit=limit,
                    end_token=now_token.room_key,
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])

                d["messages"] = {
                    "chunk": [m.get_dict() for m in messages],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                current_state = yield self.store.get_current_state(
                    event.room_id
                )
                d["state"] = [c.get_dict() for c in current_state]
            except:
                logger.exception("Failed to get snapshot")

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "end": now_token.to_string()
        }

        defer.returnValue(ret)

<commit_msg>Add message to assertion<commit_after># -*- coding: utf-8 -*-
# Copyright 2014 matrix.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from twisted.internet import defer

from synapse.api.constants import Membership
from synapse.api.events.room import RoomTopicEvent
from synapse.api.errors import RoomError
from synapse.streams.config import PaginationConfig
from ._base import BaseRoomHandler

import logging

logger = logging.getLogger(__name__)

class MessageHandler(BaseRoomHandler):

    def __init__(self, hs):
        super(MessageHandler, self).__init__(hs)
        self.hs = hs
        self.clock = hs.get_clock()
        self.event_factory = hs.get_event_factory()

    @defer.inlineCallbacks
    def get_message(self, msg_id=None, room_id=None, sender_id=None,
                    user_id=None):
        """ Retrieve a message.

        Args:
            msg_id (str): The message ID to obtain.
            room_id (str): The room where the message resides.
            sender_id (str): The user ID of the user who sent the message.
            user_id (str): The user ID of the user making this request.
        Returns:
            The message, or None if no message exists.
        Raises:
            SynapseError if something went wrong.
        """
        yield self.auth.check_joined_room(room_id, user_id)

        # Pull out the message from the db
#        msg = yield self.store.get_message(
#            room_id=room_id,
#            msg_id=msg_id,
#            user_id=sender_id
#        )

        # TODO (erikj): Once we work out the correct c-s api we need to think on how to do this.

        defer.returnValue(None)

    @defer.inlineCallbacks
    def send_message(self, event=None, suppress_auth=False, stamp_event=True):
        """ Send a message.

        Args:
            event : The message event to store.
            suppress_auth (bool) : True to suppress auth for this message. This
            is primarily so the home server can inject messages into rooms at
            will.
            stamp_event (bool) : True to stamp event content with server keys.
        Raises:
            SynapseError if something went wrong.
        """
        # TODO(paul): Why does 'event' not have a 'user' object?
        user = self.hs.parse_userid(event.user_id)
        assert user.is_mine, "User must be our own: %s" % (user,)

        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        if not suppress_auth:
            yield self.auth.check(event, snapshot, raises=True)

        yield self._on_new_room_event(event, snapshot)

        self.hs.get_handlers().presence_handler.bump_presence_active_time(
            user
        )

    @defer.inlineCallbacks
    def get_messages(self, user_id=None, room_id=None, pagin_config=None,
                     feedback=False):
        """Get messages in a room.

        Args:
            user_id (str): The user requesting messages.
            room_id (str): The room they want messages from.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config rules to apply, if any.
            feedback (bool): True to get compressed feedback with the messages
        Returns:
            dict: Pagination API results
        """
        yield self.auth.check_joined_room(room_id, user_id)

        data_source = self.hs.get_event_sources().sources["room"]

        if not pagin_config.from_token:
            pagin_config.from_token = yield self.hs.get_event_sources().get_current_token()

        user = self.hs.parse_userid(user_id)

        events, next_token = yield data_source.get_pagination_rows(
            user, pagin_config, room_id
        )

        chunk = {
            "chunk": [e.get_dict() for e in events],
            "start": pagin_config.from_token.to_string(),
            "end": next_token.to_string(),
        }

        defer.returnValue(chunk)

    @defer.inlineCallbacks
    def store_room_data(self, event=None, stamp_event=True):
        """ Stores data for a room.

        Args:
            event : The room path event
            stamp_event (bool) : True to stamp event content with server keys.
        Raises:
            SynapseError if something went wrong.
        """

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        yield self.auth.check(event, snapshot, raises=True)

        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        yield self.state_handler.handle_new_event(event, snapshot)

        yield self._on_new_room_event(event, snapshot)

    @defer.inlineCallbacks
    def get_room_data(self, user_id=None, room_id=None,
                      event_type=None, state_key="",
                      public_room_rules=[],
                      private_room_rules=["join"]):
        """ Get data from a room.

        Args:
            event : The room path event
            public_room_rules : A list of membership states the user can be in,
            in order to read this data IN A PUBLIC ROOM. An empty list means
            'any state'.
            private_room_rules : A list of membership states the user can be
            in, in order to read this data IN A PRIVATE ROOM. An empty list
            means 'any state'.
        Returns:
            The path data content.
        Raises:
            SynapseError if something went wrong.
        """
        if event_type == RoomTopicEvent.TYPE:
            # anyone invited/joined can read the topic
            private_room_rules = ["invite", "join"]

        # does this room exist
        room = yield self.store.get_room(room_id)
        if not room:
            raise RoomError(403, "Room does not exist.")

        # does this user exist in this room
        member = yield self.store.get_room_member(
            room_id=room_id,
            user_id="" if not user_id else user_id)

        member_state = member.membership if member else None

        if room.is_public and public_room_rules:
            # make sure the user meets public room rules
            if member_state not in public_room_rules:
                raise RoomError(403, "Member does not meet public room rules.")
        elif not room.is_public and private_room_rules:
            # make sure the user meets private room rules
            if member_state not in private_room_rules:
                raise RoomError(
                    403, "Member does not meet private room rules.")

        data = yield self.store.get_current_state(
            room_id, event_type, state_key
        )
        defer.returnValue(data)

    @defer.inlineCallbacks
    def get_feedback(self, event_id):
        # yield self.auth.check_joined_room(room_id, user_id)

        # Pull out the feedback from the db
        fb = yield self.store.get_feedback(event_id)

        if fb:
            defer.returnValue(fb)
        defer.returnValue(None)

    @defer.inlineCallbacks
    def send_feedback(self, event, stamp_event=True):
        if stamp_event:
            event.content["hsob_ts"] = int(self.clock.time_msec())

        snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)

        yield self.auth.check(event, snapshot, raises=True)

        # store message in db
        yield self._on_new_room_event(event, snapshot)

    @defer.inlineCallbacks
    def snapshot_all_rooms(self, user_id=None, pagin_config=None,
                           feedback=False):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            feedback (bool): True to get feedback along with these messages.
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id,
            membership_list=[Membership.INVITE, Membership.JOIN]
        )

        user = self.hs.parse_userid(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config, None
        )

        limit = pagin_config.limit
        if not limit:
            limit = 10

        for event in room_list:
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
            }

            if event.membership == Membership.INVITE:
                d["inviter"] = event.user_id

            rooms_ret.append(d)

            if event.membership != Membership.JOIN:
                continue
            try:
                messages, token = yield self.store.get_recent_events_for_room(
                    event.room_id,
                    limit=limit,
                    end_token=now_token.room_key,
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])

                d["messages"] = {
                    "chunk": [m.get_dict() for m in messages],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                current_state = yield self.store.get_current_state(
                    event.room_id
                )
                d["state"] = [c.get_dict() for c in current_state]
            except:
                logger.exception("Failed to get snapshot")

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "end": now_token.to_string()
        }

        defer.returnValue(ret)

<|endoftext|>