comparison MoinMoin/action/LikePages.py @ 0:77665d8e2254

tag of nonpublic@localhost--archive/moin--enterprise--1.5--base-0 (automatically generated log message) imported from: moin--main--1.5--base-0
author Thomas Waldmann <tw-public@gmx.de>
date Thu, 22 Sep 2005 15:09:50 +0000
parents
children ca35d9e6d63e
comparison
equal deleted inserted replaced
-1:000000000000 0:77665d8e2254
1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - LikePages action
4
5 This action generates a list of pages that either start or end
6 with the same word as the current pagename. If only one matching
7 page is found, that page is displayed directly.
8
9 @copyright: (c) 2001 by Richard Jones <richard@bizarsoftware.com.au>
10 @copyright: (c) 2001 by Jürgen Hermann <jh@web.de>
11 @license: GNU GPL, see COPYING for details.
12 """
13
14 import re
15 from MoinMoin import config, wikiutil
16 from MoinMoin.Page import Page
17
18
19 def execute(pagename, request):
20 _ = request.getText
21 start, end, matches = findMatches(pagename, request)
22
23 # Error?
24 if isinstance(matches, (str, unicode)):
25 Page(request, pagename).send_page(request, msg=matches)
26 return
27
28 # No matches
29 if not matches:
30 Page(request, pagename).send_page(request,
31 msg = _('No pages like "%s"!') % (pagename,))
32 return
33
34 # One match - display it
35 if len(matches) == 1:
36 Page(request, matches.keys()[0]).send_page(request,
37 msg = _('Exactly one page like "%s" found, redirecting to page.') % (
38 pagename,))
39 return
40
41 # more than one match, list 'em
42 request.http_headers()
43
44 # This action generate data using the user language
45 request.setContentLanguage(request.lang)
46
47 wikiutil.send_title(request, _('Pages like "%s"') % (pagename),
48 pagename=pagename)
49
50 # Start content - IMPORTANT - without content div, there is no
51 # direction support!
52 request.write(request.formatter.startContent("content"))
53
54 showMatches(pagename, request, start, end, matches)
55
56 # End content and send footer
57 request.write(request.formatter.endContent())
58 wikiutil.send_footer(request, pagename)
59
60
61 def findMatches(pagename, request, s_re=None, e_re=None,):
62 """ Find like pages
63
64 @param pagename: name to match
65 @param request: current reqeust
66 @param s_re: start re for wiki matching
67 @param e_re: end re for wiki matching
68 @rtype: tuple
69 @return: start word, end word, matches dict
70 """
71 # Get full list of pages, with no filtering - very fast. We will
72 # first search for like pages, then filter the results.
73 pages = request.rootpage.getPageList(user='', exists='')
74
75 # Remove current page
76 try:
77 pages.remove(pagename)
78 except ValueError:
79 pass
80
81 # Get matches using wiki way, start and end of word
82 start, end, matches = wikiMatches(pagename, pages, start_re=s_re,
83 end_re=e_re)
84
85 # Get the best 10 close matches using difflib
86 close_matches = {}
87 found = 0
88 for name in closeMatches(pagename, pages):
89 # Skip names already in matches
90 if name in matches:
91 continue
92
93 # Filter deleted pages or pages the user can't read
94 page = Page(request, name)
95 if page.exists() and request.user.may.read(name):
96 close_matches[name] = 8
97 found += 1
98 # Stop after 10 matches
99 if found == 10:
100 break
101
102 # Filter deleted pages or pages the user can't read from
103 # matches. Order is important!
104 for name in matches.keys():
105 page = Page(request, name)
106 if not (page.exists() and request.user.may.read(name)):
107 del matches[name]
108
109 # Finally, merge both dicts
110 matches.update(close_matches)
111
112 return start, end, matches
113
114
115 def wikiMatches(pagename, pages, start_re=None, end_re=None):
116 """ Get pages that starts or ends with same word as this page
117
118 Matches are ranked like this:
119 4 - page is subpage of pagename
120 3 - match both start and end
121 2 - match end
122 1 - match start
123
124 @param pagename: page name to match
125 @param pages: list of page names
126 @param start_re: start word re (compile regex)
127 @param end_re: end word re (compile regex)
128 @rtype: tuple
129 @return: start, end, matches dict
130 """
131 if start_re is None:
132 start_re=re.compile('([%s][%s]+)' % (config.chars_upper,
133 config.chars_lower))
134 if end_re is None:
135 end_re=re.compile('([%s][%s]+)$' % (config.chars_upper,
136 config.chars_lower))
137
138 # If we don't get results with wiki words matching, fall back to
139 # simple first word and last word, using spaces.
140 words = pagename.split()
141 match = start_re.match(pagename)
142 if match:
143 start = match.group(1)
144 else:
145 start = words[0]
146
147 match = end_re.search(pagename)
148 if match:
149 end = match.group(1)
150 else:
151 end = words[-1]
152
153 matches = {}
154 subpage = pagename + '/'
155
156 # Find any matching pages and rank by type of match
157 for name in pages:
158 if name.startswith(subpage):
159 matches[name] = 4
160 else:
161 if name.startswith(start):
162 matches[name] = 1
163 if name.endswith(end):
164 matches[name] = matches.get(name, 0) + 2
165
166 return start, end, matches
167
168
169 def closeMatches(pagename, pages):
170 """ Get close matches using difflib
171
172 Return all matching pages with rank above cutoff value.
173
174 @param pagename: page name to match
175 @param pages: list of page names
176 @rtype: list
177 @return: list of matching pages, sorted by rank
178 """
179 import difflib
180
181 # Match using case insensitive matching
182 # Make mapping from lowerpages to pages - pages might have same name
183 # with different case (although its stupid).
184 lower = {}
185 for name in pages:
186 key = name.lower()
187 if key in lower:
188 lower[key].append(name)
189 else:
190 lower[key] = [name]
191
192 # Get all close matches
193 all_matches = difflib.get_close_matches(pagename.lower(), lower.keys(),
194 len(lower), cutoff=0.6)
195
196 # Replace lower names with original names
197 matches = []
198 for name in all_matches:
199 matches.extend(lower[name])
200
201 return matches
202
203
204 def showMatches(pagename, request, start, end, matches, show_count = True):
205 keys = matches.keys()
206 keys.sort()
207 _showMatchGroup(request, matches, keys, 8, pagename, show_count)
208 _showMatchGroup(request, matches, keys, 4, "%s/..." % pagename, show_count)
209 _showMatchGroup(request, matches, keys, 3, "%s...%s" % (start, end), show_count)
210 _showMatchGroup(request, matches, keys, 1, "%s..." % (start,), show_count)
211 _showMatchGroup(request, matches, keys, 2, "...%s" % (end,), show_count)
212
213
214 def _showMatchGroup(request, matches, keys, match, title, show_count = True):
215 _ = request.getText
216 matchcount = matches.values().count(match)
217
218 if matchcount:
219 if show_count:
220 # Render title line
221 request.write(request.formatter.paragraph(1))
222 request.write(request.formatter.strong(1))
223 request.write(_('%(matchcount)d %(matches)s for "%(title)s"') % {
224 'matchcount': matchcount,
225 'matches': ' ' + (_('match'), _('matches'))[matchcount != 1],
226 'title': wikiutil.escape(title)})
227 request.write(request.formatter.strong(0))
228 request.write(request.formatter.paragraph(0))
229
230 # Render links
231 request.write(request.formatter.bullet_list(1))
232 for key in keys:
233 if matches[key] == match:
234 request.write(request.formatter.listitem(1))
235 request.write(request.formatter.pagelink(1, key))
236 request.write(request.formatter.text(key))
237 request.write(request.formatter.pagelink(0, key))
238 request.write(request.formatter.listitem(0))
239 request.write(request.formatter.bullet_list(0))
240
241