view data/plugin/macro/CollectLists.py @ 654:8c828d7b6cd4 tip

PDFList: check for pyPdf.utils.PdfReadError
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Wed, 07 Dec 2016 14:27:25 +0100
parents 127da830be6c
children
line wrap: on
line source
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - macro to collect data from definition lists from pages
    into a databrowser widget table

    <<CollectLists>>
    will create a on default a table row for every subpage of the current page
    and a table column for each key and fills the cells with data from each
    key value pair of a definition list. You can change the search_expression
    keyword to any regex you want. All attributes to the macro are optional.
    You can give as an attribute to the macro the name of the template page
    the definition list page depends on.
    You can also list the desired keys with the attribute column_heading.
    In the latter case the table colunms will have the same order as the keys
    listed.
    The macro can finally also use the definition list from another pagename.
    By setting a parser one can use a different parser than moinmoin's default
    like for example wikimarkup.
    With the attribute filter_selection you can use the filter method of the
    databrowser widget (this needs javascript enabled).
    By using a different filter_column_value than the default empty one,
    eg. name:: Cohen you get only rows shown where that name was found.
    If you want to rename your column titles later on that can be done
    by adding the dictionary keyword pointing on a page with aliases
    for your labels as definition list.
    With sort_column_name given as list you can sort the table by
    multiple columns. And sort_reverse=True makes it reverse.
    By using the keyword transpose the table is shown transposed.
    Until transpose is not part of the databrowser widget itselfs we
    don't support every feature of this macro e.g. filter_selection.

    <<CollectLists(search_expression=regex:title:^Examplepage/)>

    @copyright: 2006 by michael cohen <scudette@users.sourceforge.net> (PageDicts)
    @copyright: 2008-2016 by MoinMoin:ReimarBauer (completly rewritten)
    @license: GNU GPL, see COPYING for details.
