From c78bef8ec05d7678fe674b51208ef55410e68f94 Mon Sep 17 00:00:00 2001 From: helmutm Date: Mon, 26 Oct 2009 18:19:27 +0000 Subject: [PATCH] flexible export with selection based on parent concepts git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3604 fd906abe-77d9-0310-91a1-e0d9ade77398 --- external/README.txt | 23 ++++++ external/base.py | 168 +++++++++++++++++++++++++-------------- external/browser.py | 32 +++++++- external/element.py | 13 ++- external/exportimport.pt | 30 +++++++ setup.py | 4 +- 6 files changed, 207 insertions(+), 63 deletions(-) diff --git a/external/README.txt b/external/README.txt index 3ed12f2..1436e3b 100644 --- a/external/README.txt +++ b/external/README.txt @@ -177,6 +177,29 @@ corresponding extractor adapter. viewName=u'mystuff.html')[ annotations(creators=(u'john',))]... +Extracting selected parts of the concept map +-------------------------------------------- + + >>> extractor = Extractor(loopsRoot, os.path.join(dataDirectory, 'export')) + >>> elements = list(extractor.extractForParents([concepts['customer']], + ... includeSubconcepts=True, includeResources=True)) + >>> len(elements) + 10 + + >>> output = StringIO() + >>> writer.write(elements, output) + >>> print output.getvalue() + type(u'customer', u'Customer', options=u'', typeInterface=u'', viewName=u'') + concept(u'cust1', u'Customer 1', u'customer') + concept(u'cust2', u'Customer 2', u'customer') + concept(u'cust3', u'Customer 3', u'customer') + resource(u'd001.txt', u'Doc 001', u'textdocument', contentType='text/restructured') + resource(u'd003.txt', u'Doc 003', u'textdocument', contentType='text/restructured') + resource(u'd002.txt', u'Doc 002', u'textdocument', contentType='text/restructured') + resourceRelation(u'cust1', u'd001.txt', u'standard') + resourceRelation(u'cust1', u'd003.txt', u'standard') + resourceRelation(u'cust3', u'd002.txt', u'standard') + The Export/Import View ====================== diff --git a/external/base.py b/external/base.py index f899b93..a8e2d49 100644 --- a/external/base.py +++ b/external/base.py @@ -40,6 +40,7 @@ from loops.common import adapted from loops.external.interfaces import ILoader, IExtractor, ISubExtractor from loops.external.element import elementTypes from loops.interfaces import IConceptSchema, IResourceSchema +from loops.layout.base import LayoutNode from loops.resource import Document, MediaAsset from loops.setup import SetupManager @@ -109,79 +110,38 @@ class Extractor(Base): yield element def extractConcepts(self): - conceptElement = elementTypes['concept'] - typeConcept = self.typeConcept for name, obj in self.concepts.items(): - if obj.conceptType is None: - raise ValueError('Concept type is None for %s.' % getName(obj)) - if obj.conceptType != typeConcept: - data = self.getObjectData(obj) - tp = getName(obj.conceptType) - element = conceptElement(name, obj.title, tp, **data) - self.provideSubElements(obj, element) - yield element - - def extractConceptsForType(self, typeName): - conceptElement = elementTypes['concept'] - typeObject = self.concepts[typeName] - for obj in typeObject.getChildren([self.typePredicate]): - data = self.getObjectData(obj) - tp = getName(obj.conceptType) - element = conceptElement(name, obj.title, tp, **data) - #self.provideSubElements(obj, element) - yield element + if obj.conceptType != self.typeConcept: + yield self.getConceptElement(name, obj) def extractResources(self): - elementClass = elementTypes['resource'] for name, obj in self.resources.items(): - data = self.getObjectData(obj, IResourceSchema) - tp = getName(obj.resourceType) - if isinstance(obj, Document): # backward compatibility - tp = 'textdocument' - element = elementClass(name, obj.title, tp, **data) - element.processExport(self) - self.provideSubElements(obj, element) - yield element + yield self.getResourceElement(name, obj) def extractChildren(self): - childElement = elementTypes['child'] - typePredicate = self.typePredicate for c in self.concepts.values(): - for r in c.getChildRelations(): - if r.predicate != typePredicate: - args = [getName(r.first), getName(r.second), getName(r.predicate)] - if r.order != 0 or r.relevance != 1.0: - args.append(r.order) - if r.relevance != 1.0: - args.append(r.relevance) - yield childElement(*args) + for r in self.getChildRelations(c): + yield r def extractResourceRelations(self): - elementClass = elementTypes['resourceRelation'] - typePredicate = self.typePredicate for c in self.concepts.values(): - for r in c.getResourceRelations(): - if r.predicate != typePredicate: - args = [getName(r.first), getName(r.second), getName(r.predicate)] - if r.order != 0 or r.relevance != 1.0: - args.append(r.order) - if r.relevance != 1.0: - args.append(r.relevance) - yield elementClass(*args) + for r in self.getResourceRelations(c): + yield r def extractNodes(self, parent=None, path=''): if parent is None: parent = self.views - elementClass = elementTypes['node'] for name, obj in parent.items(): data = {} - for attr in ('description', 'body', 'viewName'): - value = getattr(obj, attr) + for attr in ('description', 'body', 'viewName', 'pageName'): + value = getattr(obj, attr, None) if value: data[attr] = value target = obj.target if target is not None: data['target'] = '/'.join((getName(getParent(target)), getName(target))) + elementClass = (isinstance(obj, LayoutNode) and elementTypes['layoutNode'] + or elementTypes['node']) elem = elementClass(name, obj.title, path, obj.nodeType, **data) self.provideSubElements(obj, elem) yield elem @@ -190,8 +150,106 @@ class Extractor(Base): #self.provideSubElements(obj, elem) yield elem + def extractForParents(self, parents, predicates=None, + includeSubconcepts=False, includeResources=False): + checked = set() + children = set() + resources = set() + for p in parents: + yield self.getConceptElement(getName(p), p) + for elem in self._extractForParents(parents, predicates, + includeSubconcepts, includeResources, + checked, children, resources): + yield elem + allConcepts = checked.union(children) + for c in allConcepts: + for r in c.getChildRelations(predicates): + if r.predicate != self.typePredicate and r.second in children: + yield self.getChildElement(r) + for c in allConcepts: + for r in c.getResourceRelations(predicates): + if r.predicate != self.typePredicate and r.second in resources: + yield self.getResourceRelationElement(r) + + def _extractForParents(self, parents, predicates, + includeSubconcepts, includeResources, + checked, children, resources): + for p in parents: + if p in checked: + continue + checked.add(p) + ch = p.getChildren(predicates) + for obj in ch: + if obj not in children: + children.add(obj) + yield self.getConceptElement(getName(obj), obj) + if includeSubconcepts: + for elem in self._extractForParents(ch, predicates, + includeSubconcepts, includeResources, + checked, children, resources): + yield elem + if includeResources: + for obj in p.getResources(predicates): + if obj not in resources: + resources.add(obj) + yield self.getResourceElement(getName(obj), obj) + # helper methods + def getConceptElement(self, name, obj): + if obj.conceptType is None: + raise ValueError('Concept type is None for %s.' % getName(obj)) + data = self.getObjectData(obj) + type = obj.conceptType + if type == self.typeConcept: + element = elementTypes['type'](getName(obj), obj.title, **data) + else: + tp = getName(type) + element = elementTypes['concept'](name, obj.title, tp, **data) + self.provideSubElements(obj, element) + return element + + def getResourceElement(self, name, obj): + data = self.getObjectData(obj, IResourceSchema) + tp = getName(obj.resourceType) + if isinstance(obj, Document): # backward compatibility + tp = 'textdocument' + element = elementTypes['resource'](name, obj.title, tp, **data) + element.processExport(self) + self.provideSubElements(obj, element) + return element + + def provideSubElements(self, obj, element): + for name, extractor in component.getAdapters((obj,), ISubExtractor): + for sub in extractor.extract(): + element.add(sub) + + def getChildRelations(self, c, predicates=None): + for r in c.getChildRelations(predicates): + if r.predicate != self.typePredicate: + yield self.getChildElement(r) + + def getChildElement(self, r): + args = [getName(r.first), getName(r.second), getName(r.predicate)] + if r.order != 0 or r.relevance != 1.0: + args.append(r.order) + if r.relevance != 1.0: + args.append(r.relevance) + return elementTypes['child'](*args) + + def getResourceRelations(self, c, predicates=None): + for r in c.getResourceRelations(predicates): + if r.predicate != self.typePredicate: + yield self.getResourceRelationElement(r) + + def getResourceRelationElement(self, r): + args = [getName(r.first), getName(r.second), getName(r.predicate)] + if r.order != 0 or r.relevance != 1.0: + args.append(r.order) + if r.relevance != 1.0: + args.append(r.relevance) + return elementTypes['resourceRelation'](*args) + def getObjectData(self, obj, defaultInterface=IConceptSchema): aObj = adapted(obj) schemaFactory = component.getAdapter(aObj, ISchemaFactory) @@ -210,9 +268,3 @@ class Extractor(Base): if not data['description']: del data['description'] return data - - def provideSubElements(self, obj, element): - for name, extractor in component.getAdapters((obj,), ISubExtractor): - for sub in extractor.extract(): - element.add(sub) - diff --git a/external/browser.py b/external/browser.py index 0a5bd39..6f3a5ba 100644 --- a/external/browser.py +++ b/external/browser.py @@ -30,7 +30,7 @@ from zope.app import zapi from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile from zope.cachedescriptors.property import Lazy from zope.security.proxy import removeSecurityProxy -from zope.traversing.api import getPath +from zope.traversing.api import getName, getPath from loops.external.base import Loader, Extractor from loops.external.interfaces import IReader, IWriter @@ -73,7 +73,11 @@ class ExportImport(object): def export(self): f = StringIO() extractor = Extractor(self.context, self.resourceExportDirectory) - elements = extractor.extract() + parentIds = self.request.form.get('parents') + if parentIds: + elements = self.extractForParents(extractor, parentIds) + else: + elements = extractor.extract() writer = component.getUtility(IWriter) writer.write(elements, f) text = f.getvalue() @@ -81,6 +85,30 @@ class ExportImport(object): self.setDownloadHeader(self.request, text) return text + def extractForParents(self, extractor, parentIds): + form = self.request.form + parentIds = [id for id in parentIds.splitlines() if id] + parents = [self.conceptManager.get(id) for id in parentIds] + parents = [p for p in parents if p is not None] + predicateIds = form.get('predicates') + predicates = (predicateIds and [self.conceptManager[id] + for id in parentIds] or None) + includeSubconcepts = form.get('include_subconcepts') + includeResources = form.get('include_resources') + return extractor.extractForParents(parents, predicates, + includeSubconcepts, includeResources) + + @Lazy + def conceptManager(self): + return self.context.getConceptManager() + + @Lazy + def predicates(self): + ptype = self.conceptManager['predicate'] + hasType = self.conceptManager['hasType'] + return [dict(name=getName(p), title=p.title) + for p in ptype.getChildren([hasType])] + def upload(self): data = self.request.get('field.data', None) resourceImportDirectory = (self.request.get('resourceImportDirectory', None) diff --git a/external/element.py b/external/element.py index 3856b7d..da59dd3 100644 --- a/external/element.py +++ b/external/element.py @@ -37,6 +37,8 @@ from loops.common import adapted from loops.interfaces import IConceptSchema from loops.external.interfaces import IElement from loops.i18n.common import I18NValue +from loops.layout.base import LayoutNode +from loops.view import Node class Element(dict): @@ -192,6 +194,7 @@ class NodeElement(Element): elementType = 'node' posArgs = ('name', 'title', 'path', 'type') + factory = Node def __init__(self, *args, **kw): for idx, arg in enumerate(args): @@ -205,13 +208,20 @@ class NodeElement(Element): #target = self.pop('target', None) kw = dict((k, v) for k, v in self.items() if k not in self.posArgs) - node = loader.addNode(self['name'], self['title'], cont, type, **kw) + node = loader.addNode(self['name'], self['title'], cont, type, + factory=self.factory, **kw) #if target is not None: # targetObject = traverse(loader.context, target, None) # node.target = targetObject #self.object = node +class LayoutNodeElement(NodeElement): + + elementType = 'layoutNode' + factory = LayoutNode + + # element registry elementTypes = dict( @@ -221,6 +231,7 @@ elementTypes = dict( resource=ResourceElement, resourceRelation=ResourceRelationElement, node=NodeElement, + layoutNode=LayoutNodeElement, I18NValue=I18NValue, ) diff --git a/external/exportimport.pt b/external/exportimport.pt index 12ac0bb..9399969 100644 --- a/external/exportimport.pt +++ b/external/exportimport.pt @@ -24,6 +24,36 @@
 

Export Site

 
+
+ + + + + + +
+
+ +
+
+ +
  +
+ + +
+ + +
+
+
 
diff --git a/setup.py b/setup.py index cab5bf7..a2879f9 100644 --- a/setup.py +++ b/setup.py @@ -197,7 +197,7 @@ class SetupManager(object): (resourceName, conceptName, getName(predicate))) def addNode(self, name, title, container=None, nodeType='page', - description=u'', body=u'', target=None, **kw): + description=u'', body=u'', target=None, factory=Node, **kw): if container is None: container = self.views nodeType = 'menu' @@ -209,7 +209,7 @@ class SetupManager(object): self.log("Wrong node type for '%s': '%s' instead of '%s'." % (name, n.nodeType, nodeType)) else: - n = addAndConfigureObject(container, Node, name, title=title, + n = addAndConfigureObject(container, factory, name, title=title, description=description, body=body, nodeType=nodeType, **kw) self.log("Node '%s' ('%s') created." % (name, title))