From a761455e358a435521ba6420611a33dfecf617d4 Mon Sep 17 00:00:00 2001 From: helmutm Date: Mon, 15 Feb 2010 11:32:11 +0000 Subject: [PATCH] work in progress: loops site synchronization: export (and subsequent import) of changes git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3730 fd906abe-77d9-0310-91a1-e0d9ade77398 --- external/base.py | 40 ++++++++++++++++++------- external/browser.py | 39 ++++++++++++++----------- external/exportimport.pt | 10 ++++++- system/sync/browser.py | 60 ++++++++++++++++++++++++++++++++++++-- system/sync/configure.zcml | 20 ++++++++++--- system/sync/control.pt | 31 ++++++++++++++++---- 6 files changed, 158 insertions(+), 42 deletions(-) diff --git a/external/base.py b/external/base.py index 52faf7f..1b86c29 100644 --- a/external/base.py +++ b/external/base.py @@ -39,7 +39,7 @@ from cybertools.typology.interfaces import IType 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, IResource +from loops.interfaces import IConceptSchema, IResourceSchema, IResource, IConcept from loops.layout.base import LayoutNode from loops.resource import Document, MediaAsset from loops.setup import SetupManager @@ -113,11 +113,12 @@ class Extractor(Base): self.count += 1 yield element - def extractConcepts(self): + def extractConcepts(self, types=None): for name, obj in self.concepts.items(): if obj.conceptType != self.typeConcept: - self.count += 1 - yield self.getConceptElement(name, obj) + if self.checkTypes(obj, types): + self.count += 1 + yield self.getConceptElement(name, obj) def extractResources(self): for name, obj in self.resources.items(): @@ -161,7 +162,8 @@ class Extractor(Base): yield elem def extractChanges(self, changedSince, parents=None, predicates=None, - includeSubconcepts=False, includeResources=False,): + types=None,): + #includeSubconcepts=False, includeResources=False,): changes = self.getChangeRecords() if not changes: return @@ -173,6 +175,8 @@ class Extractor(Base): obj = util.getObjectForUid(tr.taskId) action = tr.data.get('action') if action in ('add', 'modify'): + if not self.checkTypes(obj, types): + continue if not self.checkParents(obj, parents, predicates): continue if obj not in objects: @@ -188,6 +192,9 @@ class Extractor(Base): if (not self.checkParents(obj, parents, predicates) and not self.checkParents(child, parents, predicates)): continue + if (not self.checkTypes(obj, types) and + not self.checkTypes(child, types)): + continue if action == 'assign': element = self.getAssignmentElement(obj, child, pred) else: @@ -196,7 +203,7 @@ class Extractor(Base): yield element # TODO: include children and resources if corresponding flags are set. - def extractForParents(self, parents, predicates=None, + def extractForParents(self, parents, predicates=None, types=None, includeSubconcepts=False, includeResources=False,): concepts = set(parents) for p in parents: @@ -204,8 +211,9 @@ class Extractor(Base): conceptList = sorted(concepts, key=lambda x: (x.conceptType != self.typeConcept, getName(x))) for c in conceptList: - self.count += 1 - yield self.getConceptElement(getName(c), c) + if self.checkTypes(c, types): + self.count += 1 + yield self.getConceptElement(getName(c), c) for c in conceptList: for r in c.getChildRelations(predicates): if r.predicate != self.typePredicate and r.second in concepts: @@ -216,9 +224,10 @@ class Extractor(Base): for c in conceptList: for obj in c.getResources(predicates): if obj not in resources: - resources.add(obj) - self.count += 1 - yield self.getResourceElement(getName(obj), obj) + if self.checkTypes(obj, types): + resources.add(obj) + self.count += 1 + yield self.getResourceElement(getName(obj), obj) for c in conceptList: for r in c.getResourceRelations(predicates): if r.predicate != self.typePredicate and r.second in resources: @@ -243,12 +252,21 @@ class Extractor(Base): def checkParents(self, obj, parents, predicates): if not parents: return True + if (not IResource.providedBy(obj) and not IConcept.providedBy(obj)): + return False objParents = obj.getParents(predicates) for p in parents: if p in objParents: return True return False + def checkTypes(self, obj, types): + if not types: + return True + if (not IResource.providedBy(obj) and not IConcept.providedBy(obj)): + return False + return obj.getType() in types + def getConceptOrResourceElement(self, name, obj): if IResource.providedBy(obj): return self.getResourceElement(name, obj) diff --git a/external/browser.py b/external/browser.py index 1a6157e..217aea1 100644 --- a/external/browser.py +++ b/external/browser.py @@ -33,6 +33,7 @@ from zope.cachedescriptors.property import Lazy from zope.security.proxy import removeSecurityProxy from zope.traversing.api import getName, getPath +from cybertools.util.date import str2timeStamp from loops.external.base import Loader, Extractor from loops.external.interfaces import IReader, IWriter from loops import util @@ -73,7 +74,7 @@ class ExportImport(object): def export(self): form = self.request.form - parents = predicates = None + parents = predicates = types = None parentIds = form.get('parents') if parentIds: parentIds = [id for id in parentIds.splitlines() if id] @@ -82,6 +83,9 @@ class ExportImport(object): predicateIds = form.get('predicates') if predicateIds: predicates = ([self.conceptManager[id] for id in predicateIds]) + typeIds = form.get('types') + if typeIds: + types = ([self.conceptManager[id] for id in typeIds]) changed = form.get('changed') includeSubconcepts = form.get('include_subconcepts') includeResources = form.get('include_resources') @@ -89,13 +93,13 @@ class ExportImport(object): if changed: changed = self.parseDate(changed) if changed: - elements = extractor.extractChanges(changed, parents, predicates, - includeSubconcepts, includeResources) + elements = extractor.extractChanges(changed, parents, + predicates, types) elif parents: - elements = extractor.extractForParents(parents, predicates, + elements = extractor.extractForParents(parents, predicates, types, includeSubconcepts, includeResources) else: - elements = extractor.extract() + elements = extractor.extract(types) return self.download(elements) def download(self, elements): @@ -107,26 +111,25 @@ class ExportImport(object): self.setDownloadHeader(self.request, text) return text - def parseDate(self, s): - try: - t = time.strptime(s, '%Y-%m-%d %H:%M:%S') - except ValueError: - try: - t = time.strptime(s, '%Y-%m-%d %H:%M') - except ValueError: - t = time.strptime(s, '%Y-%m-%d') - return int(time.mktime(t)) - @Lazy def conceptManager(self): return self.context.getConceptManager() + @Lazy + def typePredicate(self): + return self.conceptManager.getTypePredicate() + + @Lazy + def types(self): + ttype = self.conceptManager['type'] + return [dict(name=getName(p), title=p.title) + for p in ttype.getChildren([self.typePredicate])] + @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])] + for p in ptype.getChildren([self.typePredicate])] def upload(self): data = self.request.get('field.data', None) @@ -148,3 +151,5 @@ class ExportImport(object): response.setHeader('Content-Type', 'text/plain') response.setHeader('Content-Length', len(text)) + def parseDate(self, s): + return str2timeStamp(s) diff --git a/external/exportimport.pt b/external/exportimport.pt index fe49e74..c6258c8 100644 --- a/external/exportimport.pt +++ b/external/exportimport.pt @@ -59,9 +59,17 @@ + + +
+ + -
 
