diff MoinMoin/action/SyncPages.py @ 1119:397b97122ad9

General SyncPages refactoring: Fixed option handling again, refined semantics of options, introduced direction option, replaced "localMatch"/"remoteMatch" by "pageMatch".
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Wed, 02 Aug 2006 01:12:42 +0200
parents 92f5835853ae
children 34abcbba032d
line wrap: on
line diff
--- a/MoinMoin/action/SyncPages.py	Mon Jul 31 12:01:57 2006 +0200
+++ b/MoinMoin/action/SyncPages.py	Wed Aug 02 01:12:42 2006 +0200
@@ -27,36 +27,81 @@
 from MoinMoin.Page import Page
 from MoinMoin.wikidicts import Dict, Group
 
+# directions
+UP, DOWN, BOTH = range(3)
+directions_map = {"up": UP, "down": DOWN, "both": BOTH}
 
 class ActionStatus(Exception): pass
 
 class UnsupportedWikiException(Exception): pass
 
 # Move these classes to MoinMoin.wikisync
-class RemotePage(object):
+class SyncPage(object):
     """ This class represents a page in (another) wiki. """
-    def __init__(self, name, revno):
+    def __init__(self, name, local_rev=None, remote_rev=None):
         self.name = name
-        self.revno = revno
+        self.local_rev = local_rev
+        self.remote_rev = remote_rev
+        assert local_rev or remote_rev
 
     def __repr__(self):
         return repr("<Remote Page %r>" % unicode(self))
 
     def __unicode__(self):
-        return u"%s<%i>" % (self.name, self.revno)
+        return u"%s<%r:%r>" % (self.name, self.local_rev, self.remote_rev)
 
     def __lt__(self, other):
         return self.name < other.name
 
+    def __hash__(self):
+        return hash(self.name)
+
     def __eq__(self, other):
-        if not isinstance(other, RemotePage):
+        if not isinstance(other, SyncPage):
             return false
         return self.name == other.name
 
-    def filter(cls, rp_list, regex):
-        return [x for x in rp_list if regex.match(x.name)]
+    def filter(cls, sp_list, func):
+        return [x for x in sp_list if func(x.name)]
     filter = classmethod(filter)
 
