diff --git a/README.txt b/README.txt
index 11c4551..b162a1a 100755
--- a/README.txt
+++ b/README.txt
@@ -49,7 +49,7 @@ default predicate concept; the default name for this is 'standard'.
>>> from cybertools.relation.registry import DummyRelationRegistry
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
- >>> concepts['standard'] = Concept('parent')
+ >>> concepts['standard'] = Concept(u'subconcept')
Now we can assign the concept c2 as a child to c1 (using the standard
ConceptRelation):
@@ -103,7 +103,21 @@ We get a list of types using the ConceptTypeSourceList:
>>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types)
[u'Topic', u'Type', u'Unknown Type']
-
+
+Using a PredicateSourceList we can retrieve a list of the available
+predicates. In order for this to work we first have to assign our predicates
+a special concept type.
+
+ >>> concepts['predicate'] = Concept(u'Predicate')
+ >>> predicate = concepts['predicate']
+ >>> concepts['hasType'].conceptType = predicate
+ >>> concepts['standard'].conceptType = predicate
+
+ >>> from loops.concept import PredicateSourceList
+ >>> predicates = PredicateSourceList(cc1)
+ >>> sorted(t.title for t in predicates)
+ [u'has type', u'subconcept']
+
Concept Views
-------------
@@ -151,6 +165,24 @@ We can also create a new concept and assign it:
>>> sorted(c.title for c in cc1.getChildren())
[u'New concept', u'loops for Zope 3']
+The concept view provides methods for displaying concept types and
+predicates:
+
+ >>> from zope.publisher.interfaces.browser import IBrowserRequest
+ >>> from loops.browser.common import LoopsTerms
+ >>> from zope.app.form.browser.interfaces import ITerms
+ >>> from zope.schema.interfaces import IIterableSource
+ >>> ztapi.provideAdapter(IIterableSource, ITerms, LoopsTerms,
+ ... with=(IBrowserRequest,))
+
+ >>> sorted((t.title, t.token) for t in view.conceptTypes())
+ [(u'Topic', '.loops/concepts/topic'), (u'Type', '.loops/concepts/type'),
+ (u'Unknown Type', '.loops/concepts/unknown')]
+
+ >>> sorted((t.title, t.token) for t in view.predicates())
+ [(u'has type', '.loops/concepts/hasType'),
+ (u'subconcept', '.loops/concepts/standard')]
+
Searchable Text Adapter
-----------------------
@@ -406,7 +438,6 @@ objects.) The source is basically a source list:
The form then uses a sort of browser view providing the ITerms interface
based on this source list:
- >>> from loops.browser.common import LoopsTerms
>>> terms = LoopsTerms(source, TestRequest())
>>> term = terms.getTerm(doc1)
>>> term.token, term.title, term.value
@@ -471,7 +502,6 @@ A node's target is rendered using the NodeView's renderTargetBody()
method. This makes use of a browser view registered for the target interface,
and of a lot of other stuff needed for the rendering machine.
- >>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> from zope.app.publisher.interfaces.browser import IBrowserView
>>> from loops.browser.resource import DocumentView
>>> ztapi.provideAdapter(IDocument, Interface, DocumentView,
diff --git a/browser/concept.py b/browser/concept.py
index 0380335..6916d98 100644
--- a/browser/concept.py
+++ b/browser/concept.py
@@ -31,9 +31,11 @@ from zope.cachedescriptors.property import Lazy
from zope.event import notify
from zope.interface import implements
from zope.publisher.interfaces import BadRequest
+from zope.publisher.interfaces.browser import IBrowserRequest
from zope import schema
+from zope.schema.interfaces import IIterableSource
from zope.security.proxy import removeSecurityProxy
-from loops.concept import Concept
+from loops.concept import Concept, ConceptTypeSourceList, PredicateSourceList
from loops.browser.common import BaseView, LoopsTerms
@@ -46,30 +48,16 @@ class ConceptView(BaseView):
def parents(self):
for r in self.context.getParentRelations():
yield ConceptRelationView(r, self.request)
- #rels = self.context.getParentRelations()
- #result = []
- #for r in rels:
- # p = r.first
- #if p is None: # this should not be necessary
- # print 'Warning: parents() got a None first on', \
- # zapi.getName(self.context), zapi.getName(r.predicate)
- # continue
- # p.predicate = r.predicate
- #return result and self.viewIterator(result) or []
-
- def viewIterator(self, objs):
- request = self.request
- for o in objs:
- yield ConceptView(o, request)
def update(self):
- action = self.request.get('action')
+ request = self.request
+ action = request.get('action')
if action is None:
return True
if action == 'create':
self.createAndAssign()
return True
- tokens = self.request.get('tokens', [])
+ tokens = request.get('tokens', [])
for token in tokens:
parts = token.split(':')
token = parts[0]
@@ -77,16 +65,20 @@ class ConceptView(BaseView):
relToken = parts[1]
concept = self.loopsRoot.loopsTraverse(token)
if action == 'assign':
- assignAs = self.request.get('assignAs', 'child')
+ assignAs = request.get('assignAs', 'child')
+ predicate = request.get('predicate') or None
+ if predicate:
+ predicate = removeSecurityProxy(
+ self.loopsRoot.loopsTraverse(predicate))
if assignAs == 'child':
- self.context.assignChild(removeSecurityProxy(concept))
+ self.context.assignChild(removeSecurityProxy(concept), predicate)
elif assignAs == 'parent':
- self.context.assignParent(removeSecurityProxy(concept))
+ self.context.assignParent(removeSecurityProxy(concept), predicate)
else:
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
elif action == 'remove':
predicate = self.loopsRoot.loopsTraverse(relToken)
- qualifier = self.request.get('qualifier')
+ qualifier = request.get('qualifier')
if qualifier == 'parents':
self.context.deassignParent(concept, [predicate])
elif qualifier == 'children':
@@ -103,17 +95,23 @@ class ConceptView(BaseView):
if not name:
raise(BadRequest, 'Empty name.')
title = request.get('create.title', u'')
- type = request.get('create.type')
+ conceptType = request.get('create.type')
concept = Concept(title)
container = self.loopsRoot.getConceptManager()
container[name] = concept
- # TODO: notify ObjectCreatedEvent() (?)
- #notify(ObjectCreatedEvent(removeSecurityProxy(concept)))
+ if conceptType:
+ ctype = self.loopsRoot.loopsTraverse(conceptType)
+ concept.conceptType = ctype
+ notify(ObjectCreatedEvent(removeSecurityProxy(concept)))
assignAs = self.request.get('assignAs', 'child')
+ predicate = request.get('create.predicate') or None
+ if predicate:
+ predicate = removeSecurityProxy(
+ self.loopsRoot.loopsTraverse(predicate))
if assignAs == 'child':
- self.context.assignChild(removeSecurityProxy(concept))
+ self.context.assignChild(removeSecurityProxy(concept), predicate)
elif assignAs == 'parent':
- self.context.assignParent(removeSecurityProxy(concept))
+ self.context.assignParent(removeSecurityProxy(concept), predicate)
else:
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
@@ -127,8 +125,32 @@ class ConceptView(BaseView):
result = cat.searchResults(loops_searchableText=searchTerm)
else:
result = self.loopsRoot.getConceptManager().values()
+ searchType = request.get('searchType', '*')
+ # TODO: query catalog for type
+ if not searchType:
+ result = [r for r in result if r.conceptType is None]
+ elif searchType != '*':
+ type = self.loopsRoot.loopsTraverse(searchType)
+ result = [r for r in result if r.conceptType == type]
return self.viewIterator(result)
+ def viewIterator(self, objs):
+ request = self.request
+ for o in objs:
+ yield ConceptView(o, request)
+
+ def conceptTypes(self):
+ types = ConceptTypeSourceList(self.context)
+ terms = zapi.getMultiAdapter((types, self.request), ITerms)
+ for type in types:
+ yield terms.getTerm(type)
+
+ def predicates(self):
+ preds = PredicateSourceList(self.context)
+ terms = zapi.getMultiAdapter((preds, self.request), ITerms)
+ for pred in preds:
+ yield terms.getTerm(pred)
+
class ConceptRelationView(object):
diff --git a/browser/concept_related.pt b/browser/concept_related.pt
index 74840b3..92d0f40 100644
--- a/browser/concept_related.pt
+++ b/browser/concept_related.pt
@@ -12,6 +12,7 @@
qualifier string:parents;
summary string:Currently assigned objects;
legend string:Parent Concepts;
+ showPredicate string:yes;
buttonText string:Remove Parents;"
style="float:left; padding-right:20px">