move QueryConcept stuff to loops.expert (keeping old query module for backward compatibility); work in progress: child-based queries with actions

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2935 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-10-23 13:35:23 +00:00
parent c2a16063c5
commit d1dbe47d57
33 changed files with 471 additions and 87 deletions

View file

@ -37,7 +37,7 @@ top-level loops container and a concept manager:
>>> #sorted(concepts)
>>> #sorted(resources)
>>> len(concepts) + len(resources)
14
13
>>> loopsRoot = site['loops']
@ -121,7 +121,7 @@ type manager.
>>> from loops.concept import ConceptTypeSourceList
>>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types)
[u'Customer', u'Domain', u'Predicate', u'Query', u'Topic', u'Type', u'Unknown Type']
[u'Customer', u'Domain', u'Predicate', u'Topic', u'Type', u'Unknown Type']
Using a PredicateSourceList we can retrieve a list of the available
predicates.
@ -197,7 +197,6 @@ types and predicates.
[(u'Customer', '.loops/concepts/customer'),
(u'Domain', '.loops/concepts/domain'),
(u'Predicate', '.loops/concepts/predicate'),
(u'Query', '.loops/concepts/query'),
(u'Topic', '.loops/concepts/topic'),
(u'Type', '.loops/concepts/type'),
(u'Unknown Type', '.loops/concepts/unknown')]
@ -487,7 +486,7 @@ view; these views we have to provide as multi-adapters:
>>> view = ConfigureView(m111, TestRequest(form = form))
>>> tt = view.targetTypes()
>>> len(tt)
10
9
>>> sorted((t.token, t.title) for t in view.targetTypes())[1]
('.loops/concepts/domain', u'Domain')
>>> view.update()
@ -580,7 +579,7 @@ is ``NodeView.getUrlForTarget()`` that expects a ConceptView, ResourceView,
or ConceptRelationView as its argument.
>>> view.getUrlForTarget(childRels[0])
'http://127.0.0.1/loops/views/m1/m11/m112/.target39'
'http://127.0.0.1/loops/views/m1/m11/m112/.target37'
Actions
-------

View file

@ -121,9 +121,10 @@
tal:define="resources python: list(item.resources())"
tal:condition="resources">
<h2 i18n:translate="">Resources</h2>
<table class="listing">
<table class="listing"
metal:define-macro="resources">
<tr>
<th tal:condition="view/showCheckboxes|nothing">&nbsp;</th>
<th tal:condition="item/showCheckboxes|nothing">&nbsp;</th>
<th i18n:translate="">Title</th>
<th i18n:translate="">Type</th>
<th i18n:translate=""
@ -138,8 +139,11 @@
<tal:item define="class python: repeat['related'].odd() and 'even' or 'odd';
description related/description">
<tr tal:attributes="class class">
<td tal:condition="view/showCheckboxes|nothing"
class="checkbox"><input type="checkbox" /></td>
<td tal:condition="item/showCheckboxes|nothing"
tal:define="uid related/uniqueId"
class="checkbox">
<input type="checkbox" name="selection:list" checked
tal:attributes="value uid;" /></td>
<td valign="top">
<a href="#"
tal:attributes="href string:${view/url}/.target${related/uniqueId};

View file

@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts), len(resources)
(21, 0)
(20, 0)
Let's now add an external collection that reads in a set of resources
from external files so we have something to work with.

View file

@ -34,7 +34,8 @@ from cybertools.meta.interfaces import IOptions
from cybertools.meta.namespace import Executor, ExecutionError
from cybertools.typology.interfaces import IType
from loops.interfaces import ILoops, ILoopsObject
from loops.query import IQueryConcept
#from loops.query import IQueryConcept
from loops.expert.concept import IQueryConcept
from loops import util

View file

@ -17,4 +17,8 @@
<allow interface="cybertools.meta.interfaces.IOptions" />
</zope:class>
<!-- backward compatibility -->
<zope:adapter factory="loops.config.base.QueryOptions" trusted="True"
for="loops.query.IQueryConcept" />
</configure>

View file

@ -436,6 +436,7 @@
<include package=".compound.blog" />
<include package=".config" />
<include package=".constraint" />
<include package=".expert" />
<include package=".external" />
<include package=".i18n" />
<include package=".integrator" />

View file

