Exemplo n.º 1
0
class WellCollection(ContainerCollection):
    title = 'Wells'
    root_name = 'wells'
    description = 'Manage plate wells'
    default_limit = 1536
    max_limit = 1536
    default_order = DescendingOrderSpecification('rack.barcode') \
                    & AscendingOrderSpecification('position')
Exemplo n.º 2
0
 def test_custom_clause(self):
     obj = object()
     spec = AscendingOrderSpecification('id')
     join_map = {'id': [obj]}
     visitor = self.MySqlOrderSpecificationVisitor(
         FooEntity, custom_join_clauses=join_map)
     visitor.visit_nullary(spec)
     self.assert_equal(visitor.get_joins(), set([obj]))
Exemplo n.º 3
0
class UserCollection(Collection):
    title = 'Users'
    root_name = 'users'
    description = 'Manage Users'
    default_order = AscendingOrderSpecification('username')

    def __getitem__(self, key):
        if key == 'current-user':
            user = get_current_user()
            if user == None:
                raise Forbidden()
            key = user.slug

        return Collection.__getitem__(self, key)
Exemplo n.º 4
0
 def test_basics(self):
     agg = StagingAggregate(MyEntity)
     ent0 = MyEntity(id=0, text='text0')
     ent1 = MyEntity(id=1, text='text1')
     agg.add(ent0)
     agg.add(ent1)
     q = agg.query()
     filter_expr = \
         EvalFilterExpression(ValueEqualToFilterSpecification('id', 0))
     self.assert_equal(q.filter(filter_expr).all(), [ent0])
     self.assert_equal(len(q.slice(1, 2).all()), 1)
     self.assert_equal(q.slice(1, 2).count(), 2)
     order_expr = EvalOrderExpression(AscendingOrderSpecification('text'))
     q = q.order(order_expr)
     self.assert_equal(q.all()[0].text, 'text0')
Exemplo n.º 5
0
 def test_custom_clause(self):
     obj = object()
     spec = AscendingOrderSpecification('id')
     join_map = {'id':[obj]}
     def get_item(key):
         return join_map[key]
     def contains(key):
         return key in join_map
     mock_join_map = MagicMock(spec=dict)
     mock_join_map.__getitem__.side_effect = get_item
     mock_join_map.__contains__.side_effect = contains
     visitor = self.MySqlOrderSpecificationVisitor(FooEntity,
                                                   custom_join_clauses=
                                                             mock_join_map)
     visitor.visit_nullary(spec)
     mock_join_map.__getitem__.assert_called_with('id')
Exemplo n.º 6
0
class DeviceTypeCollection(Collection):
    title = 'Device Types'
    root_name = 'device-types'
    description = 'Manage device types'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 7
0
class StockInfoCollection(Collection):
    title = 'Stock Info'
    root_name = 'stock-info'
    description = 'Stock related information'
    default_order = AscendingOrderSpecification('molecule_design_pool.id')
Exemplo n.º 8
0
class SubprojectCollection(Collection):
    title = 'Sub Projects'
    root_name = 'subprojects'
    description = 'Manage subprojects'
    default_order = AscendingOrderSpecification('project.label') \
                    & AscendingOrderSpecification('label')
Exemplo n.º 9
0
class WellSpecsCollection(ContainerSpecsCollection):
    title = 'Well Specs'
    root_name = 'well-specs'
    description = 'Manage well specifications'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 10
0
class ContainerSpecsCollection(Collection):
    title = 'Container Specs'
    root_name = 'container-specs'
    description = 'Manage container specifications'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 11
0
class SpeciesCollection(Collection):
    title = 'Species'
    root_name = 'species'
    description = 'Manage species'
    default_order = AscendingOrderSpecification('genus_name') \
                    & AscendingOrderSpecification('species_name')
Exemplo n.º 12
0
Arquivo: iso.py Projeto: papagr/TheLMA
class IsoCollection(Collection):
    title = 'ISOs'
    root_name = 'isos'
    description = 'Manage ISOs'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 13
