<commit_before>__version__ = "4.1.0.0"

import collections
import json
import inspect
from StringIO import StringIO
import bindings.maec_bundle as bundle_binding
import bindings.maec_package as package_binding
from cybox import Entity as cyboxEntity
from cybox.utils import Namespace
from maec.utils import maecMETA

def get_xmlns_string(ns_set):
    """Build a string with 'xmlns' definitions for every namespace in ns_set.

    Arguments:
    - ns_set: a set (or other iterable) of Namespace objects
    """
    xmlns_format = 'xmlns:{0.prefix}="{0.name}"'
    return "\n\t".join([xmlns_format.format(x) for x in ns_set])

def get_schemaloc_string(ns_set):
    """Build a "schemaLocation" string for every namespace in ns_set.

    Arguments:
    - ns_set: a set (or other iterable) of Namespace objects
    """
    schemaloc_format = '{0.name} {0.schema_location}'
    # Only include schemas that have a schema_location defined (for instance,
    # 'xsi' does not.
    return " ".join([schemaloc_format.format(x) for x in ns_set
                     if x.schema_location])

class Entity(object):
    """Base class for all classes in the MAEC SimpleAPI."""

    # By default (unless a particular subclass states otherwise), try to "cast"
    # invalid objects to the correct class using the constructor. Entity
    # subclasses should either provide a "sane" constructor or set this to
    # False.
    _try_cast = True

    def __init__(self):
        self._fields = {}

    @classmethod
    def _get_vars(cls):
        var_list = []
        for (name, obj) in inspect.getmembers(cls, inspect.isdatadescriptor):
            if isinstance(obj, TypedField):
                var_list.append(obj)

        return var_list

    def __eq__(self, other):
        # This fixes some strange behavior where an object isn't equal to
        # itself
        if other is self:
            return True

        # I'm not sure about this, if we want to compare exact classes or if
        # various subclasses will also do (I think not), but for now I'm going
        # to assume they must be equal. - GTB
        if self.__class__ != other.__class__:
            return False

        var_list = self.__class__._get_vars()

        # If there are no TypedFields, assume this class hasn't been
        # "TypedField"-ified, so we don't want these to inadvertently return
        # equal.
        if not var_list:
            return False

        for f in var_list:
            if not f.comparable:
                continue
            if getattr(self, f.attr_name) != getattr(other, f.attr_name):
                return False

        return True

    def __ne__(self, other):
        return not self == other

    def to_obj(self):
        """Default implementation of a to_obj function.

        Subclasses can override this function."""

        entity_obj = self._binding_class()

        for field in self.__class__._get_vars():
            val = getattr(self, field.attr_name)

            if field.multiple and val:
                val = [x.to_obj() for x in val]
            elif isinstance(val, Entity):
                val = val.to_obj()

            setattr(entity_obj, field.name, val)

        self._finalize_obj(entity_obj)

        return entity_obj

    def _finalize_obj(self, entity_obj):
        """Subclasses can define additional items in the binding object.

        `entity_obj` should be modified in place.
        """
        pass

    def to_dict(self):
        """Default implementation of a to_dict function.

        Subclasses can override this function."""

        entity_dict = {}

        for field in self.__class__._get_vars():
            val = getattr(self, field.attr_name)

            if field.multiple and val:
                val = [x.to_dict() for x in val]
            if isinstance(val, Entity):
                val = val.to_dict()

            # Only add non-None objects or non-empty lists
            if val is not None and val != []:
                entity_dict[field.key_name] = val

        self._finalize_dict(entity_dict)

        return entity_dict

    def _finalize_dict(self, entity_dict):
        """Subclasses can define additional items in the dictionary.

        `entity_dict` should be modified in place.
        """
        pass

    @classmethod
    def from_obj(cls, cls_obj=None):
        if not cls_obj:
            return None

        entity = cls()

        for field in cls._get_vars():
            val = getattr(cls_obj, field.name)
            if field.type_:
                if field.multiple and val is not None:
                    val = [field.type_.from_obj(x) for x in val]
                else:
                    val = field.type_.from_obj(val)
            setattr(entity, field.attr_name, val)

        return entity

    @classmethod
    def from_dict(cls, cls_dict=None):
        if cls_dict is None:
            return None

        entity = cls()

        # Shortcut if an actual dict is not provided:
        if not isinstance(cls_dict, dict):
            value = cls_dict
            # Call the class's constructor
            try:
                return cls(value)
            except TypeError:
                raise TypeError("Could not instantiate a %s from a %s: %s" %
                                (cls, type(value), value))

        for field in cls._get_vars():
            val = cls_dict.get(field.key_name)
            if field.type_:
                if issubclass(field.type_, EntityList):
                    val = field.type_.from_list(val)
                elif field.multiple:
                    if val is not None:
                        val = [field.type_.from_dict(x) for x in val]
                    else:
                        val = []
                else:
                    val = field.type_.from_dict(val)
            setattr(entity, field.attr_name, val)

        return entity

    def to_xml(self, include_namespaces=True, namespace_dict=None,
               pretty=True):
        """Export an object as an XML String.

        :param include_namespaces: whether to include xmlns and
          xsi:schemaLocation attributes on the root element. Set to true by
          default.
        :type include_namespaces: bool
        :param namespace_dict: mapping of additional XML namespaces to prefixes
        :type namespace_dict: dict
        :param pretty: produce readable (``True``) or compact (``False``)
          output. Default is ``True``
        :type pretty: bool
        """
        namespace_def = ""

        if include_namespaces:
            namespace_def = self._get_namespace_def(namespace_dict)

        if not pretty:
            namespace_def = namespace_def.replace('\n\t', ' ')

        s = StringIO()
        self.to_obj().export(s, 0, namespacedef_=namespace_def,
                             pretty_print=pretty)
        return s.getvalue()

    def to_xml_file(self, filename):
        """Export an object to an XML file. Only supports Package or Bundle objects at the moment."""
        out_file  = open(filename, 'w')
        out_file.write("<?xml version='1.0' encoding='UTF-8'?>\n")
        self.to_obj().export(out_file, 0, namespace_def = self._get_namespace_def(namespace_dict))
        out_file.close()

    def to_json(self):
        """Export an object as a JSON string.
        """
        return json.dumps(self.to_dict())

    def _get_namespace_def(self, additional_ns_dict=None):
        # copy necessary namespaces

        namespaces = self._get_namespaces()

        if additional_ns_dict:
            for ns, prefix in additional_ns_dict.iteritems():
                namespaces.update([Namespace(ns, prefix)])

        # if there are any other namepaces, include xsi for "schemaLocation"
        if namespaces:
            namespaces.update([maecMETA.lookup_prefix('xsi')])

        if not namespaces:
            return ""

        namespaces = sorted(namespaces, key=str)

        return ('\n\t' + get_xmlns_string(namespaces) +
                '\n\txsi:schemaLocation="' + get_schemaloc_string(namespaces) +
                '"')

    def _get_namespaces(self, recurse=True):
        nsset = set()

        # Get all _namespaces for parent classes
        namespaces = [x._namespace for x in self.__class__.__mro__
                      if hasattr(x, '_namespace')]

        nsset.update([maecMETA.lookup_namespace(ns) for ns in namespaces])

        #In case of recursive relationships, don't process this item twice
        self.touched = True
        if recurse:
            for x in self._get_children():
                if not hasattr(x, 'touched'):
                    nsset.update(x._get_namespaces())
        del self.touched

        return nsset

    def _get_children(self):
        #TODO: eventually everything should be in _fields, not the top level
        # of vars()
        for k, v in vars(self).items() + self._fields.items():
            if isinstance(v, Entity) or isinstance(v, cyboxEntity):
                yield v
            elif isinstance(v, list):
                for item in v:
                    if isinstance(item, Entity) or isinstance(v, cyboxEntity):
                        yield item

    @classmethod
    def istypeof(cls, obj):
        """Check if `cls` is the type of `obj`

        In the normal case, as implemented here, a simple isinstance check is
        used. However, there are more complex checks possible. For instance,
        EmailAddress.istypeof(obj) checks if obj is an Address object with
        a category of Address.CAT_EMAIL
        """
        return isinstance(obj, cls)

class EntityList(collections.MutableSequence, Entity):
    _contained_type = object

    # Don't try to cast list types (yet)
    _try_cast = False

    def __init__(self, *args):
        super(EntityList, self).__init__()
        self._inner = []

        for arg in args:
            if isinstance(arg, list):
                self.extend(arg)
            else:
                self.append(arg)

    def __getitem__(self, key):
        return self._inner.__getitem__(key)

    def __setitem__(self, key, value):
        if not self._is_valid(value):
            value = self._fix_value(value)
        self._inner.__setitem__(key, value)

    def __delitem__(self, key):
        self._inner.__delitem__(key)

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

    def insert(self, idx, value):
        if not self._is_valid(value):
            value = self._fix_value(value)
        self._inner.insert(idx, value)

    def _is_valid(self, value):
        """Check if this is a valid object to add to the list.

        Subclasses can override this function, but it's probably better to
        modify the istypeof function on the _contained_type.
        """
        return self._contained_type.istypeof(value)

    def _fix_value(self, value):
        """Attempt to coerce value into the correct type.

        Subclasses can override this function.
        """
        try:
            new_value = self._contained_type(value)
        except:
            raise ValueError("Can't put '%s' (%s) into a %s" %
                (value, type(value), self.__class__))
        return new_value

    # The next four functions can be overridden, but otherwise define the
    # default behavior for EntityList subclasses which define the following
    # class-level members:
    # - _binding_class
    # - _binding_var
    # - _contained_type

    def to_obj(self):
        tmp_list = [x.to_obj() for x in self]

        list_obj = self._binding_class()

        setattr(list_obj, self._binding_var, tmp_list)

        return list_obj

    def to_list(self):
        return [h.to_dict() for h in self]

    # Alias the `to_list` function as `to_dict`
    to_dict = to_list

    @classmethod
    def from_obj(cls, list_obj):
        if not list_obj:
            return None

        list_ = cls()

        for item in getattr(list_obj, cls._binding_var):
            list_.append(cls._contained_type.from_obj(item))

        return list_

    @classmethod
    def from_list(cls, list_list):
        if not isinstance(list_list, list):
            return None

        list_ = cls()

        for item in list_list:
            list_.append(cls._contained_type.from_dict(item))

        return list_

    @classmethod
    def object_from_list(cls, entitylist_list):
        """Convert from list representation to object representation."""
        return cls.from_list(entitylist_list).to_obj()

    @classmethod
    def list_from_object(cls, entitylist_obj):
        """Convert from object representation to list representation."""
        return cls.from_obj(entitylist_obj).to_list()