@ -9,7 +9,7 @@ type(u'note', u'Note', options=u'', typeInterface='loops.interfaces.INote', view
type(u'person', u'Person', options=u'', typeInterface='loops.knowledge.interfaces.IPerson', viewName=u'')
type(u'predicate', u'Predicate', options=u'', typeInterface=u'', viewName=u'')
type(u'process', u'Prozess', options=u'', typeInterface=u'', viewName=u'')
type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept', viewName=u'')
type(u'query', u'Query', options=u'', typeInterface='loops.expert.concept.IQueryConcept', viewName=u'')
type(u'textdocument', u'Text', options=u'', typeInterface='loops.interfaces.ITextDocument', viewName=u'')
type(u'topic', u'Topic', options=u'', typeInterface='loops.knowledge.interfaces.ITopic', viewName=u'')
type(u'type', u'Type', options=u'', typeInterface='loops.interfaces.ITypeConcept', viewName=u'')

View file

@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
32
33
>>> loopsRoot = site['loops']
@ -47,19 +47,19 @@ Type- and text-based queries
>>> from loops.expert import query
>>> qu = query.Title('ty*')
>>> list(qu.apply())
[0, 1, 45]
[0, 1, 47]
>>> qu = query.Type('loops:*')
>>> len(list(qu.apply()))
32
33
>>> qu = query.Type('loops:concept:predicate')
>>> len(list(qu.apply()))
6
7
>>> qu = query.Type('loops:concept:predicate') & query.Title('t*')
>>> list(qu.apply())
[1]
[1, 43]
State-based queries
-------------------
@ -99,7 +99,7 @@ they have at least one concept assigned).
>>> qu = query.State('classification_quality', 'classified')
>>> list(qu.apply())
[23, 25, 27]
[21, 23, 25]
Using the stateful adapter for a resource we now manually execute the
``verify`` transition.
@ -113,16 +113,16 @@ Now only two resources are still in the ``qualified`` state, the changed
one being in the ``verified`` state.
>>> list(qu.apply())
[25, 27]
[23, 25]
>>> qu = query.State('classification_quality', 'verified')
>>> list(qu.apply())
[23]
[21]
We may also provide a sequence of states for querying.
>>> qu = query.State('classification_quality', ('classified', 'verified',))
>>> list(qu.apply())
[23, 25, 27]
[21, 23, 25]
Relationship-based queries
--------------------------
@ -135,7 +135,7 @@ syntax (that in turn is based on hurry.query).
>>> cust1 = concepts['cust1']
>>> qu = query.Resources(cust1)
>>> list(qu.apply())
[23, 27]
[21, 25]
Getting objects
---------------
@ -208,6 +208,15 @@ A query instance consists of
>>> #qi.apply()
Query Concepts and Query Views
==============================
>>> from loops.expert.concept import QueryConcept
>>> component.provideAdapter(QueryConcept)
>>> from loops.expert.browser.base import BaseQueryView
Fin de partie
=============

View file

@ -26,8 +26,12 @@ $Id$
from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.traversing.api import getName, getParent
from loops.browser.common import BaseView
from cybertools.browser.form import FormController
from loops.browser.common import BaseView, concept_macros
from loops.browser.concept import ConceptView
from loops.browser.resource import ResourceView, ResourceRelationView
from loops.common import adapted
from loops import util
from loops.util import _
@ -36,11 +40,65 @@ from loops.util import _
queryTemplate = ViewPageTemplateFile('query.pt')
#class BaseQueryView(ConceptView):
class BaseQueryView(BaseView):
template = queryTemplate
childViewFactory = ResourceRelationView
showCheckboxes = True
form_action = 'execute_query_action'
@Lazy
def macro(self):
return template.macros['query']
return self.template.macros['query']
@property
def infos(self):
return concept_macros.macros
@property
def listings(self):
return concept_macros.macros
@property
def listings(self):
return concept_macros.macros
@Lazy
def targetPredicate(self):
return self.conceptManager['querytarget']
@Lazy
def defaultPredicate(self):
return self.conceptManager.getDefaultPredicate()
@Lazy
def targets(self):
return self.context.getChildren([self.targetPredicate])
def queryInfo(self):
targetNames = ', '.join(["'%s'" % t.title for t in self.targets])
return _(u'Selection using: $targets',
mapping=dict(targets=targetNames))
def results(self):
for t in self.targets:
for r in t.getResourceRelations([self.defaultPredicate]):
yield self.childViewFactory(r, self.request, contextIsSecond=True)
class ActionExecutor(FormController):
def update(self):
form = self.request.form
actions = [k for k in form.keys() if k.startswith('action.')]
if actions:
uids = form.get('selection', [])
action = actions[0]
if action == 'action.delete':
print '*** delete', uids
for uid in uids:
obj = util.getObjectForUid(uid)
parent = getParent(obj)
del parent[getName(obj)]
return True

