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:
parent
c2a16063c5
commit
d1dbe47d57
33 changed files with 471 additions and 87 deletions
|
@ -37,7 +37,7 @@ top-level loops container and a concept manager:
|
||||||
>>> #sorted(concepts)
|
>>> #sorted(concepts)
|
||||||
>>> #sorted(resources)
|
>>> #sorted(resources)
|
||||||
>>> len(concepts) + len(resources)
|
>>> len(concepts) + len(resources)
|
||||||
14
|
13
|
||||||
|
|
||||||
>>> loopsRoot = site['loops']
|
>>> loopsRoot = site['loops']
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ type manager.
|
||||||
>>> from loops.concept import ConceptTypeSourceList
|
>>> from loops.concept import ConceptTypeSourceList
|
||||||
>>> types = ConceptTypeSourceList(cc1)
|
>>> types = ConceptTypeSourceList(cc1)
|
||||||
>>> sorted(t.title for t in types)
|
>>> 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
|
Using a PredicateSourceList we can retrieve a list of the available
|
||||||
predicates.
|
predicates.
|
||||||
|
@ -197,7 +197,6 @@ types and predicates.
|
||||||
[(u'Customer', '.loops/concepts/customer'),
|
[(u'Customer', '.loops/concepts/customer'),
|
||||||
(u'Domain', '.loops/concepts/domain'),
|
(u'Domain', '.loops/concepts/domain'),
|
||||||
(u'Predicate', '.loops/concepts/predicate'),
|
(u'Predicate', '.loops/concepts/predicate'),
|
||||||
(u'Query', '.loops/concepts/query'),
|
|
||||||
(u'Topic', '.loops/concepts/topic'),
|
(u'Topic', '.loops/concepts/topic'),
|
||||||
(u'Type', '.loops/concepts/type'),
|
(u'Type', '.loops/concepts/type'),
|
||||||
(u'Unknown Type', '.loops/concepts/unknown')]
|
(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))
|
>>> view = ConfigureView(m111, TestRequest(form = form))
|
||||||
>>> tt = view.targetTypes()
|
>>> tt = view.targetTypes()
|
||||||
>>> len(tt)
|
>>> len(tt)
|
||||||
10
|
9
|
||||||
>>> sorted((t.token, t.title) for t in view.targetTypes())[1]
|
>>> sorted((t.token, t.title) for t in view.targetTypes())[1]
|
||||||
('.loops/concepts/domain', u'Domain')
|
('.loops/concepts/domain', u'Domain')
|
||||||
>>> view.update()
|
>>> view.update()
|
||||||
|
@ -580,7 +579,7 @@ is ``NodeView.getUrlForTarget()`` that expects a ConceptView, ResourceView,
|
||||||
or ConceptRelationView as its argument.
|
or ConceptRelationView as its argument.
|
||||||
|
|
||||||
>>> view.getUrlForTarget(childRels[0])
|
>>> 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
|
Actions
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -121,9 +121,10 @@
|
||||||
tal:define="resources python: list(item.resources())"
|
tal:define="resources python: list(item.resources())"
|
||||||
tal:condition="resources">
|
tal:condition="resources">
|
||||||
<h2 i18n:translate="">Resources</h2>
|
<h2 i18n:translate="">Resources</h2>
|
||||||
<table class="listing">
|
<table class="listing"
|
||||||
|
metal:define-macro="resources">
|
||||||
<tr>
|
<tr>
|
||||||
<th tal:condition="view/showCheckboxes|nothing"> </th>
|
<th tal:condition="item/showCheckboxes|nothing"> </th>
|
||||||
<th i18n:translate="">Title</th>
|
<th i18n:translate="">Title</th>
|
||||||
<th i18n:translate="">Type</th>
|
<th i18n:translate="">Type</th>
|
||||||
<th i18n:translate=""
|
<th i18n:translate=""
|
||||||
|
@ -138,8 +139,11 @@
|
||||||
<tal:item define="class python: repeat['related'].odd() and 'even' or 'odd';
|
<tal:item define="class python: repeat['related'].odd() and 'even' or 'odd';
|
||||||
description related/description">
|
description related/description">
|
||||||
<tr tal:attributes="class class">
|
<tr tal:attributes="class class">
|
||||||
<td tal:condition="view/showCheckboxes|nothing"
|
<td tal:condition="item/showCheckboxes|nothing"
|
||||||
class="checkbox"><input type="checkbox" /></td>
|
tal:define="uid related/uniqueId"
|
||||||
|
class="checkbox">
|
||||||
|
<input type="checkbox" name="selection:list" checked
|
||||||
|
tal:attributes="value uid;" /></td>
|
||||||
<td valign="top">
|
<td valign="top">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
tal:attributes="href string:${view/url}/.target${related/uniqueId};
|
tal:attributes="href string:${view/url}/.target${related/uniqueId};
|
||||||
|
|
|
@ -27,7 +27,7 @@ configuration):
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> len(concepts), len(resources)
|
>>> len(concepts), len(resources)
|
||||||
(21, 0)
|
(20, 0)
|
||||||
|
|
||||||
Let's now add an external collection that reads in a set of resources
|
Let's now add an external collection that reads in a set of resources
|
||||||
from external files so we have something to work with.
|
from external files so we have something to work with.
|
||||||
|
|
|
@ -34,7 +34,8 @@ from cybertools.meta.interfaces import IOptions
|
||||||
from cybertools.meta.namespace import Executor, ExecutionError
|
from cybertools.meta.namespace import Executor, ExecutionError
|
||||||
from cybertools.typology.interfaces import IType
|
from cybertools.typology.interfaces import IType
|
||||||
from loops.interfaces import ILoops, ILoopsObject
|
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
|
from loops import util
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,8 @@
|
||||||
<allow interface="cybertools.meta.interfaces.IOptions" />
|
<allow interface="cybertools.meta.interfaces.IOptions" />
|
||||||
</zope:class>
|
</zope:class>
|
||||||
|
|
||||||
|
<!-- backward compatibility -->
|
||||||
|
<zope:adapter factory="loops.config.base.QueryOptions" trusted="True"
|
||||||
|
for="loops.query.IQueryConcept" />
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
|
|
@ -436,6 +436,7 @@
|
||||||
<include package=".compound.blog" />
|
<include package=".compound.blog" />
|
||||||
<include package=".config" />
|
<include package=".config" />
|
||||||
<include package=".constraint" />
|
<include package=".constraint" />
|
||||||
|
<include package=".expert" />
|
||||||
<include package=".external" />
|
<include package=".external" />
|
||||||
<include package=".i18n" />
|
<include package=".i18n" />
|
||||||
<include package=".integrator" />
|
<include package=".integrator" />
|
||||||
|
|
|
@ -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'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'predicate', u'Predicate', options=u'', typeInterface=u'', viewName=u'')
|
||||||
type(u'process', u'Prozess', 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'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'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'')
|
type(u'type', u'Type', options=u'', typeInterface='loops.interfaces.ITypeConcept', viewName=u'')
|
||||||
|
|
|
@ -27,7 +27,7 @@ configuration):
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> len(concepts) + len(resources)
|
>>> len(concepts) + len(resources)
|
||||||
32
|
33
|
||||||
|
|
||||||
>>> loopsRoot = site['loops']
|
>>> loopsRoot = site['loops']
|
||||||
|
|
||||||
|
@ -47,19 +47,19 @@ Type- and text-based queries
|
||||||
>>> from loops.expert import query
|
>>> from loops.expert import query
|
||||||
>>> qu = query.Title('ty*')
|
>>> qu = query.Title('ty*')
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[0, 1, 45]
|
[0, 1, 47]
|
||||||
|
|
||||||
>>> qu = query.Type('loops:*')
|
>>> qu = query.Type('loops:*')
|
||||||
>>> len(list(qu.apply()))
|
>>> len(list(qu.apply()))
|
||||||
32
|
33
|
||||||
|
|
||||||
>>> qu = query.Type('loops:concept:predicate')
|
>>> qu = query.Type('loops:concept:predicate')
|
||||||
>>> len(list(qu.apply()))
|
>>> len(list(qu.apply()))
|
||||||
6
|
7
|
||||||
|
|
||||||
>>> qu = query.Type('loops:concept:predicate') & query.Title('t*')
|
>>> qu = query.Type('loops:concept:predicate') & query.Title('t*')
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[1]
|
[1, 43]
|
||||||
|
|
||||||
State-based queries
|
State-based queries
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -99,7 +99,7 @@ they have at least one concept assigned).
|
||||||
|
|
||||||
>>> qu = query.State('classification_quality', 'classified')
|
>>> qu = query.State('classification_quality', 'classified')
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[23, 25, 27]
|
[21, 23, 25]
|
||||||
|
|
||||||
Using the stateful adapter for a resource we now manually execute the
|
Using the stateful adapter for a resource we now manually execute the
|
||||||
``verify`` transition.
|
``verify`` transition.
|
||||||
|
@ -113,16 +113,16 @@ Now only two resources are still in the ``qualified`` state, the changed
|
||||||
one being in the ``verified`` state.
|
one being in the ``verified`` state.
|
||||||
|
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[25, 27]
|
[23, 25]
|
||||||
>>> qu = query.State('classification_quality', 'verified')
|
>>> qu = query.State('classification_quality', 'verified')
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[23]
|
[21]
|
||||||
|
|
||||||
We may also provide a sequence of states for querying.
|
We may also provide a sequence of states for querying.
|
||||||
|
|
||||||
>>> qu = query.State('classification_quality', ('classified', 'verified',))
|
>>> qu = query.State('classification_quality', ('classified', 'verified',))
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[23, 25, 27]
|
[21, 23, 25]
|
||||||
|
|
||||||
Relationship-based queries
|
Relationship-based queries
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -135,7 +135,7 @@ syntax (that in turn is based on hurry.query).
|
||||||
>>> cust1 = concepts['cust1']
|
>>> cust1 = concepts['cust1']
|
||||||
>>> qu = query.Resources(cust1)
|
>>> qu = query.Resources(cust1)
|
||||||
>>> list(qu.apply())
|
>>> list(qu.apply())
|
||||||
[23, 27]
|
[21, 25]
|
||||||
|
|
||||||
Getting objects
|
Getting objects
|
||||||
---------------
|
---------------
|
||||||
|
@ -208,6 +208,15 @@ A query instance consists of
|
||||||
>>> #qi.apply()
|
>>> #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
|
Fin de partie
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,12 @@ $Id$
|
||||||
from zope import interface, component
|
from zope import interface, component
|
||||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||||
from zope.cachedescriptors.property import Lazy
|
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.common import adapted
|
||||||
from loops import util
|
from loops import util
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
@ -36,11 +40,65 @@ from loops.util import _
|
||||||
queryTemplate = ViewPageTemplateFile('query.pt')
|
queryTemplate = ViewPageTemplateFile('query.pt')
|
||||||
|
|
||||||
|
|
||||||
|
#class BaseQueryView(ConceptView):
|
||||||
class BaseQueryView(BaseView):
|
class BaseQueryView(BaseView):
|
||||||
|
|
||||||
template = queryTemplate
|
template = queryTemplate
|
||||||
|
childViewFactory = ResourceRelationView
|
||||||
|
showCheckboxes = True
|
||||||
|
form_action = 'execute_query_action'
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def macro(self):
|
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
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
i18n_domain="loops">
|
i18n_domain="loops">
|
||||||
|
|
||||||
<zope:adapter
|
<zope:adapter
|
||||||
name="query"
|
name="query.html"
|
||||||
for="loops.interfaces.IConcept
|
for="loops.interfaces.IConcept
|
||||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||||
provides="zope.interface.Interface"
|
provides="zope.interface.Interface"
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
<metal:query define-macro="query">
|
<metal:query define-macro="query">
|
||||||
<div id="query">
|
<div id="query"
|
||||||
<h1 tal:attributes="class string:content-$level;
|
tal:define="resources item/results"
|
||||||
ondblclick item/openEditWindow"
|
tal:attributes="class string:content-$level;">
|
||||||
tal:content="item/title">
|
<form method="post">
|
||||||
Query
|
<input type="hidden" name="form.action"
|
||||||
</h1>
|
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>
|
</div>
|
||||||
</metal:query>
|
</metal:query>
|
||||||
|
|
||||||
|
|
211
expert/concept.py
Normal file
211
expert/concept.py
Normal 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
27
expert/configure.zcml
Normal 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
42
expert/setup.py
Normal 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)
|
|
@ -10,6 +10,7 @@ from zope.app.catalog.interfaces import ICatalog
|
||||||
from cybertools.typology.interfaces import IType
|
from cybertools.typology.interfaces import IType
|
||||||
from loops import util
|
from loops import util
|
||||||
from loops.concept import Concept
|
from loops.concept import Concept
|
||||||
|
from loops.expert.setup import SetupManager as ExpertSetupManager
|
||||||
from loops.resource import Resource
|
from loops.resource import Resource
|
||||||
from loops.knowledge.interfaces import IPerson
|
from loops.knowledge.interfaces import IPerson
|
||||||
from loops.knowledge.knowledge import Person
|
from loops.knowledge.knowledge import Person
|
||||||
|
@ -32,6 +33,7 @@ class TestSite(BaseTestSite):
|
||||||
component.provideAdapter(Person, provides=IPerson)
|
component.provideAdapter(Person, provides=IPerson)
|
||||||
|
|
||||||
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
|
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
|
||||||
|
component.provideAdapter(ExpertSetupManager, name='expert')
|
||||||
setup = SetupManager(loopsRoot)
|
setup = SetupManager(loopsRoot)
|
||||||
concepts, resources, views = setup.setup()
|
concepts, resources, views = setup.setup()
|
||||||
|
|
||||||
|
|
16
external/README.txt
vendored
16
external/README.txt
vendored
|
@ -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
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
>>> site = placefulSetUp(True)
|
>>> site = placefulSetUp(True)
|
||||||
|
|
||||||
>>> from loops.tests.setup import TestSite
|
>>> from loops.expert.testsetup import TestSite
|
||||||
>>> t = TestSite(site)
|
>>> t = TestSite(site)
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
>>> loopsRoot = site['loops']
|
>>> loopsRoot = site['loops']
|
||||||
>>> len(concepts), len(resources), len(views)
|
>>> len(concepts), len(resources), len(views)
|
||||||
(11, 3, 0)
|
(30, 3, 0)
|
||||||
|
|
||||||
|
|
||||||
Importing loops Objects
|
Importing loops Objects
|
||||||
|
@ -44,7 +44,7 @@ Creating the corresponding objects
|
||||||
>>> loader = Loader(loopsRoot)
|
>>> loader = Loader(loopsRoot)
|
||||||
>>> loader.load(elements)
|
>>> loader.load(elements)
|
||||||
>>> len(concepts), len(resources), len(views)
|
>>> len(concepts), len(resources), len(views)
|
||||||
(12, 3, 0)
|
(31, 3, 0)
|
||||||
|
|
||||||
>>> from loops.common import adapted
|
>>> from loops.common import adapted
|
||||||
>>> adMyquery = adapted(concepts['myquery'])
|
>>> adMyquery = adapted(concepts['myquery'])
|
||||||
|
@ -118,7 +118,7 @@ Extracting elements
|
||||||
>>> extractor = Extractor(loopsRoot, os.path.join(dataDirectory, 'export'))
|
>>> extractor = Extractor(loopsRoot, os.path.join(dataDirectory, 'export'))
|
||||||
>>> elements = list(extractor.extract())
|
>>> elements = list(extractor.extract())
|
||||||
>>> len(elements)
|
>>> len(elements)
|
||||||
20
|
52
|
||||||
|
|
||||||
Writing object information to the external storage
|
Writing object information to the external storage
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
@ -130,13 +130,13 @@ Writing object information to the external storage
|
||||||
>>> writer = PyWriter()
|
>>> writer = PyWriter()
|
||||||
>>> writer.write(elements, output)
|
>>> writer.write(elements, output)
|
||||||
>>> print output.getvalue()
|
>>> 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'')...
|
||||||
type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept',
|
type(u'query', u'Query', options=u'', typeInterface='loops.expert.concept.IQueryConcept',
|
||||||
viewName=u'')...
|
viewName=u'')...
|
||||||
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
|
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
|
||||||
viewName=u'mystuff.html')...
|
viewName=u'mystuff.html')...
|
||||||
child(u'projects', u'customer', u'standard')...
|
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')
|
resourceRelation(u'myquery', u'doc04.txt', u'standard')
|
||||||
node('home', u'Home', '', u'menu', body=u'Welcome')
|
node('home', u'Home', '', u'menu', body=u'Welcome')
|
||||||
node('myquery', u'My Query', 'home', u'page', target=u'concepts/myquery')...
|
node('myquery', u'My Query', 'home', u'page', target=u'concepts/myquery')...
|
||||||
|
@ -172,7 +172,7 @@ corresponding extractor adapter.
|
||||||
>>> PyWriter().write(extractor.extract(), output)
|
>>> PyWriter().write(extractor.extract(), output)
|
||||||
|
|
||||||
>>> print output.getvalue()
|
>>> 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',
|
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
|
||||||
viewName=u'mystuff.html')[
|
viewName=u'mystuff.html')[
|
||||||
annotations(creators=(u'john',))]...
|
annotations(creators=(u'john',))]...
|
||||||
|
|
11
helpers.txt
11
helpers.txt
|
@ -226,7 +226,7 @@ get a type manager from all loops objects, always with the same context:
|
||||||
>>> types = typeManager.types
|
>>> types = typeManager.types
|
||||||
>>> typeTokens = sorted(t.token for t in types)
|
>>> typeTokens = sorted(t.token for t in types)
|
||||||
>>> len(typeTokens)
|
>>> len(typeTokens)
|
||||||
9
|
8
|
||||||
|
|
||||||
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
|
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
|
||||||
True
|
True
|
||||||
|
@ -237,7 +237,7 @@ condition:
|
||||||
>>> types = typeManager.listTypes(include=('concept',))
|
>>> types = typeManager.listTypes(include=('concept',))
|
||||||
>>> typeTokens = sorted(t.token for t in types)
|
>>> typeTokens = sorted(t.token for t in types)
|
||||||
>>> len(typeTokens)
|
>>> len(typeTokens)
|
||||||
6
|
5
|
||||||
>>> types = typeManager.listTypes(exclude=('concept',))
|
>>> types = typeManager.listTypes(exclude=('concept',))
|
||||||
>>> typeTokens = sorted(t.token for t in types)
|
>>> typeTokens = sorted(t.token for t in types)
|
||||||
>>> len(typeTokens)
|
>>> 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
|
with the IQueryConcept interface. The query type concept itself has already
|
||||||
been provided by the setup, but we have to register a corresponding adapter.
|
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)
|
>>> 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:
|
Next we need a concept of this type:
|
||||||
|
|
||||||
|
@ -423,7 +425,6 @@ Folders
|
||||||
structures of their documents in the filesystem to the loops concept
|
structures of their documents in the filesystem to the loops concept
|
||||||
map.
|
map.
|
||||||
|
|
||||||
>>> from loops.setup import addAndConfigureObject
|
|
||||||
>>> tFolder = addAndConfigureObject(concepts, Concept, 'folder',
|
>>> tFolder = addAndConfigureObject(concepts, Concept, 'folder',
|
||||||
... title=u'Folder', conceptType=typeObject)
|
... title=u'Folder', conceptType=typeObject)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ configuration):
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> len(concepts) + len(resources)
|
>>> len(concepts) + len(resources)
|
||||||
18
|
17
|
||||||
|
|
||||||
|
|
||||||
External Collections
|
External Collections
|
||||||
|
|
|
@ -27,7 +27,7 @@ configuration):
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> len(concepts) + len(resources)
|
>>> len(concepts) + len(resources)
|
||||||
14
|
13
|
||||||
|
|
||||||
|
|
||||||
Accessing a Directory in the Filesystem
|
Accessing a Directory in the Filesystem
|
||||||
|
|
|
@ -745,6 +745,14 @@ class IExternalFile(IFile):
|
||||||
missing_value='',
|
missing_value='',
|
||||||
required=False)
|
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):
|
class IImage(IResourceAdapter):
|
||||||
""" A media asset that may be embedded in a (web) page as an image.
|
""" A media asset that may be embedded in a (web) page as an image.
|
||||||
|
|
|
@ -18,7 +18,7 @@ ZCML setup):
|
||||||
|
|
||||||
>>> from loops.organize.setup import SetupManager
|
>>> from loops.organize.setup import SetupManager
|
||||||
>>> component.provideAdapter(SetupManager, name='organize')
|
>>> component.provideAdapter(SetupManager, name='organize')
|
||||||
>>> from loops.tests.setup import TestSite
|
>>> from loops.expert.testsetup import TestSite
|
||||||
>>> t = TestSite(site)
|
>>> t = TestSite(site)
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ Automatic security settings on persons
|
||||||
|
|
||||||
>>> from zope.traversing.api import getName
|
>>> from zope.traversing.api import getName
|
||||||
>>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
|
>>> 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
|
Person objects that have a user assigned to them receive this user
|
||||||
(principal) as their owner.
|
(principal) as their owner.
|
||||||
|
|
|
@ -72,10 +72,10 @@ So we are now ready to query the favorites.
|
||||||
|
|
||||||
>>> favs = list(favorites.query(userName=johnCId))
|
>>> favs = list(favorites.query(userName=johnCId))
|
||||||
>>> favs
|
>>> favs
|
||||||
[<Favorite ['29', 1, '35', '...']: {}>]
|
[<Favorite ['27', 1, '33', '...']: {}>]
|
||||||
|
|
||||||
>>> list(favAdapted.list(johnC))
|
>>> list(favAdapted.list(johnC))
|
||||||
['29']
|
['27']
|
||||||
|
|
||||||
>>> util.getObjectForUid(favs[0].taskId) is resources['d001.txt']
|
>>> util.getObjectForUid(favs[0].taskId) is resources['d001.txt']
|
||||||
True
|
True
|
||||||
|
|
|
@ -14,7 +14,7 @@ First we set up a loops site with basic and example concepts and resources.
|
||||||
|
|
||||||
>>> from loops.organize.setup import SetupManager
|
>>> from loops.organize.setup import SetupManager
|
||||||
>>> component.provideAdapter(SetupManager, name='organize')
|
>>> component.provideAdapter(SetupManager, name='organize')
|
||||||
>>> from loops.tests.setup import TestSite
|
>>> from loops.expert.testsetup import TestSite
|
||||||
>>> t = TestSite(site)
|
>>> t = TestSite(site)
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
>>> loopsRoot = site['loops']
|
>>> 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
|
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.
|
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,
|
>>> statefulDoc01 = component.getAdapter(doc01, IStateful,
|
||||||
... name='simple_publishing')
|
... name='simple_publishing')
|
||||||
|
|
||||||
|
@ -88,7 +92,6 @@ later.
|
||||||
|
|
||||||
>>> tCustomer = concepts['customer']
|
>>> tCustomer = concepts['customer']
|
||||||
>>> from loops.concept import Concept
|
>>> from loops.concept import Concept
|
||||||
>>> from loops.setup import addAndConfigureObject
|
|
||||||
>>> c01 = addAndConfigureObject(concepts, Concept, 'c01', conceptType=tCustomer,
|
>>> c01 = addAndConfigureObject(concepts, Concept, 'c01', conceptType=tCustomer,
|
||||||
... title='im publishing')
|
... title='im publishing')
|
||||||
>>> c02 = addAndConfigureObject(concepts, Concept, 'c02', conceptType=tCustomer,
|
>>> c02 = addAndConfigureObject(concepts, Concept, 'c02', conceptType=tCustomer,
|
||||||
|
|
|
@ -357,7 +357,6 @@ class ExternalFileAdapter(FileAdapter):
|
||||||
def getExternalAddress(self):
|
def getExternalAddress(self):
|
||||||
return getattr(self.context, '_externalAddress', self.context.__name__)
|
return getattr(self.context, '_externalAddress', self.context.__name__)
|
||||||
def setExternalAddress(self, addr):
|
def setExternalAddress(self, addr):
|
||||||
# TODO (?) - use intId as default?
|
|
||||||
self.context._externalAddress = addr
|
self.context._externalAddress = addr
|
||||||
externalAddress = property(getExternalAddress, setExternalAddress)
|
externalAddress = property(getExternalAddress, setExternalAddress)
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ ZCML setup):
|
||||||
Let's look what setup has provided us with:
|
Let's look what setup has provided us with:
|
||||||
|
|
||||||
>>> sorted(concepts)
|
>>> 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']
|
u'standard', u'textdocument', u'type']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,9 @@ class FileSchemaFactory(SchemaFactory):
|
||||||
|
|
||||||
def __call__(self, interface, **kw):
|
def __call__(self, interface, **kw):
|
||||||
schema = super(FileSchemaFactory, self).__call__(interface, **kw)
|
schema = super(FileSchemaFactory, self).__call__(interface, **kw)
|
||||||
if 'request' in kw and kw['request'].principal.id != 'rootadmin':
|
if 'request' in kw:
|
||||||
|
principal = kw['request'].principal
|
||||||
|
if not principal or principal.id != 'rootadmin':
|
||||||
schema.fields.remove('contentType')
|
schema.fields.remove('contentType')
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,13 @@ ZCML setup):
|
||||||
>>> from loops.type import ConceptType, TypeConcept
|
>>> from loops.type import ConceptType, TypeConcept
|
||||||
>>> from loops.interfaces import ITypeConcept
|
>>> from loops.interfaces import ITypeConcept
|
||||||
>>> from loops.base import Loops
|
>>> from loops.base import Loops
|
||||||
>>> from loops.tests.setup import TestSite
|
>>> from loops.expert.testsetup import TestSite
|
||||||
>>> t = TestSite(site)
|
>>> t = TestSite(site)
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> loopsRoot = site['loops']
|
>>> loopsRoot = site['loops']
|
||||||
>>> query = concepts['query']
|
>>> 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
|
In addition we create a concept that holds the search page and a node
|
||||||
(page) that links to this concept:
|
(page) that links to this concept:
|
||||||
|
@ -66,13 +66,13 @@ zcml in real life:
|
||||||
|
|
||||||
>>> t = searchView.typesForSearch()
|
>>> t = searchView.typesForSearch()
|
||||||
>>> len(t)
|
>>> len(t)
|
||||||
9
|
14
|
||||||
>>> t.getTermByToken('loops:resource:*').title
|
>>> t.getTermByToken('loops:resource:*').title
|
||||||
'Any Resource'
|
'Any Resource'
|
||||||
|
|
||||||
>>> t = searchView.conceptTypesForSearch()
|
>>> t = searchView.conceptTypesForSearch()
|
||||||
>>> len(t)
|
>>> len(t)
|
||||||
4
|
9
|
||||||
>>> t.getTermByToken('loops:concept:*').title
|
>>> t.getTermByToken('loops:concept:*').title
|
||||||
'Any Concept'
|
'Any Concept'
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ a controller attribute for the search view.
|
||||||
|
|
||||||
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
|
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
|
||||||
'submitReplacing("1.results", "1.search.form",
|
'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
|
Basic (text/title) search
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -177,7 +177,7 @@ of the concepts' titles:
|
||||||
>>> request = TestRequest(form=form)
|
>>> request = TestRequest(form=form)
|
||||||
>>> view = Search(page, request)
|
>>> view = Search(page, request)
|
||||||
>>> view.listConcepts()
|
>>> 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
|
Preset Concept Types on Search Forms
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
@ -193,8 +193,8 @@ Let's start with a new type, the customer type.
|
||||||
>>> custType.options
|
>>> custType.options
|
||||||
[]
|
[]
|
||||||
|
|
||||||
>>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation')
|
>>> cust1 = concepts['cust1']
|
||||||
>>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts')
|
>>> cust2 = concepts['cust2']
|
||||||
>>> for c in (cust1, cust2):
|
>>> for c in (cust1, cust2):
|
||||||
... c.conceptType = customer
|
... c.conceptType = customer
|
||||||
... catalog.index_doc(int(util.getUidForObject(c)), c)
|
... 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')
|
>>> searchView.conceptsForType('loops:concept:customer')
|
||||||
[{'token': 'none', 'title': u'not selected'},
|
[{'token': 'none', 'title': u'not selected'},
|
||||||
{'token': '47', 'title': u'Zope Corporation'},
|
{'token': '58', 'title': u'Customer 1'},
|
||||||
{'token': '49', 'title': u'cyberconcepts'}]
|
{'token': '60', 'title': u'Customer 2'},
|
||||||
|
{'token': '62', 'title': u'Customer 3'}]
|
||||||
|
|
||||||
Let's use this new search option for querying:
|
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))
|
>>> resultsView = SearchResults(page, TestRequest(form=form))
|
||||||
>>> results = list(resultsView.results)
|
>>> results = list(resultsView.results)
|
||||||
>>> results[0].title
|
>>> results[0].title
|
||||||
u'Zope Corporation'
|
u'Customer 1'
|
||||||
|
|
||||||
|
|
||||||
Automatic Filtering
|
Automatic Filtering
|
||||||
|
|
|
@ -36,7 +36,8 @@ from cybertools.typology.interfaces import ITypeManager
|
||||||
from loops.browser.common import BaseView
|
from loops.browser.common import BaseView
|
||||||
from loops.browser.node import NodeView
|
from loops.browser.node import NodeView
|
||||||
from loops.common import adapted
|
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 import util
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
|
||||||
|
|
8
setup.py
8
setup.py
|
@ -35,7 +35,7 @@ from loops.common import adapted
|
||||||
from loops.concept import ConceptManager, Concept
|
from loops.concept import ConceptManager, Concept
|
||||||
from loops.interfaces import ILoops, ITypeConcept
|
from loops.interfaces import ILoops, ITypeConcept
|
||||||
from loops.interfaces import IFile, IImage, ITextDocument, INote
|
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.record import RecordManager
|
||||||
from loops.resource import ResourceManager, Resource
|
from loops.resource import ResourceManager, Resource
|
||||||
from loops.view import ViewManager, Node
|
from loops.view import ViewManager, Node
|
||||||
|
@ -82,16 +82,16 @@ class SetupManager(object):
|
||||||
predicate = self.addObject(conceptManager, Concept, 'predicate', title=u'Predicate')
|
predicate = self.addObject(conceptManager, Concept, 'predicate', title=u'Predicate')
|
||||||
standard = self.addObject(conceptManager, Concept, 'standard', title=u'subobject')
|
standard = self.addObject(conceptManager, Concept, 'standard', title=u'subobject')
|
||||||
domain = self.addObject(conceptManager, Concept, 'domain', title=u'Domain')
|
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')
|
file = self.addObject(conceptManager, Concept, 'file', title=u'File')
|
||||||
textdocument = self.addObject(conceptManager, Concept,
|
textdocument = self.addObject(conceptManager, Concept,
|
||||||
'textdocument', title=u'Text')
|
'textdocument', title=u'Text')
|
||||||
note = self.addObject(conceptManager, Concept, 'note', title=u'Note')
|
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
|
c.conceptType = typeConcept
|
||||||
notify(ObjectModifiedEvent(c))
|
notify(ObjectModifiedEvent(c))
|
||||||
ITypeConcept(typeConcept).typeInterface = ITypeConcept
|
ITypeConcept(typeConcept).typeInterface = ITypeConcept
|
||||||
ITypeConcept(query).typeInterface = IQueryConcept
|
#ITypeConcept(query).typeInterface = IQueryConcept
|
||||||
ITypeConcept(file).typeInterface = IFile
|
ITypeConcept(file).typeInterface = IFile
|
||||||
ITypeConcept(textdocument).typeInterface = ITextDocument
|
ITypeConcept(textdocument).typeInterface = ITextDocument
|
||||||
ITypeConcept(note).typeInterface = INote
|
ITypeConcept(note).typeInterface = INote
|
||||||
|
|
|
@ -39,7 +39,7 @@ In addition to the application site we need a loops system management site.
|
||||||
>>> systemRoot = site['loops.system']
|
>>> systemRoot = site['loops.system']
|
||||||
|
|
||||||
>>> sorted(sysConcepts)
|
>>> 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']
|
u'standard', u'textdocument', u'type']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 FieldInstance, NumberFieldInstance
|
||||||
from cybertools.composer.schema.field import DateFieldInstance, BooleanFieldInstance
|
from cybertools.composer.schema.field import DateFieldInstance, BooleanFieldInstance
|
||||||
from cybertools.composer.schema.field import EmailFieldInstance, ListFieldInstance
|
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.composer.schema.instance import Instance, Editor
|
||||||
from cybertools.relation.tests import IntIdsStub
|
from cybertools.relation.tests import IntIdsStub
|
||||||
from cybertools.relation.registry import RelationRegistry, IIndexableRelation
|
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.memberinfo import MemberInfoProvider
|
||||||
from loops.organize.stateful.base import StatefulResourceIndexInfo, handleTransition
|
from loops.organize.stateful.base import StatefulResourceIndexInfo, handleTransition
|
||||||
from loops.predicate import Predicate #, MappingAttributeRelation
|
from loops.predicate import Predicate #, MappingAttributeRelation
|
||||||
from loops.query import QueryConcept
|
from loops.expert.concept import QueryConcept
|
||||||
from loops.query import QueryConcept
|
|
||||||
from loops.resource import Resource, FileAdapter, TextDocumentAdapter
|
from loops.resource import Resource, FileAdapter, TextDocumentAdapter
|
||||||
from loops.resource import Document, MediaAsset
|
from loops.resource import Document, MediaAsset
|
||||||
from loops.resource import IndexAttributes as ResourceIndexAttributes
|
from loops.resource import IndexAttributes as ResourceIndexAttributes
|
||||||
|
@ -139,6 +139,7 @@ class TestSite(object):
|
||||||
component.provideAdapter(EmailFieldInstance, name='email')
|
component.provideAdapter(EmailFieldInstance, name='email')
|
||||||
component.provideAdapter(BooleanFieldInstance, name='boolean')
|
component.provideAdapter(BooleanFieldInstance, name='boolean')
|
||||||
component.provideAdapter(ListFieldInstance, name='list')
|
component.provideAdapter(ListFieldInstance, name='list')
|
||||||
|
component.provideAdapter(FileUploadFieldInstance, name='fileupload')
|
||||||
component.provideAdapter(SchemaFactory)
|
component.provideAdapter(SchemaFactory)
|
||||||
component.provideAdapter(ResourceSchemaFactory)
|
component.provideAdapter(ResourceSchemaFactory)
|
||||||
component.provideAdapter(FileSchemaFactory)
|
component.provideAdapter(FileSchemaFactory)
|
||||||
|
|
|
@ -29,7 +29,7 @@ configuration):
|
||||||
>>> #sorted(concepts)
|
>>> #sorted(concepts)
|
||||||
>>> #sorted(resources)
|
>>> #sorted(resources)
|
||||||
>>> len(concepts) + len(resources)
|
>>> len(concepts) + len(resources)
|
||||||
24
|
23
|
||||||
|
|
||||||
|
|
||||||
Version Information
|
Version Information
|
||||||
|
|
|
@ -34,7 +34,7 @@ ZCML setup):
|
||||||
Let's look what setup has provided us with:
|
Let's look what setup has provided us with:
|
||||||
|
|
||||||
>>> len(concepts)
|
>>> len(concepts)
|
||||||
19
|
18
|
||||||
|
|
||||||
Now let's add a few more concepts:
|
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 = xrf.getDefaultPredicate()
|
||||||
>>> defaultPred['id'], defaultPred['name']
|
>>> defaultPred['id'], defaultPred['name']
|
||||||
('16', u'standard')
|
('14', u'standard')
|
||||||
>>> typePred = xrf.getTypePredicate()
|
>>> typePred = xrf.getTypePredicate()
|
||||||
>>> typePred['id'], typePred['name']
|
>>> typePred['id'], typePred['name']
|
||||||
('1', u'hasType')
|
('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.
|
applied in an explicit assignment.
|
||||||
|
|
||||||
>>> sorted(t['name'] for t in xrf.getConceptTypes())
|
>>> 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']
|
u'task', u'textdocument', u'topic', u'type']
|
||||||
>>> sorted(t['name'] for t in xrf.getPredicates())
|
>>> sorted(t['name'] for t in xrf.getPredicates())
|
||||||
[u'depends', u'knows', u'ownedby', u'provides', u'requires', u'standard']
|
[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 = xrf.getObjectById('5')
|
||||||
>>> obj2['id'], obj2['name']
|
>>> obj2['id'], obj2['name']
|
||||||
('5', u'query')
|
('5', u'note')
|
||||||
>>> textdoc = xrf.getObjectByName(u'textdocument')
|
>>> textdoc = xrf.getObjectByName(u'textdocument')
|
||||||
>>> textdoc['id'], textdoc['name']
|
>>> textdoc['id'], textdoc['name']
|
||||||
('11', u'textdocument')
|
('9', u'textdocument')
|
||||||
|
|
||||||
All methods that retrieve one object also returns its children and parents:
|
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'
|
u'hasType'
|
||||||
>>> sorted(c['name'] for c in ch[0]['objects'])
|
>>> sorted(c['name'] for c in ch[0]['objects'])
|
||||||
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
|
[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']
|
>>> pa = defaultPred['parents']
|
||||||
>>> len(pa)
|
>>> len(pa)
|
||||||
|
@ -113,7 +113,7 @@ We can also retrieve children and parents explicitely:
|
||||||
u'hasType'
|
u'hasType'
|
||||||
>>> sorted(c['name'] for c in ch[0]['objects'])
|
>>> sorted(c['name'] for c in ch[0]['objects'])
|
||||||
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
|
[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')
|
>>> pa = xrf.getParents('7')
|
||||||
>>> len(pa)
|
>>> len(pa)
|
||||||
|
@ -172,14 +172,14 @@ Updating the concept map
|
||||||
|
|
||||||
>>> topicId = xrf.getObjectByName('topic')['id']
|
>>> topicId = xrf.getObjectByName('topic')['id']
|
||||||
>>> xrf.createConcept(topicId, u'zope2', u'Zope 2')
|
>>> 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'}
|
'name': u'zope2'}
|
||||||
|
|
||||||
The name of the concept is checked by a name chooser; if the corresponding
|
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.
|
parameter is empty, the name will be generated from the title.
|
||||||
|
|
||||||
>>> xrf.createConcept(topicId, u'', u'Python')
|
>>> 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'}
|
'name': u'python'}
|
||||||
|
|
||||||
If we try to deassign a ``hasType`` relation nothing will happen; a
|
If we try to deassign a ``hasType`` relation nothing will happen; a
|
||||||
|
|
Loading…
Add table
Reference in a new issue