+    def merge(cls, local_list, remote_list):
+        # map page names to SyncPage objects :-)
+        d = dict(zip(local_list, local_list))
+        for sp in remote_list:
+            if sp in d:
+                d[sp].remote_rev = sp.remote_rev
+            else:
+                d[sp] = sp
+        return d.keys()
+    merge = classmethod(merge)
+
+    def is_only_local(self):
+        return not self.remote_rev
+
+    def is_only_remote(self):
+        return not self.local_rev
+
+    def is_local_and_remote(self):
+        return self.local_rev and self.remote_rev
+
+    def iter_local_only(cls, sp_list):
+        for x in sp_list:
+            if x.is_only_local():
+                yield x
+    iter_local_only = classmethod(iter_local_only)
+
+    def iter_remote_only(cls, sp_list):
+        for x in sp_list:
+            if x.is_only_remote():
+                yield x
+    iter_remote_only = classmethod(iter_remote_only)
+
+    def iter_local_and_remote(cls, sp_list):
+        for x in sp_list:
+            if x.is_local_and_remote():
+                yield x
+    iter_local_and_remote = classmethod(iter_local_and_remote)
 
 class RemoteWiki(object):
     """ This class should be the base for all implementations of remote wiki
@@ -71,7 +116,7 @@
         return NotImplemented
 
     def getPages(self):
-        """ Returns a list of RemotePage instances. """
+        """ Returns a list of SyncPage instances. """
         return NotImplemented
 
 
@@ -97,7 +142,7 @@
         if not self.is_anonymous and interwikiname != remote_interwikiname:
             raise UnsupportedWikiException(_("The remote wiki uses a different InterWiki name (%(remotename)s)"
                                              " internally than you specified (%(localname)s).") % {
-                "remotename": remote_interwikiname, "localname": interwikiname})
+                "remotename": wikiutil.escape(remote_interwikiname), "localname": wikiutil.escape(interwikiname)})
 
         if self.is_anonymous:
             self.iwid_full = packLine([remote_iwid])
@@ -113,7 +158,7 @@
 
     def getPages(self):
         pages = self.connection.getAllPagesEx({"include_revno": True, "include_deleted": True})
-        return [RemotePage(unicode(name), revno) for name, revno in pages]
+        return [SyncPage(unicode(name), remote_rev=revno) for name, revno in pages]
 
     def __repr__(self):
         return "<MoinRemoteWiki wiki_url=%r valid=%r>" % (self.wiki_url, self.valid)
@@ -128,17 +173,17 @@
         pages = []
         for group_pagename in group_list:
             pages.extend(Group(self.request, group_pagename).members())
-        return [self.createRemotePage(x) for x in pages]
+        return [self.createSyncPage(x) for x in pages]
 
-    def createRemotePage(self, page_name):
-        return RemotePage(page_name, Page(self.request, page_name).get_real_rev())
+    def createSyncPage(self, page_name):
+        return SyncPage(page_name, local_rev=Page(self.request, page_name).get_real_rev())
 
     # Methods implementing the RemoteWiki interface
     def getInterwikiName(self):
         return self.request.cfg.interwikiname
 
     def getPages(self):
-        return [self.createRemotePage(x) for x in self.request.rootpage.getPageList(exists=0)]
+        return [self.createSyncPage(x) for x in self.request.rootpage.getPageList(exists=0)]
 
     def __repr__(self):
         return "<MoinLocalWiki>"
@@ -155,10 +200,10 @@
             "remotePrefix": "",
             "localPrefix": "",
             "remoteWiki": "",
-            "localMatch": None,
-            "remoteMatch": None,
+            "pageMatch": None,
             "pageList": None,
             "groupList": None,
+            "direction": "foo", # is defaulted below
         }
 
         options.update(Dict(self.request, self.pagename).get_dict())
@@ -169,21 +214,25 @@
         if options["groupList"] is not None:
             options["groupList"] = unpackLine(options["groupList"], ",")
 
+        options["direction"] = directions_map.get(options["direction"], BOTH)
+
         return options
 
     def fix_params(self, params):
         """ Does some fixup on the parameters. """
 
-        # merge the pageList case into the remoteMatch case
+        # merge the pageList case into the pageMatch case
         if params["pageList"] is not None:
-            params["localMatch"] = params["remoteMatch"] = u'|'.join([r'^%s$' % re.escape(name)
-                                                                      for name in params["pageList"]])
+            params["pageMatch"] = u'|'.join([r'^%s$' % re.escape(name)
+                                             for name in params["pageList"]])
+            del params["pageList"]
 
-        if params["localMatch"] is not None:
-            params["localMatch"] = re.compile(params["localMatch"], re.U)
-        
-        if params["remoteMatch"] is not None:
-            params["remoteMatch"] = re.compile(params["remoteMatch"], re.U)
+        if params["pageMatch"] is not None:
+            params["pageMatch"] = re.compile(params["pageMatch"], re.U)
+
+        # we do not support matching or listing pages if there is a group of pages
+        if params["groupList"]:
+            params["pageMatch"] = None
 
         return params
 
@@ -196,7 +245,6 @@
 
         params = self.fix_params(self.parse_page())
 
-
         try:
             if not self.request.cfg.interwikiname:
                 raise ActionStatus(_("Please set an interwikiname in your wikiconfig (see HelpOnConfiguration) to be able to use this action."))
@@ -224,32 +272,32 @@
         
         r_pages = remote.getPages()
         l_pages = local.getPages()
-        print "Got %i local, %i remote pages" % (len(l_pages), len(r_pages))
-        if params["localMatch"]:
-            l_pages = RemotePage.filter(l_pages, params["localMatch"])
-        if params["remoteMatch"]:
-            print "Filtering remote pages using regex %r" % params["remoteMatch"].pattern
-            r_pages = RemotePage.filter(r_pages, params["remoteMatch"])
-        print "After filtering: Got %i local, %i remote pages" % (len(l_pages), len(r_pages))
 
         if params["groupList"]:
-            pages_from_groupList = local.getGroupItems(params["groupList"])
-            if not params["localMatch"]:
-                l_pages = pages_from_groupList
-            else:
-                l_pages += pages_from_groupList
+            pages_from_groupList = set(local.getGroupItems(params["groupList"]))
+            r_pages = SyncPage.filter(r_pages, pages_from_groupList.__contains__)
+            l_pages = SyncPage.filter(l_pages, pages_from_groupList.__contains__)
 
-        l_pages = set(l_pages)
-        r_pages = set(r_pages)
+        m_pages = SyncPage.merge(l_pages, r_pages)
+
+        print "Got %i local, %i remote pages, %i merged pages" % (len(l_pages), len(r_pages), len(m_pages))
         
-        # XXX this is not correct if matching is active
-        remote_but_not_local = r_pages - l_pages
-        local_but_not_remote = l_pages - r_pages
+        if params["pageMatch"]:
+            m_pages = SyncPage.filter(m_pages, params["pageMatch"].match)
+        print "After filtering: Got %i merges pages" % (len(m_pages), )
+
+        on_both_sides = list(SyncPage.iter_local_and_remote(m_pages))
+        remote_but_not_local = list(SyncPage.iter_remote_only(m_pages))
+        local_but_not_remote = list(SyncPage.iter_local_only(m_pages))
         
         # some initial test code
         r_new_pages = u", ".join([unicode(x) for x in remote_but_not_local])
         l_new_pages = u", ".join([unicode(x) for x in local_but_not_remote])
-        raise ActionStatus("These pages are in the remote wiki, but not local: " + r_new_pages + "<br>These pages are in the local wiki, but not in the remote one: " + l_new_pages)
+        raise ActionStatus("These pages are in the remote wiki, but not local: " + wikiutil.escape(r_new_pages) + "<br>These pages are in the local wiki, but not in the remote one: " + wikiutil.escape(l_new_pages))
+        #if params["direction"] in (DOWN, BOTH):
+        #    for rp in remote_but_not_local:
+                # XXX add locking, acquire read-lock on rp
+                
 
 
 def execute(pagename, request):