0
 def test_order(self):
     txt_spec = AscendingOrderSpecification('text')
     txt_expr = EvalOrderExpression(txt_spec)
     id_spec = DescendingOrderSpecification('id')
     id_expr = EvalOrderExpression(id_spec)
     self._test_order(txt_expr, id_expr)
Exemplo n.º 14
0
class GeneCollection(Collection):
    title = 'Genes'
    root_name = 'genes'
    description = 'Manage Genes'
    default_order = AscendingOrderSpecification('accession')
Exemplo n.º 15
0
class PlateSpecsCollection(Collection):
    title = 'Plate Specs'
    root_name = 'plate-specs'
    description = 'Manage plate specifications'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 16
0
class TubeRackSpecsCollection(Collection):
    title = 'Tube Rack Specs'
    root_name = 'tube-rack-specs'
    description = 'Manage tube rack specifications'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 17
0
class Collection(Resource):
    """
    This is an abstract base class for all resource collections.
    A collection is a set of member resources which can be filtered, sorted,
    and sliced.
    """
    #: The title of the collection.
    title = None
    #: The name for the root collection (used as URL path to the root
    #: collection inside the service).
    root_name = None
    #: A description of the collection.
    description = ''
    #: The default order of the collection's members.
    default_order = AscendingOrderSpecification('id')
    #: The page size default for this collection.
    default_limit = 100
    #: The default maximum page size limit for this collection. Unless
    #: this is set in derived classes, no limit is enforced (i.e., the
    #: default maximum limit is None).
    max_limit = None

    def __init__(self, aggregate, name=None, relationship=None):
        """
        Constructor.

        :param str name: Name of the collection.
        :param aggregate: Associated aggregate.
        :type aggregate: :class:`everest.entities.aggregates.Aggregate` -
                an object implementing an interface derived from
                :class:`everest.entities.interfaces.IAggregate`.
        """
        if self.__class__ is Collection:
            raise NotImplementedError('Abstract class')
        Resource.__init__(self, relationship=relationship)
        if self.title is None:
            raise ValueError('Collection must have a title.')
        if name is None:
            name = self.root_name
        self.__name__ = name
        #: The filter specification for this resource. Attribute names in
        #: this specification are relative to the resource.
        self._filter_spec = None
        #: The order specification for this resource. Attribute names in
        #: this specification are relative to the resource.
        self._order_spec = None
        # The underlying aggregate.
        self.__aggregate = aggregate

    @classmethod
    def create_from_aggregate(cls, aggregate, relationship=None):
        """
        Creates a new collection from the given aggregate.

        :param aggregate: Aggregate containing the entities exposed by this
            collection resource.
        :param relationship: Resource relationship. If given, the root
            aggregate is converted to a relationship aggregate and the
            relationship is passed on to the collection class constructor.
        :type aggregate: :class:`everest.entities.aggregates.RootAggregate`
        """
        if not relationship is None:
            aggregate = aggregate.make_relationship_aggregate(
                relationship.domain_relationship)
        return cls(aggregate, relationship=relationship)

    def get_aggregate(self):
        """
        Returns the aggregate underlying this collection.

        :returns: Object implementing
            :class:`everest.entities.interfaces.IAggregate`.
        """
        return self.__aggregate

    def create_member(self, entity):
        """
        Creates a new member resource from the given entity and adds it to
        this collection.
        """
        member = as_member(entity)
        self.add(member)
        return member

    def load(self):
        """
        Loads this collection with the current filter, order, and slice
        settings. Future changes to these settings will not change the
        data returned by the various access methods.
        """
        self.__aggregate.load()

    def __len__(self):
        """
        Returns the size (count) of the collection.
        """
        return self.__aggregate.count()

    def __getitem__(self, key):
        """
        Gets a member (by name).

        :param key: Name of the member
        :type key: :class:`string` or :class:`unicode`
        :raises: :class:`everest.exceptions.MultipleResultsException` if more
          than one member is found for the given key value.
        :raises: KeyError If no entity is found for the given key.
        :returns: Object implementing
          :class:`everest.resources.interfaces.IMemberResource`.
        """
        ent = self.__aggregate.get_by_slug(key)
        if ent is None:
            raise KeyError(key)
        rc = as_member(ent, parent=self)
        return rc

    def __iter__(self):
        """
        Returns an iterator over the (possibly filtered and ordered)
        collection.
        """
        for obj in self.__aggregate.iterator():
            rc = as_member(obj, parent=self)
            yield rc

    def __contains__(self, member):
        """
        Checks if this collection contains the given member.

        :returns: `False` if a lookup of the ID of the given member returns
            `None` or if the ID is `None`; else, `True`.
        """
        return not (member.id is None \
                    or self.__aggregate.get_by_id(member.id) is None)

    def __str__(self):
        return "<%s name:%s parent:%s>" % (self.__class__.__name__,
                                           self.__name__, self.__parent__)

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

    def add(self, member):
        """
        Adds the given member to this collection.

        :param member: Member to add.
        :type member: Object implementing
                    :class:`everest.resources.interfaces.IMemberResource`
        :raise ValueError: if a member with the same name exists
        """
        if IMemberResource.providedBy(member):  #pylint: disable=E1101
            member.__parent__ = self
            data = member.get_entity()
        else:
            data = member
        self.__aggregate.add(data)

    def remove(self, member):
        """
        Removes the given member from this collection.

        :param member: Member to remove.
        :type member: Object implementing
                    :class:`everest.resources.interfaces.IMemberResource`
        :raise ValueError: if the member can not be found in this collection
        """
        is_member = IMemberResource.providedBy(member)  #pylint: disable=E1101
        if is_member:
            data = member.get_entity()
        else:
            data = member
        self.__aggregate.remove(data)
        if is_member:
            member.__parent__ = None

    def get(self, key, default=None):
        """
        Returns a member for the given key or the given default value if no
        match was found in the collection.
        """
        try:
            rc = self.__getitem__(key)
        except KeyError:
            rc = default
        return rc

    def update(self, data, target=None):
        """
        Updates this collection from the given data.

        :param data: Any object that can be adapted to
          :class:`everest.interfaces.IDataTraversalProxyAdapter`.
        :returns: New updated member.
        """
        if not target is None:
            target = target.get_entity()
        updated_entity = self.__aggregate.update(data, target=target)
        return as_member(updated_entity, parent=self)

    def _get_filter(self):
        if self._relationship is None:
            filter_spec = self._filter_spec
        else:
            rel_spec = self._relationship.specification
            if self._filter_spec is None:
                filter_spec = rel_spec
            else:
                spec_fac = get_filter_specification_factory()
                filter_spec = spec_fac.create_conjunction(
                    rel_spec, self._filter_spec)
        return filter_spec

    def _set_filter(self, filter_spec):
        if not filter_spec is None:
            # Translate to entity filter expression before passing on to the
            # aggregate.
            visitor = ResourceToEntityFilterSpecificationVisitor(
                get_member_class(self))
            filter_spec.accept(visitor)
            self.__aggregate.filter = visitor.expression
        else:
            self.__aggregate.filter = None
        self._filter_spec = filter_spec

    filter = property(_get_filter, _set_filter)

    def _get_order(self):
        return self._order_spec

    def _set_order(self, order_spec):
        if not order_spec is None:
            # Translate to entity order expression before passing on to the
            # aggregate.
            visitor = ResourceToEntityOrderSpecificationVisitor(
                get_member_class(self))
            order_spec.accept(visitor)
            self.__aggregate.order = visitor.expression
        else:
            self.__aggregate.order = None
        self._order_spec = order_spec

    order = property(_get_order, _set_order)

    def _get_slice(self):
        return self.__aggregate.slice

    def _set_slice(self, slice_key):
        self.__aggregate.slice = slice_key

    slice = property(_get_slice, _set_slice)

    def clone(self):
        """
        Returns a clone of this collection.
        """
        agg = self.__aggregate.clone()
        clone = self.create_from_aggregate(agg)
        # Pass filter and order specs explicitly (may differ from the ones
        # at the aggregate level).
        # pylint: disable=W0212
        clone._filter_spec = self._filter_spec
        clone._order_spec = self._order_spec
        # pylint: enable=W0212
        clone.__parent__ = self.__parent__
        return clone

    @property
    def is_root_collection(self):
        return self._relationship is None and not self.__parent__ is None

    def get_root_collection(self, rc):
        """
        Returns a root collection for the given resource.

        The root collection is created using a root aggregate fetched from
        the same repository that was used to create the root aggregate for
        this collection.
        """
        root_agg = self.__aggregate.get_root_aggregate(rc)
        coll_cls = get_collection_class(rc)
        return coll_cls.create_from_aggregate(root_agg)

    @classmethod
    def as_related_collection(cls, aggregate, relationship):
        """
        Creates a new relationship collection with a relationship aggregate
        and the relationship's relator as a parent.
        """
        rel_coll = cls.create_from_aggregate(aggregate,
                                             relationship=relationship)
        # The member at the origin of the relationship is the parent.
        rel_coll.__parent__ = relationship.relator
        # Set the collection's name to the descriptor's resource
        # attribute name.
        rel_coll.__name__ = \
            slug_from_identifier(relationship.descriptor.resource_attr)
        return rel_coll
