view data/plugin/macro/CollectLists.py @ 493:9b22669a0690

CollectLists: improved sorting of decimal values
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Thu, 04 Feb 2010 23:44:49 +0100
parents 000965a6e265
children 1294c1c0ec56
line wrap: on
line source
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - macro to collect data from definition lists from subpages
    into a databrowser widget table

    <<CollectLists>>
    will create a table column for every subpage of the current page and fills in
    the data from each key value pair of a definition list.
    Optional you can give the template page the definition list page depends on
    or the column_heading. In the latter case the order is used.
    Also it can optionally use definition list from another pagename.
    By setting optional a parser one can use e.g. wikimarkup from a different
    parser than moinmoin.
    By filter_selection you can optional use the filter method of the databrowser
    widget (this needs javascript enabled).
    By using a different filter_column_value than the default, eg. name:: Cohen  you
    get only rows shown where that name was found.
    By using the keyword transpose the table is shown transposed. Until transpose is not part
    of the databrowser widget itselfs we don't support filter_selection by transpose=True.
    
    <<CollectLists(search_expression=title:Examplepage/)>

    @copyright: 2006 by michael cohen <scudette@users.sourceforge.net> (PageDicts)
    @copyright: 2008-2010 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.wikidicts import Dict
from MoinMoin.widget.browser import DataBrowserWidget

Dependencies = ["pages"]

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 _("Page '%(new_pagename)s' does not exist or you don't have enough rights.") % {"new_pagename": pagename}

    # 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 = 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 = 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 = 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 = 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