View file

@ -6,7 +6,7 @@
i18n_domain="loops">
<zope:adapter
name="query"
name="query.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"

View file

@ -1,10 +1,20 @@
<metal:query define-macro="query">
<div id="query">
<h1 tal:attributes="class string:content-$level;
ondblclick item/openEditWindow"
tal:content="item/title">
Query
</h1>
<div id="query"
tal:define="resources item/results"
tal:attributes="class string:content-$level;">
<form method="post">
<input type="hidden" name="form.action"
tal:attributes="value item/form_action" />
<metal:infos use-macro="item/infos/concepttitle" />
<div tal:content="item/queryInfo" />
<metal:listing use-macro="item/listings/resources" /><br />
<div class="buttons">
<input type="submit" name="action.delete"
value="Delete objects" class="submit"
onClick="confirm('Do you really want to delete the selected objects?')"
i18n:attributes="value" />
</div>
</form>
</div>
</metal:query>

211
expert/concept.py Normal file
View file

@ -0,0 +1,211 @@
#
# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Query concepts management stuff.
$Id$
"""
from BTrees.IOBTree import IOBTree
from BTrees.IFBTree import weightedIntersection, weightedUnion, IFBucket
from zope import schema, component
from zope.interface import Interface, Attribute, implements
from zope.app.catalog.interfaces import ICatalog
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy
from cybertools.typology.interfaces import IType
from loops.common import AdapterBase
from loops.interfaces import IConcept, IConceptSchema
from loops.security.common import canListObject
from loops.type import TypeInterfaceSourceList
from loops.versioning.util import getVersion
from loops import util
from loops.util import _
class ScoredSet(set):
def __init__(self, data=set(), scores={}):
super(ScoredSet, self).__init__(data)
self.scores = scores
def getScore(self, obj):
return self.scores.get(obj, -1)
class BaseQuery(object):
# TODO: replace with proper use of loops.expert.query
def __init__(self, context):
self.context = context
@Lazy
def catalog(self):
return component.getUtility(ICatalog)
@Lazy
def loopsRoot(self):
return self.context.context.getLoopsRoot()
def queryConcepts(self, title=None, type=None, **kw):
if type.endswith('*'):
start = type[:-1]
end = start + '\x7f'
else:
start = end = type
cat = self.catalog
if title:
result = cat.searchResults(loops_type=(start, end), loops_title=title)
else:
result = cat.searchResults(loops_type=(start, end))
result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot
and canListObject(r))
if 'exclude' in kw:
r1 = set()
for r in result:
qur = IType(r).qualifiers
if not [qux for qux in kw['exclude'] if qux in qur]:
r1.add(r)
result = r1
return result
def queryConceptsWithChildren(self, title=None, type=None, uid=None, **kw):
if title: # there are a few characters that the index doesn't like
title = title.replace('(', ' ').replace(')', ' ')
if not title and not uid and (type is None or '*' in type):
return None
result = set()
if not uid:
queue = list(self.queryConcepts(title=title, type=type, **kw))
else:
queue = [util.getObjectForUid(uid)]
concepts = []
while queue:
c = queue.pop(0)
concepts.append(c)
for child in c.getChildren():
# TODO: check for tree level, use relevance factors, ...
if child not in queue and child not in concepts:
queue.append(child)
for c in concepts:
result.add(c)
result.update(getVersion(r) for r in c.getResources())
return result
class FullQuery(BaseQuery):
def query(self, text=None, type=None, useTitle=True, useFull=False,
conceptTitle=None, conceptUid=None, conceptType=None, **kw):
result = set()
scores = {}
intids = component.getUtility(IIntIds)
rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid,
type=conceptType)
if not rc and not text and '*' in type: # there should be some sort of selection...
return ScoredSet(result, scores)
if text or type != 'loops:*': # TODO: this may be highly inefficient!
cat = self.catalog
if type.endswith('*'):
start = type[:-1]
end = start + '\x7f'
else:
start = end = type
criteria = {'loops_type': (start, end),}
if useFull and text:
criteria['loops_text'] = text
r1 = cat.apply(criteria) #r1 = set(cat.searchResults(**criteria))
else:
r1 = IFBucket() #r1 = set()
if useTitle and text:
if 'loops_text' in criteria:
del criteria['loops_text']
criteria['loops_title'] = text
r2 = cat.apply(criteria) #r2 = set(cat.searchResults(**criteria))
else:
r2 = IFBucket() #r2 = set()
if not r1 and not r2:
r1 = cat.apply(criteria) # search only for type
x, uids = weightedUnion(r1, r2) #result = r1.union(r2)
for r, score in uids.items():
obj = intids.getObject(r)
result.add(obj)
scores[obj] = score
if rc is not None:
if result:
result = result.intersection(rc)
else:
result = rc
result = set(r for r in result
if r.getLoopsRoot() == self.loopsRoot
and canListObject(r)
and getVersion(r) == r)
return ScoredSet(result, scores)
class ConceptQuery(BaseQuery):
""" Find concepts of type `type` whose title starts with `title`.
"""
def query(self, title=None, type=None, **kw):
if title and not title.endswith('*'):
title += '*'
return self.queryConcepts(title=title, type=type, **kw)
# QueryConcept: concept objects that allow querying the database.
class IQueryConcept(IConceptSchema):
""" The schema for the query type.
"""
viewName = schema.TextLine(
title=_(u'Adapter/View name'),
description=_(u'The name of the (multi-) adapter (typically a view) '
'to be used for the query and for presenting '
'the results'),
default=u'',
required=True)
options = schema.List(
title=_(u'Options'),
description=_(u'Additional settings.'),
value_type=schema.TextLine(),
default=[],
required=False)
class QueryConcept(AdapterBase):
implements(IQueryConcept)
_contextAttributes = AdapterBase._contextAttributes + ['viewName']
_adapterAttributes = AdapterBase._adapterAttributes + ('options',)
def getOptions(self):
return getattr(self.context, '_options', [])
def setOptions(self, value):
self.context._options = value
options = property(getOptions, setOptions)
TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,)

27
expert/configure.zcml Normal file
View file

@ -0,0 +1,27 @@
<!-- $Id$ -->
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="loops">
<adapter factory="loops.expert.concept.QueryConcept" trusted="True" />
<class class="loops.expert.concept.QueryConcept">
<require permission="zope.View"
interface="loops.expert.concept.IQueryConcept" />
<require permission="zope.ManageContent"
set_schema="loops.expert.concept.IQueryConcept" />
</class>
<adapter name="execute_query_action"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.expert.browser.base.ActionExecutor"
permission="zope.ManageContent" />
<adapter factory="loops.expert.setup.SetupManager"
name="expert" />
<include package=".browser" />
</configure>

42
expert/setup.py Normal file
View file

@ -0,0 +1,42 @@
#
# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Automatic setup of a loops site for the expert package.
$Id$
"""
from loops.concept import Concept
from loops.expert.concept import IQueryConcept
from loops.setup import SetupManager as BaseSetupManager
class SetupManager(BaseSetupManager):
def setup(self):
concepts = self.context.getConceptManager()
type = concepts.getTypeConcept()
predicate = concepts['predicate']
# type concepts:
queryConcept = self.addAndConfigureObject(concepts, Concept, 'query',
title=u'Query', conceptType=type,
typeInterface=IQueryConcept)
# predicates:
queryTarget = self.addObject(concepts, Concept, 'querytarget',
title=u'is Query Target', conceptType=predicate)

