work in progress: loops.classifier - some refactorings
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2073 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
1e0b976929
commit
dd809a07af
8 changed files with 68 additions and 92 deletions
|
@ -27,7 +27,7 @@ configuration):
|
||||||
>>> concepts, resources, views = t.setup()
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
>>> len(concepts), len(resources)
|
>>> len(concepts), len(resources)
|
||||||
(20, 0)
|
(21, 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.
|
||||||
|
@ -84,8 +84,8 @@ and follow the classifier step by step.
|
||||||
>>> infoSet
|
>>> infoSet
|
||||||
{'filename': 'cust_im_contract_webbg_20071015.txt'}
|
{'filename': 'cust_im_contract_webbg_20071015.txt'}
|
||||||
|
|
||||||
>>> analyzer = component.getUtility(IAnalyzer, name=classifier.analyzer)
|
>>> analyzer = component.getAdapter(classifier, name=classifier.analyzer)
|
||||||
>>> statements = analyzer.extractStatements(infoSet, classifier)
|
>>> statements = analyzer.extractStatements(infoSet)
|
||||||
>>> statements
|
>>> statements
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
@ -120,11 +120,10 @@ that may be identified as being candidates for classification.
|
||||||
|
|
||||||
>>> from zope.app.catalog.interfaces import ICatalog
|
>>> from zope.app.catalog.interfaces import ICatalog
|
||||||
>>> cat = component.getUtility(ICatalog)
|
>>> cat = component.getUtility(ICatalog)
|
||||||
>>> #list(cat.searchResults(loops_text='webbg'))
|
|
||||||
|
|
||||||
>>> statements = analyzer.extractStatements(infoSet, classifier)
|
>>> statements = analyzer.extractStatements(infoSet)
|
||||||
>>> len(statements)
|
>>> len(statements)
|
||||||
2
|
3
|
||||||
|
|
||||||
So we are now ready to have the whole stuff run in one call.
|
So we are now ready to have the whole stuff run in one call.
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,8 @@ class Classifier(AdapterBase):
|
||||||
for name in self.extractors.split():
|
for name in self.extractors.split():
|
||||||
extractor = component.getAdapter(adapted(resource), IExtractor, name=name)
|
extractor = component.getAdapter(adapted(resource), IExtractor, name=name)
|
||||||
infoSet.update(extractor.extractInformationSet())
|
infoSet.update(extractor.extractInformationSet())
|
||||||
analyzer = component.getUtility(IAnalyzer, name=self.analyzer)
|
analyzer = component.getAdapter(self, name=self.analyzer)
|
||||||
statements = analyzer.extractStatements(infoSet, self)
|
statements = analyzer.extractStatements(infoSet)
|
||||||
for statement in statements:
|
for statement in statements:
|
||||||
self.assignConcept(statement)
|
self.assignConcept(statement)
|
||||||
|
|
||||||
|
@ -79,8 +79,12 @@ class Extractor(object):
|
||||||
class Analyzer(object):
|
class Analyzer(object):
|
||||||
|
|
||||||
implements(IAnalyzer)
|
implements(IAnalyzer)
|
||||||
|
adapts(IClassifier)
|
||||||
|
|
||||||
def extractStatements(self, informationSet, classifier=None):
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def extractStatements(self, informationSet):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +97,7 @@ class Statement(object):
|
||||||
|
|
||||||
implements(IStatement)
|
implements(IStatement)
|
||||||
|
|
||||||
def __init__(self, subject=None, predicate=None, object=None, relevance=100):
|
def __init__(self, object=None, predicate=None, subject=None, relevance=100):
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.predicate = predicate
|
self.predicate = predicate
|
||||||
self.object = object
|
self.object = object
|
||||||
|
|
|
@ -41,8 +41,8 @@ class IClassifier(Interface):
|
||||||
|
|
||||||
analyzer = schema.TextLine(
|
analyzer = schema.TextLine(
|
||||||
title=_(u'Analyzer'),
|
title=_(u'Analyzer'),
|
||||||
description=_(u'Name of a utility that is able to analyze '
|
description=_(u'Name of an adapter (or a utility?) that is able to '
|
||||||
'the resources assigned to this classifier.'),
|
'analyze the resources assigned to this classifier.'),
|
||||||
default=u'',
|
default=u'',
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
|
@ -74,17 +74,13 @@ class IExtractor(Interface):
|
||||||
|
|
||||||
|
|
||||||
class IAnalyzer(Interface):
|
class IAnalyzer(Interface):
|
||||||
""" Utility that is able to analyze an information set and
|
""" Adapter (or utility?) that is able to analyze an information set and
|
||||||
provide a collection of statements about it.
|
provide a collection of statements about it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def extractStatements(informationSet, classifier=None):
|
def extractStatements(informationSet):
|
||||||
""" Return a collection of statements derived from the
|
""" Return a collection of statements derived from the
|
||||||
information set given.
|
information set given.
|
||||||
|
|
||||||
The ``classifier`` argument may be given in order to
|
|
||||||
check the environment of the classifier, e.g. available
|
|
||||||
concepts that may be used as attributes for statements.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ $Id$
|
||||||
|
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.app.catalog.interfaces import ICatalog
|
from zope.app.catalog.interfaces import ICatalog
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
|
|
||||||
from cybertools.organize.interfaces import IPerson
|
from cybertools.organize.interfaces import IPerson
|
||||||
|
@ -45,48 +46,34 @@ class SampleAnalyzer(Analyzer):
|
||||||
resource.
|
resource.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handleCustomer(self, name, classifier):
|
def handleCustomer(self, name):
|
||||||
result = []
|
custTypes = self.getTypes(('institution', 'customer',))
|
||||||
candidates = self.findConcepts(name)
|
for c in self.findConcepts(name):
|
||||||
cm = self.getConceptManager(classifier)
|
if IType(c).typeProvider in custTypes:
|
||||||
custTypes = [c for c in (cm.get('institution'), cm.get('customer'),)
|
yield Statement(c)
|
||||||
if c is not None]
|
|
||||||
for c in candidates:
|
|
||||||
ctype = IType(c)
|
|
||||||
if ctype.typeProvider in custTypes:
|
|
||||||
result.append(Statement(c))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def handleEmployee(self, name, classifier):
|
def handleEmployee(self, name):
|
||||||
result = []
|
for c in self.findConcepts(name):
|
||||||
candidates = self.findConcepts(name)
|
|
||||||
cm = self.getConceptManager(classifier)
|
|
||||||
for c in candidates:
|
|
||||||
ctype = IType(c)
|
|
||||||
if ctype.typeInterface == IPerson:
|
|
||||||
result.append(Statement(c))
|
|
||||||
return result
|
|
||||||
|
|
||||||
def handleOwner(self, name, classifier):
|
|
||||||
result = []
|
|
||||||
candidates = self.findConcepts(name)
|
|
||||||
cm = self.getConceptManager(classifier)
|
|
||||||
for c in candidates:
|
|
||||||
if IPerson.providedBy(adapted(c)):
|
if IPerson.providedBy(adapted(c)):
|
||||||
result.append(Statement(c))
|
yield Statement(c)
|
||||||
return result
|
|
||||||
|
|
||||||
def handleDoctype(self, name, classifier):
|
def handleOwner(self, name):
|
||||||
result = []
|
cm = self.conceptManager
|
||||||
#print 'doctype', name
|
ownedby = cm.get('ownedby')
|
||||||
return result
|
for c in self.findConcepts(name):
|
||||||
|
if IPerson.providedBy(adapted(c)):
|
||||||
|
yield Statement(c, ownedby)
|
||||||
|
|
||||||
|
def handleDoctype(self, name):
|
||||||
|
docTypes = self.getTypes(('doctype',))
|
||||||
|
for c in self.findConcepts(name):
|
||||||
|
if IType(c).typeProvider in docTypes:
|
||||||
|
yield Statement(c)
|
||||||
|
|
||||||
handlers = dict(cust=handleCustomer, emp=handleEmployee)
|
handlers = dict(cust=handleCustomer, emp=handleEmployee)
|
||||||
|
|
||||||
def extractStatements(self, informationSet, classifier=None):
|
def extractStatements(self, informationSet):
|
||||||
result = []
|
result = []
|
||||||
if classifier is None:
|
|
||||||
return result # classifier is needed for getting access to concepts
|
|
||||||
fn = informationSet.get('filename')
|
fn = informationSet.get('filename')
|
||||||
if fn is None:
|
if fn is None:
|
||||||
return result
|
return result
|
||||||
|
@ -95,17 +82,21 @@ class SampleAnalyzer(Analyzer):
|
||||||
ctype = parts.pop(0)
|
ctype = parts.pop(0)
|
||||||
if ctype in self.handlers:
|
if ctype in self.handlers:
|
||||||
name = parts.pop(0)
|
name = parts.pop(0)
|
||||||
result.extend(self.handlers[ctype](self, name, classifier))
|
result.extend(self.handlers[ctype](self, name))
|
||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
result.extend(self.handleDoctype(parts.pop(0), classifier))
|
result.extend(self.handleDoctype(parts.pop(0)))
|
||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
result.extend(self.handleOwner(parts.pop(0), classifier))
|
result.extend(self.handleOwner(parts.pop(0)))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def findConcepts(self, name):
|
def findConcepts(self, name):
|
||||||
cat = component.getUtility(ICatalog)
|
cat = component.getUtility(ICatalog)
|
||||||
return cat.searchResults(loops_text=name)
|
return cat.searchResults(loops_text=name)
|
||||||
|
|
||||||
def getConceptManager(self, obj):
|
@Lazy
|
||||||
return obj.context.getLoopsRoot().getConceptManager()
|
def conceptManager(self):
|
||||||
|
return self.context.context.getConceptManager()
|
||||||
|
|
||||||
|
def getTypes(self, typeNames):
|
||||||
|
cm = self.conceptManager
|
||||||
|
return [c for c in [cm.get(name) for name in typeNames] if c is not None]
|
||||||
|
|
|
@ -23,6 +23,7 @@ from loops.interfaces import IConcept, IIndexAttributes, IExternalFile
|
||||||
from loops.integrator.collection import DirectoryCollectionProvider
|
from loops.integrator.collection import DirectoryCollectionProvider
|
||||||
from loops.integrator.collection import ExternalCollectionAdapter
|
from loops.integrator.collection import ExternalCollectionAdapter
|
||||||
from loops.integrator.interfaces import IExternalCollection, IExternalCollectionProvider
|
from loops.integrator.interfaces import IExternalCollection, IExternalCollectionProvider
|
||||||
|
from loops.organize.setup import SetupManager as OrganizeSetupManager
|
||||||
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
||||||
from loops.knowledge.knowledge import Person
|
from loops.knowledge.knowledge import Person
|
||||||
from loops.knowledge.interfaces import IPerson
|
from loops.knowledge.interfaces import IPerson
|
||||||
|
@ -39,6 +40,7 @@ class TestSite(BaseTestSite):
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
|
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
|
||||||
|
component.provideAdapter(OrganizeSetupManager, name='organize')
|
||||||
concepts, resources, views = self.baseSetup()
|
concepts, resources, views = self.baseSetup()
|
||||||
|
|
||||||
#catalog = component.getUtility(ICatalog)
|
#catalog = component.getUtility(ICatalog)
|
||||||
|
@ -56,7 +58,7 @@ class TestSite(BaseTestSite):
|
||||||
sampleClassifier.extractors = 'filename'
|
sampleClassifier.extractors = 'filename'
|
||||||
sampleClassifier.analyzer = 'sample'
|
sampleClassifier.analyzer = 'sample'
|
||||||
component.provideAdapter(FilenameExtractor, name='filename')
|
component.provideAdapter(FilenameExtractor, name='filename')
|
||||||
component.provideUtility(SampleAnalyzer(), IAnalyzer, name='sample')
|
component.provideAdapter(SampleAnalyzer, name='sample')
|
||||||
|
|
||||||
# external file stuff for providing test files
|
# external file stuff for providing test files
|
||||||
component.provideAdapter(ExternalFileAdapter, provides=IExternalFile)
|
component.provideAdapter(ExternalFileAdapter, provides=IExternalFile)
|
||||||
|
|
20
concept.py
20
concept.py
|
@ -240,26 +240,6 @@ class ConceptManager(BTreeContainer):
|
||||||
|
|
||||||
# adapters and similar components
|
# adapters and similar components
|
||||||
|
|
||||||
class xxxConceptSourceList(object):
|
|
||||||
|
|
||||||
# seems to be obsolete
|
|
||||||
|
|
||||||
implements(schema.interfaces.IIterableSource)
|
|
||||||
|
|
||||||
def __init__(self, context):
|
|
||||||
self.context = context
|
|
||||||
#self.context = removeSecurityProxy(context)
|
|
||||||
root = self.context.getLoopsRoot()
|
|
||||||
self.concepts = root.getConceptManager()
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
for obj in self.concepts.values():
|
|
||||||
yield obj
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.concepts)
|
|
||||||
|
|
||||||
|
|
||||||
class ConceptTypeSourceList(object):
|
class ConceptTypeSourceList(object):
|
||||||
|
|
||||||
implements(schema.interfaces.IIterableSource)
|
implements(schema.interfaces.IIterableSource)
|
||||||
|
|
|
@ -36,9 +36,13 @@ class SetupManager(BaseSetupManager):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
concepts = self.context.getConceptManager()
|
concepts = self.context.getConceptManager()
|
||||||
type = concepts.getTypeConcept()
|
type = concepts.getTypeConcept()
|
||||||
|
predicate = concepts['predicate']
|
||||||
|
|
||||||
person = self.addObject(concepts, Concept, 'person', title=u'Person',
|
person = self.addObject(concepts, Concept, 'person', title=u'Person',
|
||||||
conceptType=type)
|
conceptType=type)
|
||||||
personTypeAdapter = ITypeConcept(person)
|
aPerson = ITypeConcept(person)
|
||||||
if not personTypeAdapter.typeInterface: # only set if not set yet
|
if not aPerson.typeInterface: # allow overriding by other packages
|
||||||
personTypeAdapter.typeInterface = IPerson
|
aPerson.typeInterface = IPerson
|
||||||
|
|
||||||
|
owner = self.addObject(concepts, Concept, 'ownedby', title=u'owned by',
|
||||||
|
conceptType=predicate)
|
||||||
|
|
|
@ -43,22 +43,22 @@ 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'person', u'predicate', u'query',
|
[u'domain', u'file', u'hasType', u'note', u'ownedby', u'person',
|
||||||
u'standard', u'textdocument', u'type']
|
u'predicate', u'query', u'standard', u'textdocument', u'type']
|
||||||
|
|
||||||
Now let's add a few more concepts:
|
Now let's add a few more concepts:
|
||||||
|
|
||||||
>>> topic = concepts[u'topic'] = Concept(u'Topic')
|
>>> topic = concepts[u'topic'] = Concept(u'Topic')
|
||||||
>>> intIds.register(topic)
|
>>> intIds.register(topic)
|
||||||
10
|
11
|
||||||
>>> zope = concepts[u'zope'] = Concept(u'Zope')
|
>>> zope = concepts[u'zope'] = Concept(u'Zope')
|
||||||
>>> zope.conceptType = topic
|
>>> zope.conceptType = topic
|
||||||
>>> intIds.register(zope)
|
>>> intIds.register(zope)
|
||||||
11
|
12
|
||||||
>>> zope3 = concepts[u'zope3'] = Concept(u'Zope 3')
|
>>> zope3 = concepts[u'zope3'] = Concept(u'Zope 3')
|
||||||
>>> zope3.conceptType = topic
|
>>> zope3.conceptType = topic
|
||||||
>>> intIds.register(zope3)
|
>>> intIds.register(zope3)
|
||||||
12
|
13
|
||||||
|
|
||||||
Navigation typically starts at a start object, which by default ist the
|
Navigation typically starts at a start object, which by default ist the
|
||||||
domain concept (if present, otherwise the top-level type concept):
|
domain concept (if present, otherwise the top-level type concept):
|
||||||
|
@ -86,13 +86,13 @@ There are a few standard objects we can retrieve directly:
|
||||||
|
|
||||||
In addition we can get a list of all types and all predicates available;
|
In addition we can get a list of all types and all predicates available;
|
||||||
note that the 'hasType' predicate is not shown as it should not be
|
note that the 'hasType' predicate is not shown as it should not be
|
||||||
applied in a direct assignment.
|
applied in an explicit assignment.
|
||||||
|
|
||||||
>>> sorted(t['name'] for t in xrf.getConceptTypes())
|
>>> sorted(t['name'] for t in xrf.getConceptTypes())
|
||||||
[u'domain', u'file', u'note', u'person', u'predicate', u'query',
|
[u'domain', u'file', u'note', u'person', u'predicate', u'query',
|
||||||
u'textdocument', u'type']
|
u'textdocument', u'type']
|
||||||
>>> sorted(t['name'] for t in xrf.getPredicates())
|
>>> sorted(t['name'] for t in xrf.getPredicates())
|
||||||
[u'standard']
|
[u'ownedby', u'standard']
|
||||||
|
|
||||||
We can also retrieve a certain object by its id or its name:
|
We can also retrieve a certain object by its id or its name:
|
||||||
|
|
||||||
|
@ -184,14 +184,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': '10', 'id': '16',
|
{'description': u'', 'title': u'Zope 2', 'type': '11', 'id': '17',
|
||||||
'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': '10', 'id': '17',
|
{'description': u'', 'title': u'Python', 'type': '11', 'id': '18',
|
||||||
'name': u'python'}
|
'name': u'python'}
|
||||||
|
|
||||||
Changing the attributes of a concept
|
Changing the attributes of a concept
|
||||||
|
|
Loading…
Add table
Reference in a new issue