1 # -*- coding: iso-8859-1 -*-
3 MoinMoin - macro to collect data from definition lists on pages
4 into a data browser widget table
6 <<DictColumns(search_term=regex:title:^Examplepage/)>
8 @copyright: 2006 by michael cohen <scudette@users.sourceforge.net> (PageDicts)
10 @license: GNU GPL, see COPYING for details.
14 from collections import defaultdict
15 from MoinMoin import wikiutil, search
16 from MoinMoin.Page import Page
17 from MoinMoin.action.SlideShow import SlidePage
18 from MoinMoin.util.dataset import TupleDataset, Column
19 from MoinMoin.widget.browser import DataBrowserWidget
20 from MoinMoin.datastruct.backends.wiki_dicts import WikiDicts
22 Dependencies = ["pages"]
27 converts a string of comma separated values into a list
28 @param csv: string of comma separated values
31 csv_list = csv.split(',')
32 return [variable.strip() for variable in csv_list if variable.strip()]
34 def _name2index(all_names, selected_names):
36 converts names to the index
37 @param all_names: all available names
38 @param selected_names: names to lookup index position of all_names
39 @return: list of indices
43 index = [all_names.index(name) for name in selected_names]
49 class DictColumns(object):
51 Collects definition list key and values pairs.
52 Each key becomes a column with its values.
54 def __init__(self, macro, pagename=u'', title=u'', names=u'',
55 sort=u'', reverse=u'',
56 hide=u'', filter_name=u'NeverExistingDefaultFilter',
57 filter_value=u'', template_page=u'', alias_page=u'',
58 parser=u'text_moin_wiki', markup="definition list",
59 search_term=None, comments=False, enumeration=False):
61 self.formatter = macro.formatter
62 self.request = macro.request
63 self.pagename = pagename
65 self.pagename = macro.formatter.page.page_name
66 self.request.page = Page(self.request, self.pagename)
69 self.title = self.pagename
72 self.reverse = reverse
74 self.filter_name = filter_name
75 self.filter_value = filter_value
76 self.filter_key, self.filter_word = (u"", u"")
77 self.comments = comments
78 self.enumeration = enumeration
79 regex = re.compile(ur'(?P<key>\w*)=(?P<value>.*)', re.UNICODE)
81 self.filter_key, self.filter_word = regex.search(filter_value).groups()
82 except AttributeError:
83 # Don't filter if syntax was wrong
84 self.filter_value = u""
85 self.template_page = template_page
86 self.alias_page = alias_page
88 self.wiki_parser = wikiutil.importPlugin(
89 self.request.cfg, "parser",
90 parser, function="Parser")
91 except wikiutil.PluginMissingError:
92 self.wiki_parser = None
93 self.search_term = search_term
95 if search_term is None:
96 self.search_term = u'regex:title:^%s/' % self.pagename
98 def get_dict(self, dict_source):
100 gets the dictionary dependent of the markup
101 @param dict_source: pagename to read dict data from
103 if self.markup in ("definition list", "dl"):
104 return self.request.dicts[dict_source]
105 elif self.markup in ("multiline definition list", "mdl"):
106 return self.parse_multiline_dict(dict_source)
107 elif self.markup in ("title", "t"):
108 return self.parse_title(dict_source)
110 def parse_multiline_dict(self, dict_source):
112 creates a dictionary based on a definition list with multiple entries of the same key.
113 The type of the value is a list
119 @param dict_source: pagename to read dict data from
121 body = Page(self.request, dict_source).get_raw_body()
122 ddict = defaultdict(list)
124 for match in WikiDicts._dict_page_parse_regex.finditer(body):
125 key, value = match.groups()
126 ddict[key].append(value)
129 def parse_title(self, dict_source):
131 creates a dictionary based on page titles
132 @param dict_source: pagename to read dict data from
134 body = Page(self.request, dict_source).get_raw_body()
135 parser = SlidePage(self.request, dict_source).createSlideParser()
137 for title, bodyStart, bodyEnd in parser.parse(body):
138 ddict[title] = body[bodyStart:bodyEnd].strip()
141 def get_page_list(self):
143 selects the pages dependent on a search term,
144 without listing of template, dictionary pages and
145 the pagename itselfs.
147 request = self.request
148 search_term = self.search_term
149 search_result = search.searchPages(request, search_term)
150 pages = [title.page_name for title in search_result.hits]
154 filterfn = request.cfg.cache.page_template_regexact.search
155 template_pages = request.rootpage.getPageList(filter=filterfn)
156 excluded_pages = template_pages + [self.alias_page, self.pagename]
157 selected_pages = [page for page in pages if page not in excluded_pages]
158 selected_pages.sort()
159 return selected_pages
161 def get_names(self, selected_pages):
163 selects which column names should be used
164 @param selected_pages: list of page names
165 @return: list of names
167 request = self.request
168 # use selection and order
171 # use keys from template page, no order
172 elif Page(request, self.template_page).exists():
173 page_dict = self.get_dict(self.template_page)
174 names = page_dict.keys()
176 # fallback use the keys used on selected pages
178 for page_name in selected_pages:
179 page_dict = self.get_dict(page_name)
180 keys = page_dict.keys()
182 return list(set(names))
184 def dataset(self, names, selected_pages):
186 Sets the data for the data browser widget
187 @param names: column names
188 @param selected_pages: pages to read key value pairs from
190 _ = self.request.getText
191 assert isinstance(selected_pages, list)
192 request = self.request
193 hide_columns = self.hide
197 alias_dict[name] = name
198 if Page(request, self.alias_page).exists():
199 alias = self.get_dict(self.alias_page)
201 alias_dict[name] = alias.get(name, name)
203 col = Column(self.title, label=self.title)
204 if self.title in hide_columns:
207 data = TupleDataset()
209 data.columns.extend([col])
211 for page_name in selected_pages:
212 page = Page(request, page_name)
213 page_dict = self.get_dict(page_name)
214 if self.filter_value and page_dict.get(self.filter_key, '') != self.filter_word:
219 if name in page_dict.keys():
220 value = page_dict.get(name, '')
221 if isinstance(value, list) and len(value) > 1:
222 value = ' 1. %s' % '\n 1. '.join(value)
223 elif isinstance(value, list):
227 row.append((wikiutil.renderText(request, self.wiki_parser, value),
228 wikiutil.escape(value, 1)))
234 parent, child = page_name.split('/', 1)
237 link = page.link_to(request, text="%s" % child)
238 data.addRow([link] + row)
241 filtercols = self.filter_name
243 if self.filter_name != u'NeverExistingDefaultFilter' and name in filtercols:
244 col = Column(alias_dict[name], autofilter=(name in filtercols))
245 if name in hide_columns:
247 data.columns.append(col)
249 col = Column(alias_dict[name], label=alias_dict[name])
250 if name in hide_columns:
252 data.columns.extend([col])
254 col = Column("Comment", label=_("Comment:"))
255 data.columns.extend([col])
260 renders output as widget data browser table
262 request = self.request
265 selected_pages = self.get_page_list()
266 if not selected_pages:
268 Please use a more selective search term instead of search_term="%s"\
269 """) % self.search_term
271 names = self.get_names(selected_pages)
273 data = self.dataset(names, selected_pages)
274 table = DataBrowserWidget(request)
276 names.insert(0, "__name__")
277 sort_columns = _name2index(names, self.sort)
278 sort_reverse_columns = _name2index(names, self.reverse) or False
280 table.setData(data, sort_columns, reverse=sort_reverse_columns)
283 for line in data.data:
284 line.insert(0, unicode(idx + 1))
285 data.data[idx] = line
287 col = Column(" ", label=" ")
288 data.columns.insert(0, col)
290 html = ''.join(table.format(method='GET'))
293 def macro_DictColumns(macro, pagename=unicode, title=u'', names=u'', sort=u'', reverse=u'',
294 hide=u'', filter_name=u'NeverExistingDefaultFilter',
295 filter_value=u'', template_page=u'', alias_page=u'',
296 parser=u'text_moin_wiki',
297 markup=("definition list", "title",
298 "multiline definition list",
304 Creates a table by data browser widget from definition lists key value pairs.
305 @param pagename: name of the page
306 @param title: entry in upper left corner of the table
307 @param name: names of columns, key name of definition list (comma separated)
308 @param sort: name of columns to sort by
309 @param reverse: name of columns to reverse sort by
310 @param hide: name of columns to hide
311 @param filter_name: name of columns to filter by autofilter
312 @param filter_value: dict definition for value of column to filter by
313 @param template_page: pagename of the template for setting column names
314 @param alias_page: pagename of the page for setting aliases for column names
315 @param parser: name of the parser used to render markup
316 @param markup: type of markup for separating key value pairs
317 @param search_term: regex used to search for selecting pages
320 # wiki input can be a string with comma separated values.
321 kw["names"] = _csv2list(kw["names"])
322 kw["sort"] = _csv2list(kw["sort"])
323 kw["reverse"] = _csv2list(kw["reverse"])
324 kw["hide"] = _csv2list(kw["hide"])
325 kw["filter_name"] = _csv2list(kw["filter_name"])
326 html = DictColumns(**kw).render()
327 # works together with
328 # http://moinmo.in/FeatureRequests/SortableTables?action=AttachFile&do=view&target=common.js.patch
329 # html = html.replace('id="dbw.table', 'class="sortable" id="dbw.table')