"""
import operator, re
from decimal import Decimal, InvalidOperation
from MoinMoin import wikiutil, search
from MoinMoin.Page import Page
from MoinMoin.util.dataset import TupleDataset, Column
from MoinMoin.widget.browser import DataBrowserWidget

Dependencies = ["pages"]

# Using old or new dict engine depending on version.
try:
    from MoinMoin.wikidicts import Dict

    def get_dict(request, dict_source):
        return Dict(request, dict_source)
except ImportError:
    def get_dict(request, dict_source):
        return request.dicts[dict_source]

def sort_table(table, cols=0, reverse=False):
    """
    sorts by column number. If a list is given multiple columns become sorted
    """
    for col in reversed(cols):
        table = sorted(table, key=operator.itemgetter(col), reverse=reverse)
    return table

def table2rows(table):
    """
    transforms a data.data table of widget data browser into an array of lists without tuples
    """
    rows = []
    for line in table:
        tmp = []
        for element in line:
            if isinstance(element, tuple):
                try:
                    tmp.append(Decimal(element[1]))
                except InvalidOperation:
                    tmp.append(element[1])
            else:
                try:
                    tmp.append(Decimal(element))
                except InvalidOperation:
                    tmp.append(element)
        rows.append(tmp)
    return rows

def rows2table(rows):
    """
    transforms an array of lists into an array of lists with tuples compatible to widget data browser data.data
    """
    table_data = []
    for line in rows:
        tmp = [(str(element), str(element)) for element in line]
        table_data.append(tmp)
    return table_data


def macro_CollectLists(macro, pagename=unicode,
                      align=("left", "center", "right"),
                      column_heading=u'',
                      template=u'',
                      dictionary=u'',
                      transpose=False,
                      filter_column_value=u'',
                      sort_column_name=u'',
                      sort_reverse=False,
                      parser=u'text_moin_wiki',
                      search_expression=None,
                      filter_selection=u'NeverExistingDefaultFilter'):

    """
    currently we don't support transpose together with filter_selection
    """
    request = macro.request
    formatter = macro.formatter
    _ = request.getText

    try:
        WikiParser = wikiutil.importPlugin(request.cfg, 'parser', parser, function="Parser")
    except wikiutil.PluginMissingError:
        WikiParser = None

    if not pagename:
        pagename = formatter.page.page_name

    if filter_column_value and ':: ' in filter_column_value:
        filter_key, filter_word = filter_column_value.split('::')
        filter_key = filter_key.strip()
        filter_word = filter_word.strip()
    else:
        # Don't filter if syntax was wrong
        filter_column_value = u''

    if search_expression is None:
        search_expression = u'regex:title:^%s/' % pagename

    search_result = search.searchPages(request, search_expression)
    pages = [title.page_name for title in search_result.hits]

    if not pages:
        return _("**Could not find the referenced page: %s**") % search_expression

    # ignore Template pages
    filterfn = request.cfg.cache.page_template_regexact.search
    templates = request.rootpage.getPageList(filter=filterfn)
    subpages = [page for page in pages if page not in [templates, dictionary, pagename]]
    if not subpages:
        return _("Subpage of '%(pagename)s' does not exist or you don't have enough rights.") % {"pagename": pagename}
    subpages.sort()

    # use selection and order
    if column_heading:
        column_heading_keys = [key.strip() for key in column_heading.split(',')]
    # use keys from template page
    elif Page(request, template).exists():
        page = Page(request, template)
        page_dict = get_dict(request, template)
        column_heading_keys = page_dict.keys()
    else:
        # fallback use the keys of the first subpage
        page = Page(request, subpages[0])
        page_dict = get_dict(request, subpages[0])
        column_heading_keys = page_dict.keys()

    # default alias
    alias_dict = {}
    for key in column_heading_keys:
        alias_dict[key] = key
    if Page(request, dictionary).exists():
        alias_dict = get_dict(request, dictionary)
        heading_keys = [alias_dict[key] for key in column_heading_keys]

    data = TupleDataset()
    data.columns = []
    data.columns.extend([Column(pagename.strip('/'), label=pagename.strip('/'), align=align)])
    # may be transpose should be moved into the databrowser widget
    if transpose:
        data.addRow([pagename.strip('/')] + heading_keys)

    for name in subpages:
        page = Page(request, name)
        page_dict = get_dict(request, name)
        if filter_column_value and page_dict.get(filter_key, '') != filter_word:
            continue
        row = []
        keep = False
        for key in column_heading_keys:
            if key in page_dict.keys():
                value = page_dict.get(key, '')
                if WikiParser:
                    # xxx check how our brand new Image class solves this
                    if parser == u'text_moin_wiki':
                        value = value.replace('attachment:', 'attachment:%s/' % name)
                    row.append((wikiutil.renderText(request, WikiParser, value), wikiutil.escape(value, 1)))
                else:
                    row.append((wikiutil.escape(value, 1), wikiutil.escape(value, 1)))
            else:
                row.append('')
        try:
            parent, child = name.split('/', 1)
        except ValueError:
            parent = pagename
            child = name
        link = page.link_to(request, text="%s" % child)
        data.addRow([link] + row)
        if transpose:
            data.columns.extend([Column(link, label=link, align=align)])

    if transpose:
        data.data = map(None, zip(*data.data))
        data.data = data.data[1:]
    else:
        # transpose does not support filter_selection
        if filter_selection:
            filtercols = filter_selection.split(',')
            for key in column_heading_keys:
                key = key.strip()
                if filter_selection != u'NeverExistingDefaultFilter' and key in filtercols:
                    data.columns.append(Column(key, autofilter=(key in filtercols)))
                else:
                    data.columns.extend([Column(key, label=alias_dict[key], align=align)])

    table = DataBrowserWidget(request)
    if sort_column_name:
        try:
            names = sort_column_name.strip().split(',')
            index = [column_heading_keys.index(name.strip()) + 1 for name in names]
        except ValueError:
            index = 0

        rows = table2rows(data.data)
        rows = sort_table(rows, cols=index, reverse=sort_reverse)
        data.data = rows2table(rows)
    table.setData(data)
    html = ''.join(table.format(method='GET'))
    # seems to work together with
    # http://moinmo.in/FeatureRequests/SortableTables?action=AttachFile&do=view&target=common.js.patch
    # html = html.replace('id="dbw.table', 'class="sortable" id="dbw.table')
    return html