Exemplo n.º 18
0
class ProjectCollection(Collection):
    title = 'Projects'
    root_name = 'projects'
    description = 'Manage Projects'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 19
0
class LocationCollection(Collection):
    title = 'Locations'
    root_name = 'locations'
    description = 'Manage locations where racks are held'
    default_order = AscendingOrderSpecification('type') \
                    & AscendingOrderSpecification('label')
Exemplo n.º 20
0
 def create_asc_order(self, attr_name):
     return AscendingOrderSpecification(attr_name)
Exemplo n.º 21
0
class LocationTypeCollection(Collection):
    title = 'Location Types'
    root_name = 'location-types'
    description = 'Manage location types'
    default_order = AscendingOrderSpecification('name')
Exemplo n.º 22
0
class ExperimentCollection(Collection):
    title = 'Experiments'
    root_name = 'experiments'
    description = 'Manage experiments'
    default_order = AscendingOrderSpecification('label')
Exemplo n.º 23
0
class OrganizationCollection(Collection):
    title = 'Organizations'
    root_name = 'organizations'
    description = 'Manage Organizations'
    default_order = AscendingOrderSpecification('name')
Exemplo n.º 24
0
class Collection(Resource):
    """
    This is an abstract base class for all resource collections.
    A collection is a set of member resources which can be filtered, sorted,
    and sliced.
    """
    implements(ICollectionResource)

    #: The title of the collection.
    title = None
    #: The name for the root collection (used as URL path to the root
    #: collection inside the service).
    root_name = None

    #: A description of the collection.
    description = ''
    #: The default order of the collection's members.
    default_order = AscendingOrderSpecification('id')
    #: The page size default for this collection.
    default_limit = 100
    #: The default maximum page size limit for this collection. Unless
    #: this is set in derived classes, no limit is enforced (i.e., the
    #: default maximum limit is None).
    max_limit = None

    def __init__(self, aggregate, name=None):
        """
        Constructor:

        :param name: the name of the collection
        :type name: :class:`string`
        :param aggregate: the associated aggregate
        :type aggregate: :class:`everest.entities.aggregates.Aggregate` -
                an object implementing an interface derived from
                :class:`everest.entities.interfaces.IAggregate`
        """
        if self.__class__ is Collection:
            raise NotImplementedError('Abstract class')
        if self.title is None:
            raise ValueError('Collection must have a title.')
        Resource.__init__(self)
        if name is None:
            name = self.root_name
        self.__name__ = name
        #: The filter specification for this resource. Attribute names in
        #: this specification are relative to the resource..
        self._filter_spec = None
        #: The order specification for this resource. Attribute names in
        #: this specification are relative to the resource.
        self._order_spec = None
        # The underlying aggregate.
        self.__aggregate = aggregate
        #
        self.__relationship = None

    @classmethod
    def create_from_aggregate(cls, aggregate):
        """
        Creates a new collection from the given aggregate.

        :param aggregate: aggregate containing the entities exposed by this
              collection resource
        :type aggregate: :class:`everest.entities.aggregates.Aggregate` instance
        """
        return cls(aggregate)

    def set_relationship(self, relationship):
        """
        Sets the relation parent for this collection.

        The relation parent affects the expressions built for filter and order 
        operations.

        :param relationship: relation with another resource, encapsulated in a
          :class:`everest.relationship.Relationship` instance.
        """
        self.__relationship = relationship

    def get_aggregate(self):
        """
        Returns the aggregate underlying this collection.

        :return: an object implementing
            :class:`everest.entities.interfaces.IAggregate`.
        """
        return self.__aggregate

    def create_member(self, entity):
        """
        Creates a new member resource from the given entity and adds it to
        this collection.
        """
        member = as_member(entity)
        self.add(member)
        return member

    @property
    def is_nested(self):
        return not self.__parent__ is get_service()

    def __len__(self):
        """
        Returns the size (count) of the collection.
        """
        return self.__aggregate.count()

    def __getitem__(self, key):
        """
        Gets a member (by name).

        :param key: the name of the member
        :type key: :class:`string` or :class:`unicode`
        :raises: :class:`everest.exceptions.DuplicateException` if more than
          one member is found for the given key value.
        :returns: object implementing
          :class:`everest.resources.interfaces.IMemberResource`
        """
        ent = self.__aggregate.get_by_slug(key)
        if ent is None:
            raise KeyError(key)
        rc = as_member(ent, parent=self)
        return rc

    def __iter__(self):
        """
        Returns an iterator over the (possibly filtered and ordered)
        collection.
        """
        for obj in self.__aggregate.iterator():
            rc = as_member(obj, parent=self)
            yield rc

    def __str__(self):
        return "<%s name:%s parent:%s>" % (self.__class__.__name__,
                                           self.__name__, self.__parent__)

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

    def add(self, member):
        """
        Adds the given member to this collection.

        :param member: member to add.
        :type member: object implementing
                    :class:`everest.resources.interfaces.IMemberResource`
        :raise ValueError: if a member with the same name exists
        """
        self.__aggregate.add(member.get_entity())
        member.__parent__ = self

    def remove(self, member):
        """
        Removes the given member from this collection.

        :param member: member to add.
        :type member: object implementing
                    :class:`everest.resources.interfaces.IMemberResource`
        :raise ValueError: if the member can not be found in this collection
        """
        self.__aggregate.remove(member.get_entity())
        member.__parent__ = None

    def get(self, key, default=None):
        """
        Returns a member for the given key or the given default value if no
        match was found in the collection.
        """
        try:
            rc = self.__getitem__(key)
        except KeyError:
            rc = default
        return rc

    def update_from_data(self, data_element):
        """
        Updates this collection from the given data element.

        This iterates over the members of this collection and checks if
        a member with the same ID exists in the given update data. If yes, 
        the existing member is updated with the update member; if no,
        the member is removed. All data elements in the update data that
        have no ID are added as new members. Data elements with an ID that
        can not be found in this collection trigger an error.

        :param data_element: data element (hierarchical) to create a resource
            from
        :type data_element: object implementing
         `:class:everest.resources.interfaces.IExplicitDataElement`
        :raises ValueError: when a data element with an ID that is not present
          in this collection is encountered.
        """
        attrs = data_element.mapping.get_attribute_map()
        id_attr = attrs['id']
        update_ids = set()
        new_mb_els = []
        self_id_map = dict([(self_mb.id, self_mb) for self_mb in iter(self)])
        for member_el in data_element.get_members():