View file

@ -10,6 +10,7 @@ from zope.app.catalog.interfaces import ICatalog
from cybertools.typology.interfaces import IType
from loops import util
from loops.concept import Concept
from loops.expert.setup import SetupManager as ExpertSetupManager
from loops.resource import Resource
from loops.knowledge.interfaces import IPerson
from loops.knowledge.knowledge import Person
@ -32,6 +33,7 @@ class TestSite(BaseTestSite):
component.provideAdapter(Person, provides=IPerson)
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
component.provideAdapter(ExpertSetupManager, name='expert')
setup = SetupManager(loopsRoot)
concepts, resources, views = setup.setup()

16
external/README.txt vendored
View file

@ -12,12 +12,12 @@ 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
>>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
>>> len(concepts), len(resources), len(views)
(11, 3, 0)
(30, 3, 0)
Importing loops Objects
@ -44,7 +44,7 @@ Creating the corresponding objects
>>> loader = Loader(loopsRoot)
>>> loader.load(elements)
>>> len(concepts), len(resources), len(views)
(12, 3, 0)
(31, 3, 0)
>>> from loops.common import adapted
>>> adMyquery = adapted(concepts['myquery'])
@ -118,7 +118,7 @@ Extracting elements
>>> extractor = Extractor(loopsRoot, os.path.join(dataDirectory, 'export'))
>>> elements = list(extractor.extract())
>>> len(elements)
20
52
Writing object information to the external storage
--------------------------------------------------
@ -130,13 +130,13 @@ Writing object information to the external storage
>>> writer = PyWriter()
>>> writer.write(elements, output)
>>> print output.getvalue()
type(u'customer', u'Customer', options=u'', typeInterface=u'', viewName=u'')...
type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept',
type(u'country', u'Country', options=u'', typeInterface=u'', viewName=u'')...
type(u'query', u'Query', options=u'', typeInterface='loops.expert.concept.IQueryConcept',
viewName=u'')...
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
viewName=u'mystuff.html')...
child(u'projects', u'customer', u'standard')...
resource(u'doc04.txt', u'Document 4', u'textdocument', contentType='text/restructured')
resource(u'doc04.txt', u'Document 4', u'textdocument', contentType='text/restructured')...
resourceRelation(u'myquery', u'doc04.txt', u'standard')
node('home', u'Home', '', u'menu', body=u'Welcome')
node('myquery', u'My Query', 'home', u'page', target=u'concepts/myquery')...
@ -172,7 +172,7 @@ corresponding extractor adapter.
>>> PyWriter().write(extractor.extract(), output)
>>> print output.getvalue()
type(u'customer', u'Customer', options=u'', typeInterface=u'', viewName=u'')...
type(u'country', u'Country', options=u'', typeInterface=u'', viewName=u'')...
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
viewName=u'mystuff.html')[
annotations(creators=(u'john',))]...