class TypedField(object):

    def __init__(self, name, type_=None, callback_hook=None, key_name=None,
                 comparable=True, multiple=False):
        """
        Create a new field.

        - `name` is the name of the field in the Binding class
        - `type_` is the type that objects assigned to this field must be.
          If `None`, no type checking is performed.
        - `key_name` is only needed if the desired key for the dictionary
          representation is differen than the lower-case version of `name`
        - `comparable` (boolean) - whether this field should be considered
          when checking Entities for equality. Default is True. If false, this
          field is not considered
        - `multiple` (boolean) - Whether multiple instances of this field can
          exist on the Entity.
        """
        self.name = name
        self.type_ = type_
        self.callback_hook = callback_hook
        self._key_name = key_name
        self.comparable = comparable
        self.multiple = multiple

    def __get__(self, instance, owner):
        # If we are calling this on a class, we want the actual Field, not its
        # value
        if not instance:
            return self

        return instance._fields.get(self.name, [] if self.multiple else None)

    def __set__(self, instance, value):
        if ((value is not None) and (self.type_ is not None) and
                (not self.type_.istypeof(value))):
            if self.multiple and isinstance(value, list):
                # TODO: if a list, check if each item in the list is the
                # correct type.
                pass
            elif self.type_._try_cast:
                value = self.type_(value)
            else:
                raise ValueError("%s must be a %s, not a %s" %
                                    (self.name, self.type_, type(value)))
        instance._fields[self.name] = value

        if self.callback_hook:
            self.callback_hook(instance)

    def __str__(self):
        return self.attr_name

    @property
    def key_name(self):
        if self._key_name:
            return self._key_name
        else:
            return self.name.lower()

    @property
    def attr_name(self):
        """The name of this field as an attribute name.

        This is identical to the key_name, unless the key name conflicts with
        a builtin Python keyword, in which case a single underscore is
        appended.

        This should match the name given to the TypedField class variable (see
        examples below), but this is not enforced.

        Examples:
            data = maec.TypedField("Data", String)
            from_ = maec.TypedField("From", String)
        """

        attr = self.key_name
        # TODO: expand list with other Python keywords
        if attr in ('from', 'class', 'type', 'with', 'for', 'id', 'type',
                'range'):
            attr = attr + "_"
        return attr

# Parse a MAEC instance and return the correct Binding and API objects
# Returns a tuple where pos 0 = Package (binding, API) tuple, and pos 1 = Bundle (binding, API) tuple, or None
def parse_xml_instance(filename):
    package_obj = package_binding.parse(filename)
    bundle_obj = bundle_binding.parse(filename)

    if not bundle_obj.hasContent_():
        package_tuple = (package_obj, package.package.Package.from_obj(package_obj))
        return (package_tuple, None)
    elif package_obj.hasContent_():
        bundle_tuple = (bundle_obj, bundle.bundle.Bundle.from_obj(bundle_obj))
        return (None, bundle_tuple)<commit_msg>Added optional namespace_dict parameter to to_xml_file()<commit_after>__version__ = "4.1.0.0"

import collections
import json
import inspect
from StringIO import StringIO
import bindings.maec_bundle as bundle_binding
import bindings.maec_package as package_binding
from cybox import Entity as cyboxEntity
from cybox.utils import Namespace
from maec.utils import maecMETA

def get_xmlns_string(ns_set):
    """Build a string with 'xmlns' definitions for every namespace in ns_set.

    Arguments:
    - ns_set: a set (or other iterable) of Namespace objects
    """
    xmlns_format = 'xmlns:{0.prefix}="{0.name}"'
    return "\n\t".join([xmlns_format.format(x) for x in ns_set])

def get_schemaloc_string(ns_set):
    """Build a "schemaLocation" string for every namespace in ns_set.

    Arguments:
    - ns_set: a set (or other iterable) of Namespace objects
    """
    schemaloc_format = '{0.name} {0.schema_location}'
    # Only include schemas that have a schema_location defined (for instance,
    # 'xsi' does not.
    return " ".join([schemaloc_format.format(x) for x in ns_set
                     if x.schema_location])

class Entity(object):
    """Base class for all classes in the MAEC SimpleAPI."""

    # By default (unless a particular subclass states otherwise), try to "cast"
    # invalid objects to the correct class using the constructor. Entity
    # subclasses should either provide a "sane" constructor or set this to
    # False.
    _try_cast = True

    def __init__(self):
        self._fields = {}

    @classmethod
    def _get_vars(cls):
        var_list = []
        for (name, obj) in inspect.getmembers(cls, inspect.isdatadescriptor):
            if isinstance(obj, TypedField):
                var_list.append(obj)

        return var_list

    def __eq__(self, other):
        # This fixes some strange behavior where an object isn't equal to
        # itself
        if other is self:
            return True

        # I'm not sure about this, if we want to compare exact classes or if
        # various subclasses will also do (I think not), but for now I'm going
        # to assume they must be equal. - GTB
        if self.__class__ != other.__class__:
            return False

        var_list = self.__class__._get_vars()

        # If there are no TypedFields, assume this class hasn't been
        # "TypedField"-ified, so we don't want these to inadvertently return
        # equal.
        if not var_list:
            return False

        for f in var_list:
            if not f.comparable:
                continue
            if getattr(self, f.attr_name) != getattr(other, f.attr_name):
                return False

        return True

    def __ne__(self, other):
        return not self == other

    def to_obj(self):
        """Default implementation of a to_obj function.

        Subclasses can override this function."""

        entity_obj = self._binding_class()

        for field in self.__class__._get_vars():
            val = getattr(self, field.attr_name)

            if field.multiple and val:
                val = [x.to_obj() for x in val]
            elif isinstance(val, Entity):
                val = val.to_obj()

            setattr(entity_obj, field.name, val)

        self._finalize_obj(entity_obj)

        return entity_obj

    def _finalize_obj(self, entity_obj):
        """Subclasses can define additional items in the binding object.

        `entity_obj` should be modified in place.
        """
        pass

    def to_dict(self):
        """Default implementation of a to_dict function.

        Subclasses can override this function."""

        entity_dict = {}

        for field in self.__class__._get_vars():
            val = getattr(self, field.attr_name)

            if field.multiple and val:
                val = [x.to_dict() for x in val]
            if isinstance(val, Entity):
                val = val.to_dict()

            # Only add non-None objects or non-empty lists
            if val is not None and val != []:
                entity_dict[field.key_name] = val

        self._finalize_dict(entity_dict)

        return entity_dict

    def _finalize_dict(self, entity_dict):
        """Subclasses can define additional items in the dictionary.

        `entity_dict` should be modified in place.
        """
        pass

    @classmethod
    def from_obj(cls, cls_obj=None):
        if not cls_obj:
            return None

        entity = cls()

        for field in cls._get_vars():
            val = getattr(cls_obj, field.name)
            if field.type_:
                if field.multiple and val is not None:
                    val = [field.type_.from_obj(x) for x in val]
                else:
                    val = field.type_.from_obj(val)
            setattr(entity, field.attr_name, val)

        return entity

    @classmethod
    def from_dict(cls, cls_dict=None):
        if cls_dict is None:
            return None

        entity = cls()

        # Shortcut if an actual dict is not provided:
        if not isinstance(cls_dict, dict):
            value = cls_dict
            # Call the class's constructor
            try:
                return cls(value)
            except TypeError:
                raise TypeError("Could not instantiate a %s from a %s: %s" %
                                (cls, type(value), value))

        for field in cls._get_vars():
            val = cls_dict.get(field.key_name)
            if field.type_:
                if issubclass(field.type_, EntityList):
                    val = field.type_.from_list(val)
                elif field.multiple:
                    if val is not None:
                        val = [field.type_.from_dict(x) for x in val]
                    else:
                        val = []
                else:
                    val = field.type_.from_dict(val)
            setattr(entity, field.attr_name, val)

        return entity

    def to_xml(self, include_namespaces=True, namespace_dict=None,
               pretty=True):
        """Export an object as an XML String.

        :param include_namespaces: whether to include xmlns and
          xsi:schemaLocation attributes on the root element. Set to true by
          default.
        :type include_namespaces: bool
        :param namespace_dict: mapping of additional XML namespaces to prefixes
        :type namespace_dict: dict
        :param pretty: produce readable (``True``) or compact (``False``)
          output. Default is ``True``
        :type pretty: bool
        """
        namespace_def = ""

        if include_namespaces:
            namespace_def = self._get_namespace_def(namespace_dict)

        if not pretty:
            namespace_def = namespace_def.replace('\n\t', ' ')

        s = StringIO()
        self.to_obj().export(s, 0, namespacedef_=namespace_def,
                             pretty_print=pretty)
        return s.getvalue()

    def to_xml_file(self, filename, namespace_dict=None):
        """Export an object to an XML file. Only supports Package or Bundle objects at the moment."""
        out_file  = open(filename, 'w')
        out_file.write("<?xml version='1.0' encoding='UTF-8'?>\n")
        self.to_obj().export(out_file, 0, namespace_def = self._get_namespace_def(namespace_dict))
        out_file.close()

    def to_json(self):
        """Export an object as a JSON string.
        """
        return json.dumps(self.to_dict())

    def _get_namespace_def(self, additional_ns_dict=None):
        # copy necessary namespaces

        namespaces = self._get_namespaces()

        if additional_ns_dict:
            for ns, prefix in additional_ns_dict.iteritems():
                namespaces.update([Namespace(ns, prefix)])

        # if there are any other namepaces, include xsi for "schemaLocation"
        if namespaces:
            namespaces.update([maecMETA.lookup_prefix('xsi')])

        if not namespaces:
            return ""

        namespaces = sorted(namespaces, key=str)

        return ('\n\t' + get_xmlns_string(namespaces) +
                '\n\txsi:schemaLocation="' + get_schemaloc_string(namespaces) +
                '"')

    def _get_namespaces(self, recurse=True):
        nsset = set()

        # Get all _namespaces for parent classes
        namespaces = [x._namespace for x in self.__class__.__mro__
                      if hasattr(x, '_namespace')]

        nsset.update([maecMETA.lookup_namespace(ns) for ns in namespaces])

        #In case of recursive relationships, don't process this item twice
        self.touched = True
        if recurse:
            for x in self._get_children():
                if not hasattr(x, 'touched'):
                    nsset.update(x._get_namespaces())
        del self.touched

        return nsset

    def _get_children(self):
        #TODO: eventually everything should be in _fields, not the top level
        # of vars()
        for k, v in vars(self).items() + self._fields.items():
            if isinstance(v, Entity) or isinstance(v, cyboxEntity):
                yield v
            elif isinstance(v, list):
                for item in v:
                    if isinstance(item, Entity) or isinstance(v, cyboxEntity):
                        yield item

    @classmethod
    def istypeof(cls, obj):
        """Check if `cls` is the type of `obj`

        In the normal case, as implemented here, a simple isinstance check is
        used. However, there are more complex checks possible. For instance,
        EmailAddress.istypeof(obj) checks if obj is an Address object with
        a category of Address.CAT_EMAIL
        """
        return isinstance(obj, cls)