diff --git a/system/sync/browser.py b/system/sync/browser.py index 7027143..97353fd 100644 --- a/system/sync/browser.py +++ b/system/sync/browser.py @@ -22,7 +22,6 @@ view class(es) for import/export. $Id$ """ -from cStringIO import StringIO import os import time from zope import component @@ -31,14 +30,18 @@ from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile from zope.cachedescriptors.property import Lazy from zope.traversing.api import getName, getPath -from loops.organize.tracking.report import RecentChanges +from cybertools.browser.form import FormController +from cybertools.util.date import str2timeStamp, formatTimeStamp +from loops.browser.concept import ConceptView +from loops.external.base import Extractor +from loops.external.interfaces import IWriter from loops import util control_macros = ViewPageTemplateFile('control.pt') -class SyncChanges(RecentChanges): +class SyncChanges(ConceptView): """ View for controlling the transfer of changes from a loops site to another one. """ @@ -47,3 +50,54 @@ class SyncChanges(RecentChanges): def macro(self): return control_macros.macros['main'] + @Lazy + def changed(self): + return (self.request.get('changed_since') or + formatTimeStamp(self.lastSyncTimeStamp)) + + @Lazy + def lastSyncTime(self): + if self.lastSyncTimeStamp is None: + return u'-' + return formatTimeStamp(self.lastSyncTimeStamp) + + @Lazy + def lastSyncTimeStamp(self): + return None + + +class ChangesSave(FormController): + + @Lazy + def baseDirectory(self): + return util.getVarDirectory(self.request) + + @Lazy + def sitePath(self): + return getPath(self.view.virtualTargetObject)[1:].replace('/', '_') + + @Lazy + def exportDirectory(self): + return os.path.join(self.baseDirectory, 'export', self.sitePath) + + @Lazy + def targetView(self): + return self.view.virtualTarget + + def update(self): + typeIds = self.targetView.options('types') + types = [self.view.conceptManager[t] for t in typeIds] + since = self.request.get('changed_since') + changed = since and str2timeStamp(since) or self.targetView.lastSyncTimeStamp + extractor = Extractor(self.view.loopsRoot, self.exportDirectory) + elements = extractor.extractChanges(changed, types) + writer = component.getUtility(IWriter) + f = open(os.path.join(self.exportDirectory, '_changes.dmp'), 'w') + writer.write(elements, f) + f.close() + return True + + +class ChangesSync(ChangesSave): + + pass diff --git a/system/sync/configure.zcml b/system/sync/configure.zcml index f7bfc44..a61a261 100644 --- a/system/sync/configure.zcml +++ b/system/sync/configure.zcml @@ -8,9 +8,21 @@ + name="sync_changes.html" + for="loops.interfaces.IConcept" + class="loops.system.sync.browser.SyncChanges" + permission="zope.ManageContent" /> + + + + diff --git a/system/sync/control.pt b/system/sync/control.pt index abd22d4..c29c542 100644 --- a/system/sync/control.pt +++ b/system/sync/control.pt @@ -1,10 +1,29 @@ - - -
- - + +
+ +
+ +   + (Last synchronization: + ) +
  +
+ + + + + +
+
+ +