# HG changeset patch # User Alexander Schremmer # Date 1154473962 -7200 # Node ID 397b97122ad9d1efa78660ccc88e998d1620c2f0 # Parent 156d160b1dd9374f573373b3a3b9a3c189282646 General SyncPages refactoring: Fixed option handling again, refined semantics of options, introduced direction option, replaced "localMatch"/"remoteMatch" by "pageMatch". diff -r 156d160b1dd9 -r 397b97122ad9 MoinMoin/action/SyncPages.py --- 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("" % 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 "" % (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 "" @@ -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 + "
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) + "
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): diff -r 156d160b1dd9 -r 397b97122ad9 docs/CHANGES.aschremmer --- a/docs/CHANGES.aschremmer Mon Jul 31 12:01:57 2006 +0200 +++ b/docs/CHANGES.aschremmer Wed Aug 02 01:12:42 2006 +0200 @@ -79,7 +79,9 @@ Week 30: Implemented IWID support, added function to generate random strings. Added support for editing the InterWikiMap in the wiki. Added locking to the PickleTagStore and the MetaDict classes. Added handling of various options and detection of anonymous wikis to the SyncPages action. -Week 31: Load the IWID and the meta dict lazily. +Week 31: Load the IWID and the meta dict lazily. Reworked RemotePage/SyncPage, + fixed option handling again, refined semantics of options, introduced + direction option, replaced "localMatch"/"remoteMatch" by "pageMatch". 2006-07-18: the requested daily entry is missing here, see http://moinmoin.wikiwikiweb.de/GoogleSoc2006/BetterProgress 2006-07-19: the requested daily entry is missing here, see http://moinmoin.wikiwikiweb.de/GoogleSoc2006/BetterProgress