class EntityList(collections.MutableSequence, Entity):
    _contained_type = object

    # Don't try to cast list types (yet)
    _try_cast = False

    def __init__(self, *args):
        super(EntityList, self).__init__()
        self._inner = []

        for arg in args:
            if isinstance(arg, list):
                self.extend(arg)
            else:
                self.append(arg)

    def __getitem__(self, key):
        return self._inner.__getitem__(key)

    def __setitem__(self, key, value):
        if not self._is_valid(value):
            value = self._fix_value(value)
        self._inner.__setitem__(key, value)

    def __delitem__(self, key):
        self._inner.__delitem__(key)

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

    def insert(self, idx, value):
        if not self._is_valid(value):
            value = self._fix_value(value)
        self._inner.insert(idx, value)

    def _is_valid(self, value):
        """Check if this is a valid object to add to the list.

        Subclasses can override this function, but it's probably better to
        modify the istypeof function on the _contained_type.
        """
        return self._contained_type.istypeof(value)

    def _fix_value(self, value):
        """Attempt to coerce value into the correct type.

        Subclasses can override this function.
        """
        try:
            new_value = self._contained_type(value)
        except:
            raise ValueError("Can't put '%s' (%s) into a %s" %
                (value, type(value), self.__class__))
        return new_value

    # The next four functions can be overridden, but otherwise define the
    # default behavior for EntityList subclasses which define the following
    # class-level members:
    # - _binding_class
    # - _binding_var
    # - _contained_type

    def to_obj(self):
        tmp_list = [x.to_obj() for x in self]

        list_obj = self._binding_class()

        setattr(list_obj, self._binding_var, tmp_list)

        return list_obj

    def to_list(self):
        return [h.to_dict() for h in self]

    # Alias the `to_list` function as `to_dict`
    to_dict = to_list

    @classmethod
    def from_obj(cls, list_obj):
        if not list_obj:
            return None

        list_ = cls()

        for item in getattr(list_obj, cls._binding_var):
            list_.append(cls._contained_type.from_obj(item))

        return list_

    @classmethod
    def from_list(cls, list_list):
        if not isinstance(list_list, list):
            return None

        list_ = cls()

        for item in list_list:
            list_.append(cls._contained_type.from_dict(item))

        return list_

    @classmethod
    def object_from_list(cls, entitylist_list):
        """Convert from list representation to object representation."""
        return cls.from_list(entitylist_list).to_obj()

    @classmethod
    def list_from_object(cls, entitylist_obj):
        """Convert from object representation to list representation."""
        return cls.from_obj(entitylist_obj).to_list()

class TypedField(object):

    def __init__(self, name, type_=None, callback_hook=None, key_name=None,
                 comparable=True, multiple=False):
        """
        Create a new field.

        - `name` is the name of the field in the Binding class
        - `type_` is the type that objects assigned to this field must be.
          If `None`, no type checking is performed.
        - `key_name` is only needed if the desired key for the dictionary
          representation is differen than the lower-case version of `name`
        - `comparable` (boolean) - whether this field should be considered
          when checking Entities for equality. Default is True. If false, this
          field is not considered
        - `multiple` (boolean) - Whether multiple instances of this field can
          exist on the Entity.
        """
        self.name = name
        self.type_ = type_
        self.callback_hook = callback_hook
        self._key_name = key_name
        self.comparable = comparable
        self.multiple = multiple

    def __get__(self, instance, owner):
        # If we are calling this on a class, we want the actual Field, not its
        # value
        if not instance:
            return self

        return instance._fields.get(self.name, [] if self.multiple else None)

    def __set__(self, instance, value):
        if ((value is not None) and (self.type_ is not None) and
                (not self.type_.istypeof(value))):
            if self.multiple and isinstance(value, list):
                # TODO: if a list, check if each item in the list is the
                # correct type.
                pass
            elif self.type_._try_cast:
                value = self.type_(value)
            else:
                raise ValueError("%s must be a %s, not a %s" %
                                    (self.name, self.type_, type(value)))
        instance._fields[self.name] = value

        if self.callback_hook:
            self.callback_hook(instance)

    def __str__(self):
        return self.attr_name

    @property
    def key_name(self):
        if self._key_name:
            return self._key_name
        else:
            return self.name.lower()

    @property
    def attr_name(self):
        """The name of this field as an attribute name.

        This is identical to the key_name, unless the key name conflicts with
        a builtin Python keyword, in which case a single underscore is
        appended.

        This should match the name given to the TypedField class variable (see
        examples below), but this is not enforced.

        Examples:
            data = maec.TypedField("Data", String)
            from_ = maec.TypedField("From", String)
        """

        attr = self.key_name
        # TODO: expand list with other Python keywords
        if attr in ('from', 'class', 'type', 'with', 'for', 'id', 'type',
                'range'):
            attr = attr + "_"
        return attr

# Parse a MAEC instance and return the correct Binding and API objects
# Returns a tuple where pos 0 = Package (binding, API) tuple, and pos 1 = Bundle (binding, API) tuple, or None
def parse_xml_instance(filename):
    package_obj = package_binding.parse(filename)
    bundle_obj = bundle_binding.parse(filename)

    if not bundle_obj.hasContent_():
        package_tuple = (package_obj, package.package.Package.from_obj(package_obj))
        return (package_tuple, None)
    elif package_obj.hasContent_():
        bundle_tuple = (bundle_obj, bundle.bundle.Bundle.from_obj(bundle_obj))
        return (None, bundle_tuple)<|endoftext|>


<commit_before>from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template import RequestContext
from django.views.decorators.http import require_POST

from project.forms import recipe_form_map
from project.models import Project
import buildout_manage

def add_recipe(request, project_id):
    project = get_object_or_404(Project, id=project_id)
    return render_to_response("project/add_recipe.html",
            RequestContext(request, {
                'project': project,
                'available_recipes': buildout_manage.recipes}))

def edit_recipe(request, project_id, recipe_name):
    #FIXME: rename recipe_name to section_name to avoid confusion
    # and recipe_type to recipe_name
    project = get_object_or_404(Project, id=project_id)
    buildout=project.buildout()
    section=buildout[recipe_name]
    recipe_type = section['recipe']
    recipe = buildout_manage.recipes[recipe_type](buildout, recipe_name)

    form = recipe_form_map[recipe_type](project, initial=recipe.dict())
    return render_to_response("project/edit_recipe.html", RequestContext(
        request, {'form': form,
            'template_name': "project/recipe_templates/%s.html" % recipe_type,
            'recipe_name': recipe_type,
            'project': project}))