View file

@ -226,7 +226,7 @@ get a type manager from all loops objects, always with the same context:
>>> types = typeManager.types
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
9
8
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
True
@ -237,7 +237,7 @@ condition:
>>> types = typeManager.listTypes(include=('concept',))
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
6
5
>>> types = typeManager.listTypes(exclude=('concept',))
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
@ -313,9 +313,11 @@ We first have to set up the query type, i.e. a type concept associated
with the IQueryConcept interface. The query type concept itself has already
been provided by the setup, but we have to register a corresponding adapter.
>>> from loops.query import QueryConcept
>>> from loops.expert.concept import IQueryConcept, QueryConcept
>>> component.provideAdapter(QueryConcept)
>>> query = concepts['query']
>>> from loops.setup import addAndConfigureObject
>>> query = addAndConfigureObject(concepts, Concept,
... 'query', conceptType=typeObject, typeInterface=IQueryConcept)
Next we need a concept of this type:
@ -423,7 +425,6 @@ Folders
structures of their documents in the filesystem to the loops concept
map.
>>> from loops.setup import addAndConfigureObject
>>> tFolder = addAndConfigureObject(concepts, Concept, 'folder',
... title=u'Folder', conceptType=typeObject)

View file

@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
18
17
External Collections

View file

@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
14
13
Accessing a Directory in the Filesystem

View file

