provide basic functionality for loops.compound
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2262 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
d40012fb18
commit
433d930315
5 changed files with 199 additions and 17 deletions
|
@ -4,3 +4,73 @@ loops - Linked Objects for Organization and Processing Services
|
|||
|
||||
($Id$)
|
||||
|
||||
>>> from zope import component
|
||||
>>> from zope.traversing.api import getName
|
||||
|
||||
Let's set up a loops site with basic and example concepts and resources.
|
||||
|
||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||
>>> site = placefulSetUp(True)
|
||||
|
||||
>>> from loops.tests.setup import TestSite
|
||||
>>> t = TestSite(site)
|
||||
>>> concepts, resources, views = t.setup()
|
||||
|
||||
|
||||
Compund Objects - Hierarchies with Ordered Components
|
||||
=====================================================
|
||||
|
||||
>>> from loops.compound.base import Compound
|
||||
>>> component.provideAdapter(Compound)
|
||||
|
||||
>>> tType = concepts.getTypeConcept()
|
||||
>>> from loops.setup import addAndConfigureObject
|
||||
>>> from loops.concept import Concept
|
||||
>>> from loops.compound.interfaces import ICompound
|
||||
|
||||
We first create the compound type and one instance of the newly created
|
||||
type. We also need an ``ispartof`` predicate.
|
||||
|
||||
>>> tCompound = addAndConfigureObject(concepts, Concept, 'compound',
|
||||
... title=u'Compound',
|
||||
... conceptType=tType, typeInterface=ICompound)
|
||||
>>> c01 = addAndConfigureObject(concepts, Concept, 'c01',
|
||||
... title=u'Compound #01', conceptType=tCompound)
|
||||
>>> tPredicate = concepts.getPredicateType()
|
||||
>>> isPartof = addAndConfigureObject(concepts, Concept, 'ispartof',
|
||||
... title=u'is Part of', conceptType=tPredicate)
|
||||
|
||||
In order to access the compound concept's attributes we have to adapt
|
||||
it.
|
||||
|
||||
>>> from loops.common import adapted
|
||||
>>> aC01 = adapted(c01)
|
||||
|
||||
Now we are able to add resources to it.
|
||||
|
||||
>>> aC01.add(resources[u'd003.txt'])
|
||||
>>> aC01.add(resources[u'd001.txt'])
|
||||
|
||||
>>> [getName(p) for p in aC01.getParts()]
|
||||
[u'd003.txt', u'd001.txt']
|
||||
|
||||
>>> aC01.add(resources[u'd001.txt'], 0)
|
||||
>>> [getName(p) for p in aC01.getParts()]
|
||||
[u'd001.txt', u'd003.txt', u'd001.txt']
|
||||
|
||||
>>> aC01.add(resources[u'd002.txt'], -1)
|
||||
>>> [getName(p) for p in aC01.getParts()]
|
||||
[u'd001.txt', u'd003.txt', u'd002.txt', u'd001.txt']
|
||||
|
||||
We can reorder the parts of a compound.
|
||||
|
||||
>>> aC01.reorder([resources[u'd002.txt'], resources[u'd001.txt'], ])
|
||||
>>> [getName(p) for p in aC01.getParts()]
|
||||
[u'd002.txt', u'd001.txt', u'd003.txt', u'd001.txt']
|
||||
|
||||
And remove a part from the compound.
|
||||
|
||||
>>> aC01.remove(resources[u'd001.txt'], 1)
|
||||
>>> [getName(p) for p in aC01.getParts()]
|
||||
[u'd002.txt', u'd003.txt', u'd001.txt']
|
||||
|
||||
|
|
|
@ -22,3 +22,90 @@ Compound objects like articles, blog posts, storyboard items, ...
|
|||
$Id$
|
||||
"""
|
||||
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.interface import implements
|
||||
from zope.traversing.api import getName
|
||||
|
||||
from loops.common import AdapterBase
|
||||
from loops.compound.interfaces import ICompound, compoundPredicateName
|
||||
|
||||
|
||||
class Compound(AdapterBase):
|
||||
|
||||
implements(ICompound)
|
||||
|
||||
def getParts(self):
|
||||
return self.context.getChildren([self.partOf])
|
||||
|
||||
def add(self, obj, position=None):
|
||||
if position is None:
|
||||
order = self.getMaxOrder() + 1
|
||||
else:
|
||||
order = self.getOrderForPosition(position)
|
||||
self.context.assignChild(obj, self.partOf, order=order)
|
||||
|
||||
def remove(self, obj, position=None):
|
||||
if position is None:
|
||||
self.context.deassignChild(obj, [self.partOf])
|
||||
else:
|
||||
rel = self.getPartRelations()[position]
|
||||
self.context.deassignChild(obj, [self.partOf], order=rel.order)
|
||||
|
||||
def reorder(self, parts):
|
||||
existing = list(self.getPartRelations())
|
||||
order = 1
|
||||
for p in parts:
|
||||
for idx, x in enumerate(existing):
|
||||
if x.second == p:
|
||||
x.order = order
|
||||
order += 1
|
||||
del existing[idx]
|
||||
break
|
||||
else:
|
||||
raise ValueError("Part '%s' not in list of existing parts."
|
||||
% getName(p))
|
||||
for x in existing: # position the rest at the end
|
||||
x.order = order
|
||||
order += 1
|
||||
|
||||
# helper methods and properties
|
||||
|
||||
def getPartRelations(self):
|
||||
return self.context.getChildRelations([self.partOf])
|
||||
|
||||
def getMaxOrder(self):
|
||||
rels = self. getPartRelations()
|
||||
if rels:
|
||||
return max(r.order for r in rels)
|
||||
return 1
|
||||
|
||||
def getOrderForPosition(self, pos):
|
||||
rels = self. getPartRelations()
|
||||
if pos < 0:
|
||||
pos = len(rels) + pos
|
||||
previous = 0
|
||||
value = None
|
||||
for idx, r in enumerate(rels):
|
||||
if idx == pos: # position found
|
||||
if previous < r.order - 1: # space for a new entry
|
||||
value = previous + 1
|
||||
break
|
||||
value = r.order
|
||||
r.order += 1
|
||||
elif idx > pos:
|
||||
if previous < r.order - 1: # no renumber necessary any more
|
||||
break
|
||||
r.order += 1
|
||||
previous = r.order
|
||||
if value is None: # pos was greater than length, use last order found
|
||||
value = previous + 1
|
||||
return value
|
||||
|
||||
@Lazy
|
||||
def conceptManager(self):
|
||||
return self.context.getConceptManager()
|
||||
|
||||
@Lazy
|
||||
def partOf(self):
|
||||
return self.conceptManager[compoundPredicateName]
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
"""
|
||||
Compound objects like articles, blog posts, storyboard items, ...
|
||||
|
||||
This is somehow related to cybertools.composer - so maybe we should
|
||||
move part of the code defined here to that more generic package.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
|
@ -28,34 +31,55 @@ from zope import interface, component, schema
|
|||
from loops.util import _
|
||||
|
||||
|
||||
compoundPredicateName = 'ispartof'
|
||||
|
||||
|
||||
class ICompound(Interface):
|
||||
""" A compound is a concept that is built up of other objects, its
|
||||
components.
|
||||
parts or components.
|
||||
|
||||
These components are typically resources, but may also be other
|
||||
concepts that should provide the ICompound interface as their
|
||||
type interface. The components are assigned in an ordered way
|
||||
so that the components are accessed in a reproducible order.
|
||||
These parts are typically resources, but may also be other
|
||||
concepts that may/should provide the ICompound interface as their
|
||||
type interface. The parts are assigned in an ordered way
|
||||
so that the parts are accessed in a reproducible order.
|
||||
|
||||
The components are bound to the compound via standard resource
|
||||
The parts are bound to the compound via standard resource
|
||||
or concept relations using a special predicate, ``ispartof``.
|
||||
"""
|
||||
|
||||
components = Attribute('Objects (resources or other compounds) that '
|
||||
'this object consists of')
|
||||
def getParts():
|
||||
""" Return the objects (resources or other compounds) that
|
||||
this object consists of in the defined order.
|
||||
"""
|
||||
|
||||
def add(obj, position=None):
|
||||
""" Add a component to the compound.
|
||||
""" Add a part to the compound.
|
||||
|
||||
If the ``position`` argument is None, append it to the end
|
||||
of the current list of components, otherwise insert it before
|
||||
of the current list of parts, otherwise insert it before
|
||||
the position given. The numbering of the positions is like for
|
||||
Python lists, so 0 means before the first one, -1 before the
|
||||
last one.
|
||||
"""
|
||||
|
||||
def reorder(components):
|
||||
""" Change the order settings of the relations that connect the
|
||||
components given (a sequence) to the compound so that they are
|
||||
ordered according to this sequence.
|
||||
def remove(obj, position=None):
|
||||
""" Remove the object given from the context object's list of
|
||||
parts.
|
||||
|
||||
If the object given is not present raise an error. If there
|
||||
is more than one part relation to the object given
|
||||
all of them will be removed, except if the position argument
|
||||
is given in which case only the object at the given position
|
||||
will be removed.
|
||||
"""
|
||||
|
||||
def reorder(parts):
|
||||
""" Change the order settings of the relations that connect the
|
||||
parts given (a sequence) to the compound so that they are
|
||||
ordered according to this sequence.
|
||||
|
||||
If the parts`` argument contains a value that is not
|
||||
in the context objects`s list of parts raise an error. If the
|
||||
context object has parts that are not in the ``parts`` argument
|
||||
they will be moved to the end of the list.
|
||||
"""
|
||||
|
|
|
@ -197,10 +197,11 @@ class Concept(Contained, Persistent):
|
|||
def assignParent(self, concept, predicate=None, order=0, relevance=1.0):
|
||||
concept.assignChild(self, predicate, order, relevance)
|
||||
|
||||
def deassignChild(self, child, predicates=None):
|
||||
def deassignChild(self, child, predicates=None, order=None):
|
||||
registry = component.getUtility(IRelationRegistry)
|
||||
for rel in self.getChildRelations(predicates, child):
|
||||
registry.unregister(rel)
|
||||
if order is None or rel.order == order:
|
||||
registry.unregister(rel)
|
||||
|
||||
def deassignParent(self, parent, predicates=None):
|
||||
parent.deassignChild(self, predicates)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
Loading…
Add table
Reference in a new issue