def recipe_template(request, project_id, recipe_name):
    project = get_object_or_404(Project, id=project_id)
    form = recipe_form_map[recipe_name](project)
    return render_to_response("project/recipe_templates/%s.html" % recipe_name,
            {'form': form})

@require_POST
def save_recipe(request, project_id):
    project = get_object_or_404(Project, id=project_id)
    recipe_name = request.POST['recipe_name']
    form = recipe_form_map[recipe_name](project, request.POST)
    if form.is_valid():
        buildout = project.buildout()
        form.save(buildout)
        return redirect(project.get_absolute_url())
    else:
        return render_to_response("project/recipe_templates/%s.html" % recipe_name,
                {'form': form})

<commit_msg>Import from new module.<commit_after>from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template import RequestContext
from django.views.decorators.http import require_POST

from recipes.forms import recipe_form_map
from project.models import Project
import buildout_manage

def add_recipe(request, project_id):
    project = get_object_or_404(Project, id=project_id)
    return render_to_response("project/add_recipe.html",
            RequestContext(request, {
                'project': project,
                'available_recipes': buildout_manage.recipes}))

def edit_recipe(request, project_id, recipe_name):
    #FIXME: rename recipe_name to section_name to avoid confusion
    # and recipe_type to recipe_name
    project = get_object_or_404(Project, id=project_id)
    buildout=project.buildout()
    section=buildout[recipe_name]
    recipe_type = section['recipe']
    recipe = buildout_manage.recipes[recipe_type](buildout, recipe_name)

    form = recipe_form_map[recipe_type](project, initial=recipe.dict())
    return render_to_response("project/edit_recipe.html", RequestContext(
        request, {'form': form,
            'template_name': "project/recipe_templates/%s.html" % recipe_type,
            'recipe_name': recipe_type,
            'project': project}))

def recipe_template(request, project_id, recipe_name):
    project = get_object_or_404(Project, id=project_id)
    form = recipe_form_map[recipe_name](project)
    return render_to_response("project/recipe_templates/%s.html" % recipe_name,
            {'form': form})

@require_POST
def save_recipe(request, project_id):
    project = get_object_or_404(Project, id=project_id)
    recipe_name = request.POST['recipe_name']
    form = recipe_form_map[recipe_name](project, request.POST)
    if form.is_valid():
        buildout = project.buildout()
        form.save(buildout)
        return redirect(project.get_absolute_url())
    else:
        return render_to_response("project/recipe_templates/%s.html" % recipe_name,
                {'form': form})

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Alec Thomas <alec@swapoff.org>

"""Permission policy enforcement through an authz-like configuration file.
Refer to SVN documentation for syntax of the authz file. Groups are supported.

Each section of the config is a glob to match against a Trac resource
descriptor. These descriptors are in the form:

    <realm>:<id>@<version>[/<realm>:<id>@<version> ...]

Resources are ordered left to right, from parent to child. If any component is
inapplicable, * is substituted.

eg. An attachment on WikiStart:

    wiki:WikiStart@117/attachment/FOO.JPG@*

or the Wiki module as a whole:

    wiki:*@*

Sections are checked against the current Trac resource **IN ORDER** of
appearance in the configuration file. ORDER IS CRITICAL.

Once a section matches, the current username is matched, **IN ORDER**, against
the keys of the section. If a key is prefixed with a @ it is treated as a
group. The username will match any of 'anonymous', 'authenticated', <username>
or '*', using normal Trac permission rules.

Example configuration:

    [groups]
    administrators = athomas

    [*/attachment:*]
    * = WIKI_VIEW, TICKET_VIEW

    [wiki:WikiStart@*]
    @administrators = WIKI_ADMIN
    anonymous = WIKI_VIEW
    * = WIKI_VIEW

    # Deny access to page templates
    [wiki:PageTemplates/*]
    * =

    # Match everything else
    [*]
    @administrators = TRAC_ADMIN
    anonymous = BROWSER_VIEW, CHANGESET_VIEW, FILE_VIEW, LOG_VIEW, MILESTONE_VIEW, POLL_VIEW, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_VIEW, SEARCH_VIEW, TICKET_CREATE, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, WIKI_CREATE, WIKI_MODIFY, WIKI_VIEW
    # Give authenticated users some extra permissions
    authenticated = REPO_SEARCH, XML_RPC

"""

import os
from fnmatch import fnmatch
from trac.core import *
from trac.config import Option
from trac.util.compat import set
from trac.perm import PermissionSystem, IPermissionPolicy
from configobj import ConfigObj

class AuthzPolicy(Component):
    implements(IPermissionPolicy)

    authz_file = Option('authz_policy', 'authz_file', None,
                        'Location of authz policy configuration file.')

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods
    def check_permission(self, username, action, context):
        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file()) > self.authz_mtime:
            self.parse_authz()
        ctx_key = self.flatten_context(context)
        permissions = self.authz_permissions(ctx_key, username)
        if permissions is None:
            return None
        elif permissions:
            permissions = PermissionSystem(self.env).expand_actions(permissions)
        return action in permissions

    # Internal methods
    def get_authz_file(self):
        f = self.authz_file
        return os.path.isabs(f) and f or os.path.join(self.env.path, f)

    def parse_authz(self):
        self.env.log.debug('Parsing authz security policy %s' %
                           self.get_authz_file())
        self.authz = ConfigObj(self.get_authz_file())
        self.groups_by_user = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            for user in users:
                self.groups_by_user.setdefault(user, set()).add('@' + group)
        self.authz_mtime = os.path.getmtime(self.get_authz_file())

    def flatten_context(self, context):
        def flatten(context):
            # XXX Use of root realm is inconsistent. Sometimes it is the parent
            # object, other times not. XXX
            if not context or not (context.realm or context.id):
                return []
            parent = flatten(context.parent)
            return parent + ['%s:%s@%s' % (context.realm or '*',
                                           context.id or '*',
                                           context.version or '*')]
        return '/'.join(flatten(context))

    def authz_permissions(self, ctx_key, username):
        valid_users = ['*', 'anonymous']
        if username and username != 'anonymous':
            valid_users += ['authenticated', username]
        for ctx_glob in [a for a in self.authz.sections if a != 'groups']:
            if fnmatch(ctx_key, ctx_glob):
                section = self.authz[ctx_glob]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        #self.env.log.debug('%s matched section %s' % (ctx_key, ctx_glob))
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<commit_msg>AuthzPolicy: updated the plugin doc with some needy informations.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Alec Thomas <alec@swapoff.org>

"""Permission policy enforcement through an authz-like configuration file.
Refer to SVN documentation for syntax of the authz file. Groups are supported.

Note that this plugin requires the `configobj` package:

    http://www.voidspace.org.uk/python/configobj.html

You should be able to install it by doing a simple `easy_install configobj`

Enabling this policy requires listing it in the trac.ini:

[trac]
permission_policies = AuthzPolicy, DefaultPermissionPolicy

[authz_policy]
authz_file = conf/authzpolicy.conf

This means that the AuthzPolicy permissions will be checked first, and only
if no rule is found will the DefaultPermissionPolicy be used.

The authzpolicy.conf file is a .ini style configuration file.

 - Each section of the config is a glob pattern used to match against a Trac
resource descriptor. These descriptors are in the form:

     <realm>:<id>@<version>[/<realm>:<id>@<version> ...]

   Resources are ordered left to right, from parent to child.
   If any component is inapplicable, * is substituted.

   e.g. An attachment on WikiStart:

     wiki:WikiStart@117/attachment/FOO.JPG@*

   any of the following sections would match it:

     [wiki:*]
     [wiki:WikiStart*]
     [wiki:WikiStart@*]
     [wiki:WikiStart@*/attachment/*]

   but be careful, not this one:

     [wiki:WikiStart@117/attachment/FOO.JPG]

   as the above won't match the @ part in the attachment resource descriptor.

 - Sections are checked against the current Trac resource **IN ORDER** of
   appearance in the configuration file. ORDER IS CRITICAL.

 - Once a section matches, the current username is matched, **IN ORDER**,
   against the keys of the section. If a key is prefixed with a @, it is
   treated as a group. The username will match any of 'anonymous',
   'authenticated', <username> or '*', using normal Trac permission rules.

Example configuration:

    [groups]
    administrators = athomas

    [*/attachment:*]
    * = WIKI_VIEW, TICKET_VIEW

    [wiki:WikiStart@*]
    @administrators = WIKI_ADMIN
    anonymous = WIKI_VIEW
    * = WIKI_VIEW

    # Deny access to page templates
    [wiki:PageTemplates/*]
    * =

    # Match everything else
    [*]
    @administrators = TRAC_ADMIN
    anonymous = BROWSER_VIEW, CHANGESET_VIEW, FILE_VIEW, LOG_VIEW, MILESTONE_VIEW, POLL_VIEW, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_VIEW, SEARCH_VIEW, TICKET_CREATE, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, WIKI_CREATE, WIKI_MODIFY, WIKI_VIEW
    # Give authenticated users some extra permissions
    authenticated = REPO_SEARCH, XML_RPC

"""

import os
from fnmatch import fnmatch
from trac.core import *
from trac.config import Option
from trac.util.compat import set
from trac.perm import PermissionSystem, IPermissionPolicy
from configobj import ConfigObj