@ -745,6 +745,14 @@ class IExternalFile(IFile):
missing_value='',
required=False)
externalAddress = schema.BytesLine(
title=_(u'External Address'),
description=_(u'The full address for accessing the object '
'on the external storage, e.g. a filename or path.'),
default='',
missing_value='',
required=False)
class IImage(IResourceAdapter):
""" A media asset that may be embedded in a (web) page as an image.

View file

@ -18,7 +18,7 @@ ZCML setup):
>>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize')
>>> from loops.tests.setup import TestSite
>>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
@ -237,7 +237,7 @@ Automatic security settings on persons
>>> from zope.traversing.api import getName
>>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
[u'john', u'martha', u'person.newuser']
[u'jim', u'john', u'martha', u'person.newuser']
Person objects that have a user assigned to them receive this user
(principal) as their owner.

View file

@ -72,10 +72,10 @@ So we are now ready to query the favorites.
>>> favs = list(favorites.query(userName=johnCId))
>>> favs
[<Favorite ['29', 1, '35', '...']: {}>]
[<Favorite ['27', 1, '33', '...']: {}>]
>>> list(favAdapted.list(johnC))
['29']
['27']
>>> util.getObjectForUid(favs[0].taskId) is resources['d001.txt']
True

View file

@ -14,7 +14,7 @@ First we set up a loops site with basic and example concepts and resources.
>>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize')
>>> from loops.tests.setup import TestSite
>>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
@ -40,7 +40,11 @@ making an object statful we'll use an adapter.
We may now take a document and adapt it to IStateful so that we may
check the document's state and perform transitions to other states.
>>> doc01 = resources['d001.txt']
>>> from loops.resource import Resource
>>> from loops.setup import addAndConfigureObject
>>> tText = concepts['textdocument']
>>> doc01 = resources['doc01.txt'] = addAndConfigureObject(resources,
... Resource, 'doc01.txt', conceptType=tText)
>>> statefulDoc01 = component.getAdapter(doc01, IStateful,
... name='simple_publishing')
@ -88,7 +92,6 @@ later.
>>> tCustomer = concepts['customer']
>>> from loops.concept import Concept
>>> from loops.setup import addAndConfigureObject
>>> c01 = addAndConfigureObject(concepts, Concept, 'c01', conceptType=tCustomer,
... title='im publishing')
>>> c02 = addAndConfigureObject(concepts, Concept, 'c02', conceptType=tCustomer,

View file

@ -357,7 +357,6 @@ class ExternalFileAdapter(FileAdapter):
def getExternalAddress(self):
return getattr(self.context, '_externalAddress', self.context.__name__)
def setExternalAddress(self, addr):
# TODO (?) - use intId as default?
self.context._externalAddress = addr
externalAddress = property(getExternalAddress, setExternalAddress)

View file

@ -35,7 +35,7 @@ ZCML setup):
Let's look what setup has provided us with:
>>> sorted(concepts)
[u'domain', u'file', u'hasType', u'note', u'predicate', u'query',
[u'domain', u'file', u'hasType', u'note', u'predicate',
u'standard', u'textdocument', u'type']

View file

@ -47,8 +47,10 @@ class FileSchemaFactory(SchemaFactory):
def __call__(self, interface, **kw):
schema = super(FileSchemaFactory, self).__call__(interface, **kw)
if 'request' in kw and kw['request'].principal.id != 'rootadmin':
schema.fields.remove('contentType')
if 'request' in kw:
principal = kw['request'].principal
if not principal or principal.id != 'rootadmin':
schema.fields.remove('contentType')
return schema

View file

@ -21,13 +21,13 @@ ZCML setup):
>>> from loops.type import ConceptType, TypeConcept
>>> from loops.interfaces import ITypeConcept
>>> from loops.base import Loops
>>> from loops.tests.setup import TestSite
>>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
>>> query = concepts['query']
>>> topic = concepts['topic'] = Concept(u'Topic')
>>> topic = concepts['topic']
In addition we create a concept that holds the search page and a node
(page) that links to this concept:
@ -66,13 +66,13 @@ zcml in real life:
>>> t = searchView.typesForSearch()
>>> len(t)
9
14
>>> t.getTermByToken('loops:resource:*').title
'Any Resource'
>>> t = searchView.conceptTypesForSearch()
>>> len(t)
4
9
>>> t.getTermByToken('loops:concept:*').title
'Any Concept'
@ -91,7 +91,7 @@ a controller attribute for the search view.
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
'submitReplacing("1.results", "1.search.form",
"http://127.0.0.1/loops/views/page/.target29/@@searchresults.html");...'
"http://127.0.0.1/loops/views/page/.target80/@@searchresults.html");...'
Basic (text/title) search
-------------------------
@ -177,7 +177,7 @@ of the concepts' titles:
>>> request = TestRequest(form=form)
>>> view = Search(page, request)
>>> view.listConcepts()
u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '34'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '37'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '39'}]}"
u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '85'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '87'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '89'}]}"
Preset Concept Types on Search Forms
------------------------------------
@ -193,8 +193,8 @@ Let's start with a new type, the customer type.
>>> custType.options
[]
>>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation')
>>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts')
>>> cust1 = concepts['cust1']
>>> cust2 = concepts['cust2']
>>> for c in (cust1, cust2):
... c.conceptType = customer
... catalog.index_doc(int(util.getUidForObject(c)), c)
@ -219,16 +219,17 @@ and thus include the customer type in the preset search types.
>>> searchView.conceptsForType('loops:concept:customer')
[{'token': 'none', 'title': u'not selected'},
{'token': '47', 'title': u'Zope Corporation'},
{'token': '49', 'title': u'cyberconcepts'}]
{'token': '58', 'title': u'Customer 1'},
{'token': '60', 'title': u'Customer 2'},
{'token': '62', 'title': u'Customer 3'}]
Let's use this new search option for querying:
>>> form = {'search.4.text_selected': u'47'}
>>> form = {'search.4.text_selected': u'58'}
>>> resultsView = SearchResults(page, TestRequest(form=form))
>>> results = list(resultsView.results)
>>> results[0].title
u'Zope Corporation'
u'Customer 1'
Automatic Filtering

View file

@ -36,7 +36,8 @@ from cybertools.typology.interfaces import ITypeManager
from loops.browser.common import BaseView
from loops.browser.node import NodeView
from loops.common import adapted
from loops.query import ConceptQuery, FullQuery
#from loops.query import ConceptQuery, FullQuery
from loops.expert.concept import ConceptQuery, FullQuery
from loops import util
from loops.util import _

View file

@ -35,7 +35,7 @@ from loops.common import adapted
from loops.concept import ConceptManager, Concept
from loops.interfaces import ILoops, ITypeConcept
from loops.interfaces import IFile, IImage, ITextDocument, INote
from loops.query import IQueryConcept
#from loops.query import IQueryConcept
from loops.record import RecordManager
from loops.resource import ResourceManager, Resource
from loops.view import ViewManager, Node
@ -82,16 +82,16 @@ class SetupManager(object):
predicate = self.addObject(conceptManager, Concept, 'predicate', title=u'Predicate')
standard = self.addObject(conceptManager, Concept, 'standard', title=u'subobject')
domain = self.addObject(conceptManager, Concept, 'domain', title=u'Domain')
query = self.addObject(conceptManager, Concept, 'query', title=u'Query')
#query = self.addObject(conceptManager, Concept, 'query', title=u'Query')
file = self.addObject(conceptManager, Concept, 'file', title=u'File')
textdocument = self.addObject(conceptManager, Concept,
'textdocument', title=u'Text')
note = self.addObject(conceptManager, Concept, 'note', title=u'Note')
for c in (typeConcept, domain, query, note, file, textdocument, predicate):
for c in (typeConcept, domain, note, file, textdocument, predicate):
c.conceptType = typeConcept
notify(ObjectModifiedEvent(c))
ITypeConcept(typeConcept).typeInterface = ITypeConcept
ITypeConcept(query).typeInterface = IQueryConcept
#ITypeConcept(query).typeInterface = IQueryConcept
ITypeConcept(file).typeInterface = IFile
ITypeConcept(textdocument).typeInterface = ITextDocument
ITypeConcept(note).typeInterface = INote

View file

@ -39,7 +39,7 @@ In addition to the application site we need a loops system management site.
>>> systemRoot = site['loops.system']
>>> sorted(sysConcepts)
[u'domain', u'file', u'hasType', u'job', u'note', u'predicate', u'query',
[u'domain', u'file', u'hasType', u'job', u'note', u'predicate',
u'standard', u'textdocument', u'type']

View file

@ -33,6 +33,7 @@ from cybertools.composer.schema.factory import SchemaFactory
from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
from cybertools.composer.schema.field import DateFieldInstance, BooleanFieldInstance
from cybertools.composer.schema.field import EmailFieldInstance, ListFieldInstance
from cybertools.composer.schema.field import FileUploadFieldInstance
from cybertools.composer.schema.instance import Instance, Editor
from cybertools.relation.tests import IntIdsStub
from cybertools.relation.registry import RelationRegistry, IIndexableRelation
@ -56,8 +57,7 @@ from loops.layout.base import LayoutNode
from loops.organize.memberinfo import MemberInfoProvider
from loops.organize.stateful.base import StatefulResourceIndexInfo, handleTransition
from loops.predicate import Predicate #, MappingAttributeRelation
from loops.query import QueryConcept
from loops.query import QueryConcept
from loops.expert.concept import QueryConcept
from loops.resource import Resource, FileAdapter, TextDocumentAdapter
from loops.resource import Document, MediaAsset
from loops.resource import IndexAttributes as ResourceIndexAttributes
@ -139,6 +139,7 @@ class TestSite(object):
component.provideAdapter(EmailFieldInstance, name='email')
component.provideAdapter(BooleanFieldInstance, name='boolean')
component.provideAdapter(ListFieldInstance, name='list')
component.provideAdapter(FileUploadFieldInstance, name='fileupload')
component.provideAdapter(SchemaFactory)
component.provideAdapter(ResourceSchemaFactory)
component.provideAdapter(FileSchemaFactory)

View file

@ -29,7 +29,7 @@ configuration):
>>> #sorted(concepts)
>>> #sorted(resources)
>>> len(concepts) + len(resources)
24
23
Version Information

View file

@ -34,7 +34,7 @@ ZCML setup):
Let's look what setup has provided us with:
>>> len(concepts)
19
18
Now let's add a few more concepts:
@ -58,7 +58,7 @@ There are a few standard objects we can retrieve directly:
>>> defaultPred = xrf.getDefaultPredicate()
>>> defaultPred['id'], defaultPred['name']
('16', u'standard')
('14', u'standard')
>>> typePred = xrf.getTypePredicate()
>>> typePred['id'], typePred['name']
('1', u'hasType')
@ -71,7 +71,7 @@ note that the 'hasType' predicate is not shown as it should not be
applied in an explicit assignment.
>>> sorted(t['name'] for t in xrf.getConceptTypes())
[u'customer', u'domain', u'file', u'note', u'person', u'predicate', u'query',
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
u'task', u'textdocument', u'topic', u'type']
>>> sorted(t['name'] for t in xrf.getPredicates())
[u'depends', u'knows', u'ownedby', u'provides', u'requires', u'standard']
@ -80,10 +80,10 @@ We can also retrieve a certain object by its id or its name:
>>> obj2 = xrf.getObjectById('5')
>>> obj2['id'], obj2['name']
('5', u'query')
('5', u'note')
>>> textdoc = xrf.getObjectByName(u'textdocument')
>>> textdoc['id'], textdoc['name']
('11', u'textdocument')
('9', u'textdocument')
All methods that retrieve one object also returns its children and parents:
@ -94,7 +94,7 @@ All methods that retrieve one object also returns its children and parents:
u'hasType'
>>> sorted(c['name'] for c in ch[0]['objects'])
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
u'query', u'task', u'textdocument', u'topic', u'type']
u'task', u'textdocument', u'topic', u'type']
>>> pa = defaultPred['parents']
>>> len(pa)
@ -113,7 +113,7 @@ We can also retrieve children and parents explicitely:
u'hasType'
>>> sorted(c['name'] for c in ch[0]['objects'])
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
u'query', u'task', u'textdocument', u'topic', u'type']
u'task', u'textdocument', u'topic', u'type']
>>> pa = xrf.getParents('7')
>>> len(pa)
@ -172,14 +172,14 @@ Updating the concept map
>>> topicId = xrf.getObjectByName('topic')['id']
>>> xrf.createConcept(topicId, u'zope2', u'Zope 2')
{'description': u'', 'title': u'Zope 2', 'type': '24', 'id': '56',
{'description': u'', 'title': u'Zope 2', 'type': '22', 'id': '54',
'name': u'zope2'}
The name of the concept is checked by a name chooser; if the corresponding
parameter is empty, the name will be generated from the title.
>>> xrf.createConcept(topicId, u'', u'Python')
{'description': u'', 'title': u'Python', 'type': '24', 'id': '58',
{'description': u'', 'title': u'Python', 'type': '22', 'id': '56',
'name': u'python'}
If we try to deassign a ``hasType`` relation nothing will happen; a