data/plugin/macro/CollectLists.py
author Reimar Bauer <rb.proj AT googlemail DOT com>
Mon, 16 Apr 2012 16:56:57 +0200
changeset 587 1b06afc3a430
parent 504 31e92294799a
permissions -rw-r--r--
merged main
     1 # -*- coding: iso-8859-1 -*-
     2 """
     3     MoinMoin - macro to collect data from definition lists from pages
     4     into a databrowser widget table
     5 
     6     <<CollectLists>>
     7     will create a on default a table row for every subpage of the current page
     8     and a table column for each key and fills the cells with data from each
     9     key value pair of a definition list. You can change the search_expression
    10     keyword to any regex you want. All attributes to the macro are optional.
    11     You can give as an attribute to the macro the name of the template page
    12     the definition list page depends on.
    13     You can also list the desired keys with the attribute column_heading.
    14     In the latter case the table colunms will have the same order as the keys
    15     listed.
    16     The macro can finally also use the definition list from another pagename.
    17     By setting a parser one can use a different parser than moinmoin's default
    18     like for example wikimarkup.
    19     With the attribute filter_selection you can use the filter method of the
    20     databrowser widget (this needs javascript enabled).
    21     By using a different filter_column_value than the default empty one,
    22     eg. name:: Cohen you get only rows shown where that name was found.
    23     If you want to rename your column titles later on that can be done
    24     by adding the dictionary keyword pointing on a page with aliases
    25     for your labels as definition list.
    26     With sort_column_name given as list you can sort the table by
    27     multiple columns. And sort_reverse=True makes it reverse.
    28     By using the keyword transpose the table is shown transposed.
    29     Until transpose is not part of the databrowser widget itselfs we
    30     don't support every feature of this macro e.g. filter_selection.
    31 
    32     <<CollectLists(search_expression=regex:title:^Examplepage/)>
    33 
    34     @copyright: 2006 by michael cohen <scudette@users.sourceforge.net> (PageDicts)
    35     @copyright: 2008-2010 by MoinMoin:ReimarBauer (completly rewritten)
    36     @license: GNU GPL, see COPYING for details.
    37 """
    38 import operator, re
    39 from decimal import Decimal, InvalidOperation
    40 from MoinMoin import wikiutil, search
    41 from MoinMoin.Page import Page
    42 from MoinMoin.util.dataset import TupleDataset, Column
    43 from MoinMoin.widget.browser import DataBrowserWidget
    44 
    45 Dependencies = ["pages"]
    46 
    47 # Using old or new dict engine depending on version.
    48 try:
    49     from MoinMoin.wikidicts import Dict
    50 
    51     def get_dict(request, dict_source):
    52         return Dict(request, dict_source)
    53 except ImportError:
    54     def get_dict(request, dict_source):
    55         return request.dicts[dict_source]
    56 
    57 def sort_table(table, cols=0, reverse=False):
    58     """
    59     sorts by column number. If a list is given multiple columns become sorted
    60     """
    61     for col in reversed(cols):
    62         table = sorted(table, key=operator.itemgetter(col), reverse=reverse)
    63     return table
    64 
    65 def table2rows(table):
    66     """
    67     transforms a data.data table of widget data browser into an array of lists without tuples
    68     """
    69     rows = []
    70     for line in table:
    71         tmp = []
    72         for element in line:
    73             if isinstance(element, tuple):
    74                 try:
    75                     tmp.append(Decimal(element[1]))
    76                 except InvalidOperation:
    77                     tmp.append(element[1])
    78             else:
    79                 try:
    80                     tmp.append(Decimal(element))
    81                 except InvalidOperation:
    82                     tmp.append(element)
    83         rows.append(tmp)
    84     return rows
    85 
    86 def rows2table(rows):
    87     """
    88     transforms an array of lists into an array of lists with tuples compatible to widget data browser data.data
    89     """
    90     table_data = []
    91     for line in rows:
    92         tmp = [(str(element), str(element)) for element in line]
    93         table_data.append(tmp)
    94     return table_data
    95 
    96 
    97 def macro_CollectLists(macro, pagename=unicode,
    98                       align=("left", "center", "right"),
    99                       column_heading=u'',
   100                       template=u'',
   101                       dictionary=u'',
   102                       transpose=False,
   103                       filter_column_value=u'',
   104                       sort_column_name=u'',
   105                       sort_reverse=False,
   106                       parser=u'text_moin_wiki',
   107                       search_expression=None,
   108                       filter_selection=u'NeverExistingDefaultFilter'):
   109 
   110     """
   111     currently we don't support transpose together with filter_selection
   112     """
   113     request = macro.request
   114     formatter = macro.formatter
   115     _ = request.getText
   116 
   117     try:
   118         WikiParser = wikiutil.importPlugin(request.cfg, 'parser', parser, function="Parser")
   119     except wikiutil.PluginMissingError:
   120         WikiParser = None
   121 
   122     if not pagename:
   123         pagename = formatter.page.page_name
   124 
   125     if filter_column_value and ':: ' in filter_column_value:
   126         filter_key, filter_word = filter_column_value.split('::')
   127         filter_key = filter_key.strip()
   128         filter_word = filter_word.strip()
   129     else:
   130         # Don't filter if syntax was wrong
   131         filter_column_value = u''
   132 
   133     if search_expression is None:
   134         search_expression = u'regex:title:^%s/' % pagename
   135 
   136     search_result = search.searchPages(request, search_expression)
   137     pages = [title.page_name for title in search_result.hits]
   138 
   139     if not pages:
   140         return _("**Could not find the referenced page: %s**") % search_expression
   141 
   142     # ignore Template pages
   143     filterfn = request.cfg.cache.page_template_regexact.search
   144     templates = request.rootpage.getPageList(filter=filterfn)
   145     subpages = [page for page in pages if page not in [templates, dictionary, pagename]]
   146     if not subpages:
   147         return _("Subpage of '%(pagename)s' does not exist or you don't have enough rights.") % {"pagename": pagename}
   148     subpages.sort()
   149 
   150     # use selection and order
   151     if column_heading:
   152         column_heading_keys = [key.strip() for key in column_heading.split(',')]
   153     # use keys from template page
   154     elif Page(request, template).exists():
   155         page = Page(request, template)
   156         page_dict = get_dict(request, template)
   157         column_heading_keys = page_dict.keys()
   158     else:
   159         # fallback use the keys of the first subpage
   160         page = Page(request, subpages[0])
   161         page_dict = get_dict(request, subpages[0])
   162         column_heading_keys = page_dict.keys()
   163 
   164     # default alias
   165     alias_dict = {}
   166     for key in column_heading_keys:
   167         alias_dict[key] = key
   168     if Page(request, dictionary).exists():
   169         alias_dict = get_dict(request, dictionary)
   170         heading_keys = [alias_dict[key] for key in column_heading_keys]
   171 
   172     data = TupleDataset()
   173     data.columns = []
   174     data.columns.extend([Column(pagename.strip('/'), label=pagename.strip('/'), align=align)])
   175     # may be transpose should be moved into the databrowser widget
   176     if transpose:
   177         data.addRow([pagename.strip('/')] + heading_keys)
   178 
   179     for name in subpages:
   180         page = Page(request, name)
   181         page_dict = get_dict(request, name)
   182         if filter_column_value and page_dict.get(filter_key, '') != filter_word:
   183             continue
   184         row = []
   185         keep = False
   186         for key in column_heading_keys:
   187             if key in page_dict.keys():
   188                 value = page_dict.get(key, '')
   189                 if WikiParser:
   190                     # xxx check how our brand new Image class solves this
   191                     if parser == u'text_moin_wiki':
   192                         value = value.replace('attachment:', 'attachment:%s/' % name)
   193                     row.append((wikiutil.renderText(request, WikiParser, value), wikiutil.escape(value, 1)))
   194                 else:
   195                     row.append((wikiutil.escape(value, 1), wikiutil.escape(value, 1)))
   196             else:
   197                 row.append('')
   198         try:
   199             parent, child = name.split('/', 1)
   200         except ValueError:
   201             parent = pagename
   202             child = name
   203         link = page.link_to(request, text="%s" % child)
   204         data.addRow([link] + row)
   205         if transpose:
   206             data.columns.extend([Column(link, label=link, align=align)])
   207 
   208     if transpose:
   209         data.data = map(None, zip(*data.data))
   210         data.data = data.data[1:]
   211     else:
   212         # transpose does not support filter_selection
   213         if filter_selection:
   214             filtercols = filter_selection.split(',')
   215             for key in column_heading_keys:
   216                 key = key.strip()
   217                 if filter_selection != u'NeverExistingDefaultFilter' and key in filtercols:
   218                     data.columns.append(Column(key, autofilter=(key in filtercols)))
   219                 else:
   220                     data.columns.extend([Column(key, label=alias_dict[key], align=align)])
   221 
   222     table = DataBrowserWidget(request)
   223     if sort_column_name:
   224         try:
   225             names = sort_column_name.strip().split(',')
   226             index = [column_heading_keys.index(name.strip()) + 1 for name in names]
   227         except ValueError:
   228             index = 0
   229 
   230         rows = table2rows(data.data)
   231         rows = sort_table(rows, cols=index, reverse=sort_reverse)
   232         data.data = rows2table(rows)
   233     table.setData(data)
   234     html = ''.join(table.format(method='GET'))
   235     # seems to work together with
   236     # http://moinmo.in/FeatureRequests/SortableTables?action=AttachFile&do=view&target=common.js.patch
   237     # html = html.replace('id="dbw.table', 'class="sortable" id="dbw.table')
   238     return html
   239