class AuthzPolicy(Component):
    implements(IPermissionPolicy)

    authz_file = Option('authz_policy', 'authz_file', None,
                        'Location of authz policy configuration file.')

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods
    def check_permission(self, username, action, context):
        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file()) > self.authz_mtime:
            self.parse_authz()
        ctx_key = self.flatten_context(context)
        permissions = self.authz_permissions(ctx_key, username)
        if permissions is None:
            return None
        elif permissions:
            permissions = PermissionSystem(self.env).expand_actions(permissions)
        return action in permissions

    # Internal methods
    def get_authz_file(self):
        f = self.authz_file
        return os.path.isabs(f) and f or os.path.join(self.env.path, f)

    def parse_authz(self):
        self.env.log.debug('Parsing authz security policy %s' %
                           self.get_authz_file())
        self.authz = ConfigObj(self.get_authz_file())
        self.groups_by_user = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            for user in users:
                self.groups_by_user.setdefault(user, set()).add('@' + group)
        self.authz_mtime = os.path.getmtime(self.get_authz_file())

    def flatten_context(self, context):
        def flatten(context):
            # XXX Use of root realm is inconsistent. Sometimes it is the parent
            # object, other times not. XXX
            if not context or not (context.realm or context.id):
                return []
            parent = flatten(context.parent)
            return parent + ['%s:%s@%s' % (context.realm or '*',
                                           context.id or '*',
                                           context.version or '*')]
        return '/'.join(flatten(context))

    def authz_permissions(self, ctx_key, username):
        valid_users = ['*', 'anonymous']
        if username and username != 'anonymous':
            valid_users += ['authenticated', username]
        for ctx_glob in [a for a in self.authz.sections if a != 'groups']:
            if fnmatch(ctx_key, ctx_glob):
                section = self.authz[ctx_glob]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        #self.env.log.debug('%s matched section %s' % (ctx_key, ctx_glob))
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<|endoftext|>


<commit_before>import h5py

def createHDF5Unlabeled (outputFile, trainDataShape, trainDataDtype,
                         trainMaxShape=None, log=None) :
    '''Utility to create the HDF5 file and return the handles. This allows
       users to fill out the buffers in a memory conscious manner.

       outputFile        : Name of the file to write. The extension should be
                           either .h5 or .hdf5
       trainDataShape    : Training data dimensions
       trainDataDtype    : Training data dtype
       trainMaxShape     : Optionally specify a maxshape for the training set
       log               : Logger to use
    '''
    if not outputFile.endswith('.h5') and not outputFile.endswith('.hdf5') :
        raise Exception('The file must end in the .h5 or .hdf5 extension.')

    # create a file in a standard way
    hdf5 = h5py.File(outputFile, libver='latest', mode='w')

    # the file will always have training data in train/data
    trainData = hdf5.create_dataset('train/data', shape=trainDataShape,
                                    dtype=trainDataDtype,
                                    maxshape=trainMaxShape)

    # TODO: Should we additionally have a test/data set to allow early
    #       stoppage? This will test the ability to reconstruct data
    #       never before encountered. It's likely a better way to perform
    #       training instead of naive number of epochs.
    return [hdf5, trainData]

def createHDF5Labeled (outputFile,
                       trainDataShape, trainDataDtype, trainIndicesDtype,
                       testDataShape, testDataDtype, testIndicesDtype,
                       labelsShape, log=None) :
    '''Utility to create the HDF5 file and return the handles. This allows
       users to fill out the buffers in a memory conscious manner.

       outputFile        : Name of the file to write. The extension should be
                           either .h5 or .hdf5
       trainDataShape    : Training data dimensions
       trainDataDtype    : Training data dtype
       trainIndicesDtype : Training indicies dtype
       testDataShape     : Testing data dimensions
       testDataDtype     : Testing data dtype
       testIndicesDtype  : Testing indicies dtype
       labelsShape       : Labels shape associated with indices
       log               : Logger to use
    '''
    hdf5, trainData = createHDF5Unlabeled(outputFile, trainDataShape,
                                          trainDataDtype, log=log)

    # supervised learning will have indices associated with the training data
    trainIndices = hdf5.create_dataset('train/indices',
                                       shape=tuple(trainDataShape[:2]),
                                       dtype=trainIndicesDtype)

    # add testing data and indices
    testData = hdf5.create_dataset('test/data', shape=testDataShape,
                                   dtype=testDataDtype)
    testIndices = hdf5.create_dataset('test/indices',
                                      shape=tuple(testDataShape[:2]),
                                      dtype=testIndicesDtype)

    # each index with have an associated string label
    labelsShape = labelsShape if isinstance(labelsShape, tuple) else\
                  (labelsShape, )
    labelsDtype = h5py.special_dtype(vlen=str)
    labels = hdf5.create_dataset('labels', shape=labelsShape,
                                 dtype=labelsDtype)

    return [hdf5, trainData, trainIndices, testData, testIndices, labels]

def writeHDF5 (outputFile, trainData, trainIndices=None,
               testData=None, testIndices=None, labels=None, log=None) :
    '''Utility to write a hdf5 file to disk given the data exists in numpy.

       outputFile   : Name of the file to write. The extension should be pkl.gz
       trainData    : Training data (numBatch, batchSize, chan, row, col)
       trainIndices : Training indices (either one-hot or float vectors)
       testData     : Testing data (numBatch, batchSize, chan, row, col)
       testIndices  : Testing indices (either one-hot or float vectors)
       labels       : String labels associated with indices
       log          : Logger to use
    '''
    if log is not None :
        log.debug('Writing to [' + outputFile + ']')

    # write the data to disk -- if it was supplied
    with h5py.File(outputFile, mode='w') as hdf5 :
        hdf5.create_dataset('train/data', data=trainData)

        # TODO: This should also be updated if we find unsupervised training
        #       is better with early stoppage via a test set.
        if trainIndices is not None :
            hdf5.create_dataset('train/indices', data=trainIndices)
        if testData is not None :
            hdf5.create_dataset('test/data', data=testData)
        if testIndices is not None :
            hdf5.create_dataset('test/indices', data=testIndices)
        if labels is not None :
            hdf5.create_dataset('labels', data=[l.encode("ascii", "ignore") \
                                               for l in labels])

        # ensure it gets to disk
        hdf5.flush()
        hdf5.close()

def readHDF5 (inFile, log=None) :
    '''Utility to read a pickle in from disk.

       inFile : Name of the file to read. The extension should be pkl.gz
       log    : Logger to use

       return : (train, test, labels)
    '''
    if not inFile.endswith('.h5') and not inFile.endswith('.hdf5') :
        raise Exception('The file must end in the .h5 or .hdf5 extension.')

    if log is not None :
        log.debug('Opening the file in memory-mapped mode')

    # open the file
    hdf5 = h5py.File(inFile, mode='r')

    # read the available information
    trainData = hdf5.get("train/data")
    trainIndices = None
    if 'train/indices' in hdf5 :
        trainIndices = hdf5.get('train/indices')
    testData = None
    if 'test/data' in hdf5 :
        testData = hdf5.get('test/data')
    testIndices = None
    if 'test/indices' in hdf5 :
        testIndices = hdf5.get('test/indices')
    labels = None
    if 'labels' in hdf5 :
        labels = hdf5.get('labels')

    # the returned information should be checked for None
    return (trainData, trainIndices), (testData, testIndices), labels
<commit_msg>Return everything as a list now. This allows us to append things that are needed<commit_after>import h5py

def createHDF5Unlabeled (outputFile, trainDataShape, trainDataDtype,
                         trainMaxShape=None, log=None) :
    '''Utility to create the HDF5 file and return the handles. This allows
       users to fill out the buffers in a memory conscious manner.

       outputFile        : Name of the file to write. The extension should be
                           either .h5 or .hdf5
       trainDataShape    : Training data dimensions
       trainDataDtype    : Training data dtype
       trainMaxShape     : Optionally specify a maxshape for the training set
       log               : Logger to use
    '''
    if not outputFile.endswith('.h5') and not outputFile.endswith('.hdf5') :
        raise Exception('The file must end in the .h5 or .hdf5 extension.')

    # create a file in a standard way
    hdf5 = h5py.File(outputFile, libver='latest', mode='w')

    # the file will always have training data in train/data
    trainData = hdf5.create_dataset('train/data', shape=trainDataShape,
                                    dtype=trainDataDtype,
                                    maxshape=trainMaxShape)

    # TODO: Should we additionally have a test/data set to allow early
    #       stoppage? This will test the ability to reconstruct data
    #       never before encountered. It's likely a better way to perform
    #       training instead of naive number of epochs.
    return [hdf5, trainData]

def createHDF5Labeled (outputFile,
                       trainDataShape, trainDataDtype, trainIndicesDtype,
                       testDataShape, testDataDtype, testIndicesDtype,
                       labelsShape, log=None) :
    '''Utility to create the HDF5 file and return the handles. This allows
       users to fill out the buffers in a memory conscious manner.

       outputFile        : Name of the file to write. The extension should be
                           either .h5 or .hdf5
       trainDataShape    : Training data dimensions
       trainDataDtype    : Training data dtype
       trainIndicesDtype : Training indicies dtype
       testDataShape     : Testing data dimensions
       testDataDtype     : Testing data dtype
       testIndicesDtype  : Testing indicies dtype
       labelsShape       : Labels shape associated with indices
       log               : Logger to use
    '''
    hdf5, trainData = createHDF5Unlabeled(outputFile, trainDataShape,
                                          trainDataDtype, log=log)

    # supervised learning will have indices associated with the training data
    trainIndices = hdf5.create_dataset('train/indices',
                                       shape=tuple(trainDataShape[:2]),
                                       dtype=trainIndicesDtype)

    # add testing data and indices
    testData = hdf5.create_dataset('test/data', shape=testDataShape,
                                   dtype=testDataDtype)
    testIndices = hdf5.create_dataset('test/indices',
                                      shape=tuple(testDataShape[:2]),
                                      dtype=testIndicesDtype)

    # each index with have an associated string label
    labelsShape = labelsShape if isinstance(labelsShape, tuple) else\
                  (labelsShape, )
    labelsDtype = h5py.special_dtype(vlen=str)
    labels = hdf5.create_dataset('labels', shape=labelsShape,
                                 dtype=labelsDtype)

    return [hdf5, trainData, trainIndices, testData, testIndices, labels]

