From dd809a07af3154ac7a63922afcb05f0d6fea5970 Mon Sep 17 00:00:00 2001 From: helmutm Date: Fri, 28 Sep 2007 13:06:42 +0000 Subject: [PATCH] work in progress: loops.classifier - some refactorings git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2073 fd906abe-77d9-0310-91a1-e0d9ade77398 --- classifier/README.txt | 11 +++---- classifier/base.py | 12 ++++--- classifier/interfaces.py | 12 +++---- classifier/sample.py | 71 ++++++++++++++++++---------------------- classifier/testsetup.py | 4 ++- concept.py | 20 ----------- organize/setup.py | 12 ++++--- xmlrpc/README.txt | 18 +++++----- 8 files changed, 68 insertions(+), 92 deletions(-) diff --git a/classifier/README.txt b/classifier/README.txt index 6654baa..2407089 100644 --- a/classifier/README.txt +++ b/classifier/README.txt @@ -27,7 +27,7 @@ configuration): >>> concepts, resources, views = t.setup() >>> len(concepts), len(resources) - (20, 0) + (21, 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. @@ -84,8 +84,8 @@ and follow the classifier step by step. >>> infoSet {'filename': 'cust_im_contract_webbg_20071015.txt'} - >>> analyzer = component.getUtility(IAnalyzer, name=classifier.analyzer) - >>> statements = analyzer.extractStatements(infoSet, classifier) + >>> analyzer = component.getAdapter(classifier, name=classifier.analyzer) + >>> statements = analyzer.extractStatements(infoSet) >>> statements [] @@ -120,11 +120,10 @@ that may be identified as being candidates for classification. >>> from zope.app.catalog.interfaces import ICatalog >>> cat = component.getUtility(ICatalog) - >>> #list(cat.searchResults(loops_text='webbg')) - >>> statements = analyzer.extractStatements(infoSet, classifier) + >>> statements = analyzer.extractStatements(infoSet) >>> len(statements) - 2 + 3 So we are now ready to have the whole stuff run in one call. diff --git a/classifier/base.py b/classifier/base.py index 28f933f..d399547 100644 --- a/classifier/base.py +++ b/classifier/base.py @@ -55,8 +55,8 @@ class Classifier(AdapterBase): for name in self.extractors.split(): extractor = component.getAdapter(adapted(resource), IExtractor, name=name) infoSet.update(extractor.extractInformationSet()) - analyzer = component.getUtility(IAnalyzer, name=self.analyzer) - statements = analyzer.extractStatements(infoSet, self) + analyzer = component.getAdapter(self, name=self.analyzer) + statements = analyzer.extractStatements(infoSet) for statement in statements: self.assignConcept(statement) @@ -79,8 +79,12 @@ class Extractor(object): class Analyzer(object): implements(IAnalyzer) + adapts(IClassifier) - def extractStatements(self, informationSet, classifier=None): + def __init__(self, context): + self.context = context + + def extractStatements(self, informationSet): return [] @@ -93,7 +97,7 @@ class Statement(object): 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.predicate = predicate self.object = object diff --git a/classifier/interfaces.py b/classifier/interfaces.py index 252e2a9..d653a9b 100644 --- a/classifier/interfaces.py +++ b/classifier/interfaces.py @@ -41,8 +41,8 @@ class IClassifier(Interface): analyzer = schema.TextLine( title=_(u'Analyzer'), - description=_(u'Name of a utility that is able to analyze ' - 'the resources assigned to this classifier.'), + description=_(u'Name of an adapter (or a utility?) that is able to ' + 'analyze the resources assigned to this classifier.'), default=u'', required=False) @@ -74,17 +74,13 @@ class IExtractor(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. """ - def extractStatements(informationSet, classifier=None): + def extractStatements(informationSet): """ Return a collection of statements derived from the 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. """ diff --git a/classifier/sample.py b/classifier/sample.py index 18b7f33..c1436ef 100644 --- a/classifier/sample.py +++ b/classifier/sample.py @@ -24,6 +24,7 @@ $Id$ from zope import component from zope.app.catalog.interfaces import ICatalog +from zope.cachedescriptors.property import Lazy from zope.component import adapts from cybertools.organize.interfaces import IPerson @@ -45,48 +46,34 @@ class SampleAnalyzer(Analyzer): resource. """ - def handleCustomer(self, name, classifier): - result = [] - candidates = self.findConcepts(name) - cm = self.getConceptManager(classifier) - custTypes = [c for c in (cm.get('institution'), cm.get('customer'),) - if c is not None] - for c in candidates: - ctype = IType(c) - if ctype.typeProvider in custTypes: - result.append(Statement(c)) - return result + def handleCustomer(self, name): + custTypes = self.getTypes(('institution', 'customer',)) + for c in self.findConcepts(name): + if IType(c).typeProvider in custTypes: + yield Statement(c) - def handleEmployee(self, name, classifier): - result = [] - 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: + def handleEmployee(self, name): + for c in self.findConcepts(name): if IPerson.providedBy(adapted(c)): - result.append(Statement(c)) - return result + yield Statement(c) - def handleDoctype(self, name, classifier): - result = [] - #print 'doctype', name - return result + def handleOwner(self, name): + cm = self.conceptManager + ownedby = cm.get('ownedby') + 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) - def extractStatements(self, informationSet, classifier=None): + def extractStatements(self, informationSet): result = [] - if classifier is None: - return result # classifier is needed for getting access to concepts fn = informationSet.get('filename') if fn is None: return result @@ -95,17 +82,21 @@ class SampleAnalyzer(Analyzer): ctype = parts.pop(0) if ctype in self.handlers: name = parts.pop(0) - result.extend(self.handlers[ctype](self, name, classifier)) + result.extend(self.handlers[ctype](self, name)) if len(parts) > 1: - result.extend(self.handleDoctype(parts.pop(0), classifier)) + result.extend(self.handleDoctype(parts.pop(0))) if len(parts) > 1: - result.extend(self.handleOwner(parts.pop(0), classifier)) + result.extend(self.handleOwner(parts.pop(0))) return result def findConcepts(self, name): cat = component.getUtility(ICatalog) return cat.searchResults(loops_text=name) - def getConceptManager(self, obj): - return obj.context.getLoopsRoot().getConceptManager() + @Lazy + 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] diff --git a/classifier/testsetup.py b/classifier/testsetup.py index 6d758b7..a932a82 100644 --- a/classifier/testsetup.py +++ b/classifier/testsetup.py @@ -23,6 +23,7 @@ from loops.interfaces import IConcept, IIndexAttributes, IExternalFile from loops.integrator.collection import DirectoryCollectionProvider from loops.integrator.collection import ExternalCollectionAdapter 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.knowledge import Person from loops.knowledge.interfaces import IPerson @@ -39,6 +40,7 @@ class TestSite(BaseTestSite): def setup(self): component.provideAdapter(KnowledgeSetupManager, name='knowledge') + component.provideAdapter(OrganizeSetupManager, name='organize') concepts, resources, views = self.baseSetup() #catalog = component.getUtility(ICatalog) @@ -56,7 +58,7 @@ class TestSite(BaseTestSite): sampleClassifier.extractors = 'filename' sampleClassifier.analyzer = 'sample' component.provideAdapter(FilenameExtractor, name='filename') - component.provideUtility(SampleAnalyzer(), IAnalyzer, name='sample') + component.provideAdapter(SampleAnalyzer, name='sample') # external file stuff for providing test files component.provideAdapter(ExternalFileAdapter, provides=IExternalFile) diff --git a/concept.py b/concept.py index b214940..0d41b79 100644 --- a/concept.py +++ b/concept.py @@ -240,26 +240,6 @@ class ConceptManager(BTreeContainer): # 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): implements(schema.interfaces.IIterableSource) diff --git a/organize/setup.py b/organize/setup.py index c9ba971..67fcb6f 100644 --- a/organize/setup.py +++ b/organize/setup.py @@ -36,9 +36,13 @@ class SetupManager(BaseSetupManager): def setup(self): concepts = self.context.getConceptManager() type = concepts.getTypeConcept() + predicate = concepts['predicate'] + person = self.addObject(concepts, Concept, 'person', title=u'Person', conceptType=type) - personTypeAdapter = ITypeConcept(person) - if not personTypeAdapter.typeInterface: # only set if not set yet - personTypeAdapter.typeInterface = IPerson - + aPerson = ITypeConcept(person) + if not aPerson.typeInterface: # allow overriding by other packages + aPerson.typeInterface = IPerson + + owner = self.addObject(concepts, Concept, 'ownedby', title=u'owned by', + conceptType=predicate) diff --git a/xmlrpc/README.txt b/xmlrpc/README.txt index a68a8a8..c09ce84 100755 --- a/xmlrpc/README.txt +++ b/xmlrpc/README.txt @@ -43,22 +43,22 @@ ZCML setup): Let's look what setup has provided us with: >>> sorted(concepts) - [u'domain', u'file', u'hasType', u'note', u'person', u'predicate', u'query', - u'standard', u'textdocument', u'type'] + [u'domain', u'file', u'hasType', u'note', u'ownedby', u'person', + u'predicate', u'query', u'standard', u'textdocument', u'type'] Now let's add a few more concepts: >>> topic = concepts[u'topic'] = Concept(u'Topic') >>> intIds.register(topic) - 10 + 11 >>> zope = concepts[u'zope'] = Concept(u'Zope') >>> zope.conceptType = topic >>> intIds.register(zope) - 11 + 12 >>> zope3 = concepts[u'zope3'] = Concept(u'Zope 3') >>> zope3.conceptType = topic >>> intIds.register(zope3) - 12 + 13 Navigation typically starts at a start object, which by default ist the 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; 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()) [u'domain', u'file', u'note', u'person', u'predicate', u'query', u'textdocument', u'type'] >>> 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: @@ -184,14 +184,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': '10', 'id': '16', + {'description': u'', 'title': u'Zope 2', 'type': '11', 'id': '17', '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': '10', 'id': '17', + {'description': u'', 'title': u'Python', 'type': '11', 'id': '18', 'name': u'python'} Changing the attributes of a concept