#            if ILinkedDataElement in provided_by(member_el):
#                # Found a link - do not do anything.
#                mb_id = member_el.get_id()
#            else:
            mb_id = member_el.get_terminal(id_attr)
            if mb_id is None:
                # New data element without an ID - queue for adding.
                new_mb_els.append(member_el)
                continue
            else:
                self_mb = self_id_map.get(mb_id)
                if not self_mb is None:
                    # Found an existing member - update.
                    self_mb.update_from_data(member_el)
                else:
                    # New data element with a new ID. This is suspicious.
                    raise ValueError('New member data should not provide '
                                     'an ID attribute.')
            update_ids.add(mb_id)
        # Before adding any new members, check for delete operations.
        for self_mb in iter(self):
            if not self_mb.id in update_ids:
                # Found an existing member ID that was not supplied with
                # the update data- remove.
                self.remove(self_mb)
        # Now, add new members.
        mb_cls = get_member_class(self.__class__)
        for new_member_el in new_mb_els:
            new_member = mb_cls.create_from_data(new_member_el)
            self.add(new_member)

    def update_from_entity(self, member, source_entity):
        """
        Updates the given member from the given entity.
        
        Unlike :method:`update_from_data`, this completely disregards all
        resource attribute declarations and relies on the repository to
        perform the state update.
        
        :param member: Member resource to update.
        :param source_entity: Entity (domain object) to use
        """
        self.__aggregate.update(member.get_entity(), source_entity)

    def _get_filter(self):
        if self.__relationship is None:
            filter_spec = self._filter_spec
        else:
            # Prepend the relationship specification to the current filter
            # specification.
            if self._filter_spec is None:
                filter_spec = self.__relationship.specification
            else:
                spec_fac = get_filter_specification_factory()
                filter_spec = spec_fac.create_conjunction(
                                            self.__relationship.specification,
                                            self._filter_spec)
        return filter_spec

    def _set_filter(self, filter_spec):
        if not filter_spec is None:
            # Translate to entity filter expression before passing on to the
            # aggregate.
            visitor = ResourceToEntityFilterSpecificationVisitor(
                                                        get_member_class(self))
            filter_spec.accept(visitor)
            self.__aggregate.filter = visitor.expression
        else:
            self.__aggregate.filter = None
        self._filter_spec = filter_spec

    filter = property(_get_filter, _set_filter)

    def _get_order(self):
        return self._order_spec

    def _set_order(self, order_spec):
        if not order_spec is None:
            # Translate to entity order expression before passing on to the
            # aggregate.
            visitor = ResourceToEntityOrderSpecificationVisitor(
                                                        get_member_class(self))
            order_spec.accept(visitor)
            self.__aggregate.order = visitor.expression
        else:
            self.__aggregate.order = None
        self._order_spec = order_spec

    order = property(_get_order, _set_order)

    def _get_slice(self):
        return self.__aggregate.slice

    def _set_slice(self, slice_key):
        self.__aggregate.slice = slice_key

    slice = property(_get_slice, _set_slice)

    def clone(self):
        """
        Returns a clone of this collection.
        """
        agg = self.__aggregate.clone()
        clone = self.create_from_aggregate(agg)
        clone.__parent__ = self.__parent__
        clone.set_relationship(self.__relationship)
        # Pass filter and order specs explicitly (may differ from the ones
        # at the aggregate level).
        clone._filter_spec = self._filter_spec # pylint: disable=W0212
        clone._order_spec = self._order_spec # pylint: disable=W0212
        return clone
Exemplo n.º 25
0
class MoleculeTypeCollection(Collection):
    title = 'Molecule Types'
    root_name = 'molecule-types'
    description = 'Manage molecule types'
    default_order = AscendingOrderSpecification('name')