def writeHDF5 (outputFile, trainData, trainIndices=None,
               testData=None, testIndices=None, labels=None, log=None) :
    '''Utility to write a hdf5 file to disk given the data exists in numpy.

       outputFile   : Name of the file to write. The extension should be pkl.gz
       trainData    : Training data (numBatch, batchSize, chan, row, col)
       trainIndices : Training indices (either one-hot or float vectors)
       testData     : Testing data (numBatch, batchSize, chan, row, col)
       testIndices  : Testing indices (either one-hot or float vectors)
       labels       : String labels associated with indices
       log          : Logger to use
    '''
    if log is not None :
        log.debug('Writing to [' + outputFile + ']')

    # write the data to disk -- if it was supplied
    with h5py.File(outputFile, mode='w') as hdf5 :
        hdf5.create_dataset('train/data', data=trainData)

        # TODO: This should also be updated if we find unsupervised training
        #       is better with early stoppage via a test set.
        if trainIndices is not None :
            hdf5.create_dataset('train/indices', data=trainIndices)
        if testData is not None :
            hdf5.create_dataset('test/data', data=testData)
        if testIndices is not None :
            hdf5.create_dataset('test/indices', data=testIndices)
        if labels is not None :
            hdf5.create_dataset('labels', data=[l.encode("ascii", "ignore") \
                                               for l in labels])

        # ensure it gets to disk
        hdf5.flush()
        hdf5.close()

def readHDF5 (inFile, log=None) :
    '''Utility to read a pickle in from disk.

       inFile : Name of the file to read. The extension should be pkl.gz
       log    : Logger to use

       return : (train, test, labels)
    '''
    if not inFile.endswith('.h5') and not inFile.endswith('.hdf5') :
        raise Exception('The file must end in the .h5 or .hdf5 extension.')

    if log is not None :
        log.debug('Opening the file in memory-mapped mode')

    # open the file
    hdf5 = h5py.File(inFile, mode='r')

    # read the available information
    trainData = hdf5.get("train/data")
    trainIndices = None
    if 'train/indices' in hdf5 :
        trainIndices = hdf5.get('train/indices')
    testData = None
    if 'test/data' in hdf5 :
        testData = hdf5.get('test/data')
    testIndices = None
    if 'test/indices' in hdf5 :
        testIndices = hdf5.get('test/indices')
    labels = None
    if 'labels' in hdf5 :
        labels = hdf5.get('labels')

    # the returned information should be checked for None
    return [trainData, trainIndices], [testData, testIndices], labels
<|endoftext|>


<commit_before>#
# growler/middleware/renderer.py
#

import asyncio
import os

class Renderer():
    """
    Loads templates
    """
    def __init__(self, path, engine):
        self.path = os.path.abspath(path)
        if not os.path.exists(self.path):
            print("[Renderer] Error: No path exists at {}".format(self.path))
            raise Exception("Path '{}' does not exist.".format(self.path))
        print("[Renderer] Template files located in {}".format(self.path))

        if isinstance(engine, str):
            engine = render_engine_map.get(engine, None)

        if engine is None:
            raise Exception("[Renderer] No valid rendering engine provided.")

        self.engine = engine(self)

    @asyncio.coroutine
    def __call__(self, req, res):

        def _render(template, obj={}, cb=None):
            print("[Renderer::_render]", template)
            res.locals.update(obj)
            filename = self._find_file(template)
            print("[HTML]")
            html = self.engine(filename, res)
            res.send_html(html)

        res.locals = {}
        res.render = _render

    def _find_file(self, fname):
        filename = os.path.join(self.path, fname)
        if not os.path.isfile(filename):
            filename += self.engine.default_file_extension()
            if not os.path.isfile(filename):
                raise Exception("No File found {}.".format(fname))
        print("[Renderer::_find_file]", filename)
        return filename

class MakoRenderer():

    def __init__(self, source):
        from mako.template import Template
        self._render = Template
        print("[MakoRenderer]")

    def __call__(self, filename, res):
        print("[MakoRenderer] CALL", filename)
        tmpl = self._render(filename=filename)
        html = tmpl.render()
        return html

    def default_file_extension(self):
        return ".mako"

class JadeRenderer():

    def __init__(self, source):
        print("[JadeRenderer]")
        from pyjade.ext import mako
        from pyjade.ext.mako import preprocessor as mako_preprocessor
        from mako.template import Template

        self._render = Template
        self._engine = mako
        self._preprocessor = mako_preprocessor

    def default_file_extension(self):
        return ".jade"

    def __call__(self, filename, res):
        print("[JadeRenderer] ::", filename)
        tmpl = self._render(filename=filename, preprocessor=self._preprocessor)
        html = tmpl.render(**res.locals)
        return html

render_engine_map = {
    'mako': MakoRenderer,
    'jade': JadeRenderer
}
<commit_msg>Added documentation to the renderer middleware<commit_after>#
# growler/middleware/renderer.py
#

import asyncio
import os

class Renderer():
    """
    A growler-middleware class for changing template files into html (or any
    file format) to send back to the client.

    A renderer is created with the template directory and name of rendering
        engine as parameters.

    Upon being called in the middleware chain, the __call__ method will add a
    'render' function
    """

    render_engine_map = dict()

    def __init__(self, path, engine):
        """
        Construct a renderer.

        @param path: The directory containing the templates to render. If this
            is a list, it is automatically concatenated by os.path.join.

        @param engine: The rendering engine or the string key for the type of
            engine, details should be found in the documentation for that
            engine.
        """
        if isinstance(path, list):
            path = os.path.join(*path)

        self.path = os.path.abspath(path)

        if not os.path.exists(self.path):
            print("[Renderer] Error: No path exists at {}".format(self.path))
            raise Exception("Path '{}' does not exist.".format(self.path))

        print("[Renderer] Template files located in {}".format(self.path))

        if isinstance(engine, str):
            engine = render_engine_map.get(engine, None)

        if engine is None:
            raise Exception("[Renderer] No valid rendering engine provided.")

        self.engine = engine(self)

    def __call__(self, req, res):
        """
        The action of this middleware upon client request. The response is
        given a member 'locals' which house the local variables for use in the
        template, and a new method 'render' which takes a template file name
        (relative to the template directory given to the Render's constructor),
        a dict which will update any values in res.locals. After the engine
        runs on this file, the resulting html is sent to the client
        automatically, ending the res/req chain.
        """

        def _render(template, obj={}):
            res.locals.update(obj)
            filename = self._find_file(template)
            html = self.engine(filename, res)
            res.send_html(html)

        res.locals = {}
        res.render = _render

    def _find_file(self, fname):
        """
        Looks for the file with filename 'fname'
        """

        def next_file():
            filename = os.path.join(self.path, fname)
            yield filename
            yield filename + self.engine.default_file_extension

        for filename in next_file():
            if not os.path.isfile(filename):
                continue
            return filename

        raise Exception("No file found provided name '{}'.".format(fname))

class MakoRenderer():
    """
    The default 'Mako' renderer for the growler project. This class provides a
    reference on how template engines may work for rendering webpages with a
    growler app.
    """

    default_file_exension = '.mako'

    def __init__(self, renderer):
        """
        Construct the renderer, provided the parent renderer.
        """
        from mako.template import Template
        self._render = Template
        print("[MakoRenderer]")

    def __call__(self, filename, res):
        print("[MakoRenderer] CALL", filename)
        tmpl = self._render(filename=filename)
        html = tmpl.render()
        return html

class JadeRenderer():
    """
    A render engine using the pyjade package to render jade files into mako
    files, which are then turned into html by the make.template package.
    """

    default_file_extension = '.jade'

    def __init__(self, renderer):
        """
        Construct the renderer, provided the parent renderer.
        """
        from pyjade.ext import mako
        from pyjade.ext.mako import preprocessor as mako_preprocessor
        from mako.template import Template
        print("[JadeRenderer]")

        self._render = Template
        self._engine = mako
        self._preprocessor = mako_preprocessor

    def __call__(self, filename, res):
        print("[JadeRenderer] ::", filename)
        tmpl = self._render(filename=filename, preprocessor=self._preprocessor)
        html = tmpl.render(**res.locals)
        return html

render_engine_map = {
    'mako': MakoRenderer,
    'jade': JadeRenderer
}
<|endoftext|>


<commit_before>__author__ = 'lovci'

import argparse
import subprocess
import os

def construct_cutadapt_call(miRNA_seq, miRNA_id, fastq, output, **kwargs):
    miRNA_seq = miRNA_seq.replace("U", "T")
    return " ".join(['cutadapt',
                     '-g', 'N' + miRNA_seq, '-g', 'N' + miRNA_seq[1:], '-g', 'N' + miRNA_seq[2:],
                     '-g', 'N' + miRNA_seq[3:], '-g', 'N' + miRNA_seq[1:-1], '-g', 'N' + miRNA_seq[1:-2],
                     '-g', 'N' + miRNA_seq[:-2],
                     '--match-read-wildcards', '--trimmed-only',
                     '-m', '18', '-y', '\'.' + miRNA_id + '\'', '-f', 'fastq', '-O', '20', '-e', '0.05',
                     '-o', '\'' + output + '\'', '\'' + fastq + '\''])

def run_cutadapt(miRNA_seq, miRNA_id, fastq, output, **kwargs):
    call_this = construct_cutadapt_call(miRNA_seq, miRNA_id, fastq, output)
    outname = str(fastq) + "." + str(miRNA_id) + ".counts.txt"
    with open(str(outname), 'w') as output:
	server = subprocess.Popen(call_this, shell=True, stdout=output)
	server.communicate()

def count_lines(file_name):
    line_count = int(subprocess.check_output(['wc', '-l', file_name]).split(' ')[0])
    return line_count

parser = argparse.ArgumentParser(description='split a fastq file')
parser.add_argument('--miRNA_seq', dest='miRNA_seq', type=str,
                   help='DNA or RNA sequence of miRNA')

parser.add_argument('--miRNA_id', dest='miRNA_id', type=str,
                   help='name of miRNA')

parser.add_argument('--fastq', dest='fastq', type=str,
                   help='name of fastq to split')

parser.add_argument('--output', dest='output', type=str,
                   help='name of output fastq, after split')

parser.add_argument('--print_call', dest='print_call',
                    action='store_true', default=False,
                    help='only prints the call, doesn\'t actually run cutadapt')

args = vars(parser.parse_args())

if args['print_call']:
    print construct_cutadapt_call(**args)
else:
    run_cutadapt(**args)
    lc = count_lines(args['output'])

    try:
        assert lc >= 4 #one fastq entry
        print "output for %s on %s is %d lines long" %(args['miRNA_id'], args['fastq'], lc/4)
    except:
        os.remove(args['output'])
        raise AssertionError("output was too small for %s on %s, raising a failure status" % (args['miRNA_id'],
                                                                                args['fastq']))
<commit_msg>Add a #!/usr/bin/env python line at the top to specifically call python<commit_after>#!/usr/bin/env python

__author__ = 'lovci'

import argparse
import subprocess
import os

def construct_cutadapt_call(miRNA_seq, miRNA_id, fastq, output, **kwargs):
    miRNA_seq = miRNA_seq.replace("U", "T")
    return " ".join(['cutadapt',
                     '-g', 'N' + miRNA_seq, '-g', 'N' + miRNA_seq[1:], '-g', 'N' + miRNA_seq[2:],
                     '-g', 'N' + miRNA_seq[3:], '-g', 'N' + miRNA_seq[1:-1], '-g', 'N' + miRNA_seq[1:-2],
                     '-g', 'N' + miRNA_seq[:-2],
                     '--match-read-wildcards', '--trimmed-only',
                     '-m', '18', '-y', '\'.' + miRNA_id + '\'', '-f', 'fastq', '-O', '20', '-e', '0.05',
                     '-o', '\'' + output + '\'', '\'' + fastq + '\''])

def run_cutadapt(miRNA_seq, miRNA_id, fastq, output, **kwargs):
    call_this = construct_cutadapt_call(miRNA_seq, miRNA_id, fastq, output)
    outname = str(fastq) + "." + str(miRNA_id) + ".counts.txt"
    with open(str(outname), 'w') as output:
	server = subprocess.Popen(call_this, shell=True, stdout=output)
	server.communicate()

def count_lines(file_name):
    line_count = int(subprocess.check_output(['wc', '-l', file_name]).split(' ')[0])
    return line_count

parser = argparse.ArgumentParser(description='split a fastq file')
parser.add_argument('--miRNA_seq', dest='miRNA_seq', type=str,
                   help='DNA or RNA sequence of miRNA')

parser.add_argument('--miRNA_id', dest='miRNA_id', type=str,
                   help='name of miRNA')

parser.add_argument('--fastq', dest='fastq', type=str,
                   help='name of fastq to split')

parser.add_argument('--output', dest='output', type=str,
                   help='name of output fastq, after split')

parser.add_argument('--print_call', dest='print_call',
                    action='store_true', default=False,
                    help='only prints the call, doesn\'t actually run cutadapt')

args = vars(parser.parse_args())

if args['print_call']:
    print construct_cutadapt_call(**args)
else:
    run_cutadapt(**args)
    lc = count_lines(args['output'])

    try:
        assert lc >= 4 #one fastq entry
        print "output for %s on %s is %d lines long" %(args['miRNA_id'], args['fastq'], lc/4)
    except:
        os.remove(args['output'])
        raise AssertionError("output was too small for %s on %s, raising a failure status" % (args['miRNA_id'],
                                                                                args['fastq']))
<|endoftext|>


<commit_before>from StringIO import StringIO
from html2text import HTML2Text
from lxml import etree

def read_file(name):
    with open(name) as inp:
        result = inp.read()
    return result

# TODO: handle non-ascii chars
# TODO: set sub-head level based on map level
# TODO: Handle hyperlinks
# TODO: Handle images

def remove_redundant_newlines(text):
    return text.replace("\n\n[\n]*","\n\n")

class AbstractReformatter():
    def append_text(self, text):
        raise Exception('My subclass should implement this')

    def add_image(self, image_url):
        raise Exception('My subclass should implement this')

    def get_md(self):
        raise Exception('My subclass should implement this')

    def get_script(self):
        raise Exception('My subclass should implement this')

class LeanpubReformatter(AbstractReformatter):
    def __init__(self):
        self.result = StringIO()
        self.script = StringIO()

    def append_text(self, text):
        self.result.write(text)

    def add_image(self, image_location):
        if image_location.startswith('http:'):
            raise Exception('nonce error')
        self.script.write('cp %s ./manuscript/images/\n' % image_location)

    def get_md(self):
        return self.result.getvalue()

    def get_script(self):
        return self.script.getvalue()

class Converter():
    def __init__(self, xml, writer=LeanpubReformatter()):
        self.map_xml = xml
        self.writer = writer
        self.html_converter = HTML2Text(out=writer.append_text)

    def convert_node(self, node, depth=1):
        if node.get('TEXT'):
            self.writer.append_text('\n\n%s%s\n\n' % (depth*'#', node.get('TEXT')))
        self.convert_html_in(node)
        if node.get('LINK'):
            self.writer.add_image(node.get('LINK'))
        if len(node):
            for child in node:
                self.convert_node(child, depth + 1)

    def get_trimmed_markdown(self):
        return remove_redundant_newlines(self.writer.get_md())

    def convert_map(self):
        fm = etree.XML(self.map_xml)
        root = fm.find('node')
        for node in root:
            self.convert_node(node)

    def convert_html_in(self, node):
        html = node.find('richcontent')
        # first deal with notes
        if html is not None and len(html):
            html_text = etree.tostring(html)
            self.html_converter.handle(html_text)<commit_msg>Removed redundant Abstract Class.<commit_after>from StringIO import StringIO
from html2text import HTML2Text
from lxml import etree

def read_file(name):
    with open(name) as inp:
        result = inp.read()
    return result

# TODO: handle non-ascii chars
# TODO: set sub-head level based on map level
# TODO: Handle hyperlinks
# TODO: Handle images

def remove_redundant_newlines(text):
    return text.replace("\n\n[\n]*","\n\n")

class LeanpubReformatter():
    def __init__(self):
        self.result = StringIO()
        self.script = StringIO()

    def append_text(self, text):
        self.result.write(text)

    def add_image(self, image_location):
        if image_location.startswith('http:'):
            raise Exception('nonce error')
        self.script.write('cp %s ./manuscript/images/\n' % image_location)

    def get_md(self):
        return self.result.getvalue()

    def get_script(self):
        return self.script.getvalue()

class Converter():
    def __init__(self, xml, writer=LeanpubReformatter()):
        self.map_xml = xml
        self.writer = writer
        self.html_converter = HTML2Text(out=writer.append_text)

    def convert_node(self, node, depth=1):
        if node.get('TEXT'):
            self.writer.append_text('\n\n%s%s\n\n' % (depth*'#', node.get('TEXT')))
        self.convert_html_in(node)
        if node.get('LINK'):
            self.writer.add_image(node.get('LINK'))
        if len(node):
            for child in node:
                self.convert_node(child, depth + 1)

    def get_trimmed_markdown(self):
        return remove_redundant_newlines(self.writer.get_md())

    def convert_map(self):
        fm = etree.XML(self.map_xml)
        root = fm.find('node')
        for node in root:
            self.convert_node(node)

    def convert_html_in(self, node):
        html = node.find('richcontent')
        # first deal with notes
        if html is not None and len(html):
            html_text = etree.tostring(html)
            self.html_converter.handle(html_text)<|endoftext|>


<commit_before>import sys
import os
import platform
import subprocess
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QDockWidget, QDesktopWidget, QShortcut
from PyQt5.QtGui import QKeySequence
from filelistwidget import FileList
from sweep import Sweep
from mpllayout import MplLayout
from customdockwidget import CustomDockWidget

class FolderBrowser(QMainWindow):
    def __init__(self, n_layouts, dir_path, name_func_dict,
                 window_title='FolderBrowser'):
        QMainWindow.__init__(self)
        self.n_layouts = n_layouts
        self.dir_path = dir_path
        self.name_func_dict = name_func_dict
        self.setWindowTitle(window_title)
        self.dock_widgets = []
        self.init_statusbar()
        self.init_mpl_layouts()
        self.init_file_list()
        self.setDockNestingEnabled(True)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.set_hotkeys()
        self.show()

    def set_new_sweep(self):
        file_list_item = self.file_list.currentItem()
        sweep_path = file_list_item.data(QtCore.Qt.UserRole)
        self.sweep = Sweep(sweep_path)
        self.sweep.set_pdata(self.name_func_dict)
        title = self.compose_title(self.sweep, sweep_path)
        for mpl_layout in self.mpl_layouts:
            mpl_layout.set_title(title)
            mpl_layout.reset_and_plot(self.sweep)
        self.sweep_path = sweep_path

    def init_statusbar(self):
        self.statusBar = QtWidgets.QStatusBar()
        self.setStatusBar(self.statusBar)

    def init_mpl_layouts(self):
        self.mpl_layouts = [None] * self.n_layouts
        for i in range(self.n_layouts):
            self.mpl_layouts[i] = MplLayout(statusBar=self.statusBar,
                                            parent=self)
        for i, mpl_layout in enumerate(self.mpl_layouts):
            title = 'Plot {}'.format(i)
            dock_widget = CustomDockWidget(title, self)
            dock_widget.setWidget(mpl_layout)
            dock_widget.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
            self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock_widget)
            self.dock_widgets.append(dock_widget)
        self.set_active_layout(self.mpl_layouts[0])

    def init_file_list(self):
        self.file_list = FileList(self.dir_path)
        self.file_list.itemClicked.connect(self.set_new_sweep)
        dock_widget = QDockWidget('Browser', self)
        dock_widget.setWidget(self.file_list)
        dock_widget.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, dock_widget)
        self.dock_widgets.append(dock_widget)

    def reload_file_list(self):
        self.file_list.reload_items()

    def set_active_layout(self, layout):
        try:
            inactive_str = 'background-color: 10; border: none'
            self.active_layout.navi_toolbar.setStyleSheet(inactive_str)
        except AttributeError:
            pass
        active_str = 'background-color: lightblue; border: none'
        layout.navi_toolbar.setStyleSheet(active_str)
        self.active_layout = layout

    def set_hotkeys(self):
        self.copy_fig_hotkey = QShortcut(QKeySequence('Ctrl+c'), self)
        self.copy_fig_hotkey.activated.connect(self.copy_active_fig)
        self.open_folder_hotkey = QShortcut(QKeySequence('Ctrl+Shift+o'), self)
        self.open_folder_hotkey.activated.connect(self.open_folder)
        self.open_folder_hotkey = QShortcut(QKeySequence('Ctrl+Shift+r'), self)
        self.open_folder_hotkey.activated.connect(self.reload_file_list)

    def copy_active_fig(self):
        self.active_layout.copy_fig_to_clipboard()
        active_dock_widget = self.active_layout.parentWidget()
        title = active_dock_widget.windowTitle()
        msg = 'Figure in ' + title + ' copied to clipboard'
        self.statusBar.showMessage(msg, 1000)

    def open_folder(self):
        if platform.system() != 'Windows':
            err_msg = 'Open folder only implemented on Windows'
            self.statusBar.showMessage(err_msg)
            return
        norm_path = os.path.normpath(self.sweep_path)
        cmd = ['explorer', norm_path]
        subprocess.Popen(cmd)

    def set_name_func_dict(self, name_func_dict):
        self.name_func_dict = name_func_dict

    def showEvent(self, event):
        ava_space = QDesktopWidget().availableGeometry()
        self.move(ava_space.x()+0.5*ava_space.width(), 0)
        self.resize(ava_space.width()*0.49, ava_space.height()*0.96)
        self.set_new_sweep()

    @staticmethod
    def compose_title(sweep, sweep_path):
        sweep_name = sweep.meta['name']
        date_stamp = os.path.basename(sweep_path)
        return date_stamp + '\n' + sweep_name

if __name__=='__main__':
    from pcols import name_func_dict
    n_figs = 2
    data_path = 'C:/Dropbox/PhD/sandbox_phd/FolderBrowser/data'
    # data_path = 'C:/Dropbox/z_QDev_Morten_Hels/sandbox_phd/FolderBrowser/data'
    # data_path = 'D:/Qdev users/mchels/2016_08_09_cnt_gen5_FI/data/2016_08_09_initial'
    qApp = QtWidgets.QApplication(sys.argv)
    brw = FolderBrowser(n_figs, data_path, name_func_dict)
    sys.exit(qApp.exec_())
    #qApp.exec_()
<commit_msg>Don't resize GUI when it is shown initially<commit_after>import sys
import os
import platform
import subprocess
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QDockWidget, QDesktopWidget, QShortcut
from PyQt5.QtGui import QKeySequence
from filelistwidget import FileList
from sweep import Sweep
from mpllayout import MplLayout
from customdockwidget import CustomDockWidget

class FolderBrowser(QMainWindow):
    def __init__(self, n_layouts, dir_path, name_func_dict,
                 window_title='FolderBrowser'):
        QMainWindow.__init__(self)
        self.n_layouts = n_layouts
        self.dir_path = dir_path
        self.name_func_dict = name_func_dict
        self.setWindowTitle(window_title)
        self.dock_widgets = []
        self.init_statusbar()
        self.init_mpl_layouts()
        self.init_file_list()
        self.setDockNestingEnabled(True)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.set_hotkeys()
        self.show()

    def set_new_sweep(self):
        file_list_item = self.file_list.currentItem()
        sweep_path = file_list_item.data(QtCore.Qt.UserRole)
        self.sweep = Sweep(sweep_path)
        self.sweep.set_pdata(self.name_func_dict)
        title = self.compose_title(self.sweep, sweep_path)
        for mpl_layout in self.mpl_layouts:
            mpl_layout.set_title(title)
            mpl_layout.reset_and_plot(self.sweep)
        self.sweep_path = sweep_path

    def init_statusbar(self):
        self.statusBar = QtWidgets.QStatusBar()
        self.setStatusBar(self.statusBar)

    def init_mpl_layouts(self):
        self.mpl_layouts = [None] * self.n_layouts
        for i in range(self.n_layouts):
            self.mpl_layouts[i] = MplLayout(statusBar=self.statusBar,
                                            parent=self)
        for i, mpl_layout in enumerate(self.mpl_layouts):
            title = 'Plot {}'.format(i)
            dock_widget = CustomDockWidget(title, self)
            dock_widget.setWidget(mpl_layout)
            dock_widget.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
            self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock_widget)
            self.dock_widgets.append(dock_widget)
        self.set_active_layout(self.mpl_layouts[0])

    def init_file_list(self):
        self.file_list = FileList(self.dir_path)
        self.file_list.itemClicked.connect(self.set_new_sweep)
        dock_widget = QDockWidget('Browser', self)
        dock_widget.setWidget(self.file_list)
        dock_widget.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, dock_widget)
        self.dock_widgets.append(dock_widget)

    def reload_file_list(self):
        self.file_list.reload_items()

    def set_active_layout(self, layout):
        try:
            inactive_str = 'background-color: 10; border: none'
            self.active_layout.navi_toolbar.setStyleSheet(inactive_str)
        except AttributeError:
            pass
        active_str = 'background-color: lightblue; border: none'
        layout.navi_toolbar.setStyleSheet(active_str)
        self.active_layout = layout

    def set_hotkeys(self):
        self.copy_fig_hotkey = QShortcut(QKeySequence('Ctrl+c'), self)
        self.copy_fig_hotkey.activated.connect(self.copy_active_fig)
        self.open_folder_hotkey = QShortcut(QKeySequence('Ctrl+Shift+o'), self)
        self.open_folder_hotkey.activated.connect(self.open_folder)
        self.open_folder_hotkey = QShortcut(QKeySequence('Ctrl+Shift+r'), self)
        self.open_folder_hotkey.activated.connect(self.reload_file_list)

    def copy_active_fig(self):
        self.active_layout.copy_fig_to_clipboard()
        active_dock_widget = self.active_layout.parentWidget()
        title = active_dock_widget.windowTitle()
        msg = 'Figure in ' + title + ' copied to clipboard'
        self.statusBar.showMessage(msg, 1000)

    def open_folder(self):
        if platform.system() != 'Windows':
            err_msg = 'Open folder only implemented on Windows'
            self.statusBar.showMessage(err_msg)
            return
        norm_path = os.path.normpath(self.sweep_path)
        cmd = ['explorer', norm_path]
        subprocess.Popen(cmd)

    def set_name_func_dict(self, name_func_dict):
        self.name_func_dict = name_func_dict

    @staticmethod
    def compose_title(sweep, sweep_path):
        sweep_name = sweep.meta['name']
        date_stamp = os.path.basename(sweep_path)
        return date_stamp + '\n' + sweep_name

if __name__=='__main__':
    from pcols import name_func_dict
    n_figs = 2
    data_path = 'C:/Dropbox/PhD/sandbox_phd/FolderBrowser/data'
    # data_path = 'C:/Dropbox/z_QDev_Morten_Hels/sandbox_phd/FolderBrowser/data'
    # data_path = 'D:/Qdev users/mchels/2016_08_09_cnt_gen5_FI/data/2016_08_09_initial'
    qApp = QtWidgets.QApplication(sys.argv)
    brw = FolderBrowser(n_figs, data_path, name_func_dict)
    sys.exit(qApp.exec_())
    #qApp.exec_()
<|endoftext|>