<commit_before># -*- coding: UTF-8 -*-
# Copyright 2009-2017 <NAME>
# License: BSD (see file COPYING for details)

"""Defines the classes :class:`AbstractTable` and
:class:`VirtualTable`.

"""
from __future__ import print_function
# import six
# str = six.text_type
from builtins import str
# from builtins import object

import logging
logger = logging.getLogger(__name__)

import os
import yaml

from django.db import models
from django.conf import settings

from django.utils.translation import ugettext_lazy as _

from lino.core import actors
from lino.core import actions
from lino.core import fields
from lino.core import signals
from lino.core.tablerequest import TableRequest


class InvalidRequest(Exception):
    pass


if False:  # 20130710

    from lino.utils.config import Configured

    class GridConfig(Configured):

        def __init__(self, report, data, *args, **kw):
            self.report = report
            self.data = data
            self.label_en = data.get('label')
            self.data.update(label=_(self.label_en))
            super(GridConfig, self).__init__(*args, **kw)
            must_save = self.validate()
            if must_save:
                msg = self.save_config()
                #~ msg = self.save_grid_config()
                logger.debug(msg)

        def validate(self):
            """
            Removes unknown columns
            """
            must_save = False
            gc = self.data
            columns = gc['columns']
            col_count = len(columns)
            widths = gc.get('widths', None)
            hiddens = gc.get('hiddens', None)
            if widths is None:
                widths = [None for x in columns]
                gc.update(widths=widths)
            elif col_count != len(widths):
                raise Exception("%d columns, but %d widths" %
                                (col_count, len(widths)))
            if hiddens is None:
                hiddens = [False for x in columns]
                gc.update(hiddens=hiddens)
            elif col_count != len(hiddens):
                raise Exception("%d columns, but %d hiddens" %
                                (col_count, len(hiddens)))

            valid_columns = []
            valid_widths = []
            valid_hiddens = []
            for i, colname in enumerate(gc['columns']):
                f = self.report.get_data_elem(colname)
                if f is None:
                    logger.debug(
                        "Removed unknown column %d (%r). Must save.",
                        i, colname)
                    must_save = True
                else:
                    valid_columns.append(colname)
                    valid_widths.append(widths[i])
                    valid_hiddens.append(hiddens[i])
            gc.update(widths=valid_widths)
            gc.update(hiddens=valid_hiddens)
            gc.update(columns=valid_columns)
            return must_save

        def unused_write_content(self, f):
            self.data.update(label=self.label_en)
            f.write(yaml.dump(self.data))
            self.data.update(label=_(self.label_en))

        def write_content(self, f):
            f.write(yaml.dump(self.data))


class TableHandle(object):
    """
    For every table we create one "handle" per renderer.
    """

    _layouts = None

    def __init__(self, actor):
        self.actor = actor
        # Handle.__init__(self)
        #~ super(TableHandle,self).__init__()

    def __str__(self):
        #~ return str(self.ui.__class__)+str(self.actor) + 'Handle'
        return str(self.actor) + 'Handle'

    def setup_layouts(self):
        if self._layouts is not None:
            return
        self._layouts = [self.list_layout]

    def get_actor_url(self, *args, **kw):
        return settings.SITE.kernel.get_actor_url(self.actor, *args, **kw)

    def submit_elems(self):
        return []

    def get_list_layout(self):
        self.setup_layouts()
        return self._layouts[0]

    def get_columns(self):
        lh = self.get_list_layout()
        #~ print 20110315, layout._main.columns
        return lh.main.columns

    def get_slaves(self):
        return [sl.get_handle() for sl in self.actor._slaves]


class Group(object):

    def __init__(self):
        self.sums = []

    def process_row(self, collector, row):
        collector.append(row)

    #~ def add_to_table(self,table):
        #~ self.table = table
        #~ for col in table.computed_columns.values():


class AbstractTable(actors.Actor):
    """An AbstractTable is the definition of a tabular data view, usually
    displayed in a Grid (but it's up to the user interface to decide
    how to implement this).

    Base class for :class:`Table <lino.core.dbtables.Table>` and
    :class:`VirtualTable <lino.core.tables.VirtualTable>`.

    """

    _handle_class = TableHandle

    hide_zero_rows = False
    """Set this to `True` if you want to remove rows which contain no
    values when rendering this table as plain HTML.  This is ignored
    when rendered as ExtJS.

    """

    column_names = '*'
    """A string that describes the list of columns of this table.

    Lino will automatically create a
    :class:`lino.core.layouts.ColumnsLayout` from this.
    This string must not contain any newline characters because a
    ColumnsLayout's `main` panel descriptor must be horizontal.

    Default value is ``'*'``.  This wildcard character means "all
    columns which have not been named explicitly". It can be combined
    with explicitly specified names. For example::

      column_names = "name owner * date"

    specifies that `name` and `owner` come first, followed by all
    other columns and finally by `date`.

    See also :meth:`setup_column` and :meth:`get_column_names`.

    """

    start_at_bottom = False
    """Set this to `True` if you want your table to *start at the
    bottom*.  Unlike reverse ordering, the rows remain in their
    natural order, but when we open a grid on this table, we want it
    to start on the last page.
    
    First use case are :class:`ml.sales.InvoicesByJournal` and
    :class:`ml.ledger.InvoicesByJournal`.
    But the result is not yet satisfying.
<commit_msg>Updated documentation for AbstractTable.colum_names ticket #1461<commit_after># -*- coding: UTF-8 -*-
# Copyright 2009-2017 <NAME>
# License: BSD (see file COPYING for details)

"""Defines the classes :class:`AbstractTable` and
:class:`VirtualTable`.

"""
from __future__ import print_function
# import six
# str = six.text_type
from builtins import str
# from builtins import object

import logging
logger = logging.getLogger(__name__)

import os
import yaml

from django.db import models
from django.conf import settings

from django.utils.translation import ugettext_lazy as _

from lino.core import actors
from lino.core import actions
from lino.core import fields
from lino.core import signals
from lino.core.tablerequest import TableRequest


class InvalidRequest(Exception):
    pass


if False:  # 20130710

    from lino.utils.config import Configured

    class GridConfig(Configured):

        def __init__(self, report, data, *args, **kw):
            self.report = report
            self.data = data
            self.label_en = data.get('label')
            self.data.update(label=_(self.label_en))
            super(GridConfig, self).__init__(*args, **kw)
            must_save = self.validate()
            if must_save:
                msg = self.save_config()
                #~ msg = self.save_grid_config()
                logger.debug(msg)

        def validate(self):
            """
            Removes unknown columns
            """
            must_save = False
            gc = self.data
            columns = gc['columns']
            col_count = len(columns)
            widths = gc.get('widths', None)
            hiddens = gc.get('hiddens', None)
            if widths is None:
                widths = [None for x in columns]
                gc.update(widths=widths)
            elif col_count != len(widths):
                raise Exception("%d columns, but %d widths" %
                                (col_count, len(widths)))
            if hiddens is None:
                hiddens = [False for x in columns]
                gc.update(hiddens=hiddens)
            elif col_count != len(hiddens):
                raise Exception("%d columns, but %d hiddens" %
                                (col_count, len(hiddens)))

            valid_columns = []
            valid_widths = []
            valid_hiddens = []
            for i, colname in enumerate(gc['columns']):
                f = self.report.get_data_elem(colname)
                if f is None:
                    logger.debug(
                        "Removed unknown column %d (%r). Must save.",
                        i, colname)
                    must_save = True
                else:
                    valid_columns.append(colname)
                    valid_widths.append(widths[i])
                    valid_hiddens.append(hiddens[i])
            gc.update(widths=valid_widths)
            gc.update(hiddens=valid_hiddens)
            gc.update(columns=valid_columns)
            return must_save

        def unused_write_content(self, f):
            self.data.update(label=self.label_en)
            f.write(yaml.dump(self.data))
            self.data.update(label=_(self.label_en))

        def write_content(self, f):
            f.write(yaml.dump(self.data))


class TableHandle(object):
    """
    For every table we create one "handle" per renderer.
    """

    _layouts = None

    def __init__(self, actor):
        self.actor = actor
        # Handle.__init__(self)
        #~ super(TableHandle,self).__init__()

    def __str__(self):
        #~ return str(self.ui.__class__)+str(self.actor) + 'Handle'
        return str(self.actor) + 'Handle'

    def setup_layouts(self):
        if self._layouts is not None:
            return
        self._layouts = [self.list_layout]

    def get_actor_url(self, *args, **kw):
        return settings.SITE.kernel.get_actor_url(self.actor, *args, **kw)

    def submit_elems(self):
        return []

    def get_list_layout(self):
        self.setup_layouts()
        return self._layouts[0]

    def get_columns(self):
        lh = self.get_list_layout()
        #~ print 20110315, layout._main.columns
        return lh.main.columns

    def get_slaves(self):
        return [sl.get_handle() for sl in self.actor._slaves]


class Group(object):

    def __init__(self):
        self.sums = []

    def process_row(self, collector, row):
        collector.append(row)

    #~ def add_to_table(self,table):
        #~ self.table = table
        #~ for col in table.computed_columns.values():


class AbstractTable(actors.Actor):
    """An AbstractTable is the definition of a tabular data view, usually
    displayed in a Grid (but it's up to the user interface to decide
    how to implement this).

    Base class for :class:`Table <lino.core.dbtables.Table>` and
    :class:`VirtualTable <lino.core.tables.VirtualTable>`.

    """

    _handle_class = TableHandle

    hide_zero_rows = False
    """Set this to `True` if you want to remove rows which contain no
    values when rendering this table as plain HTML.  This is ignored
    when rendered as ExtJS.

    """

    column_names = '*'
    """A string that describes the list of columns of this table.

    Lino will automatically create a
    :class:`lino.core.layouts.ColumnsLayout` from this.
    This string must not contain any newline characters because a
    ColumnsLayout's `main` panel descriptor must be horizontal.

    Default value is ``'*'``. Where all columns are included.
    This wildcard character means "all columns which have not been
    named explicitly can be selected by the user and inserted at
    this point". It can be combined with explicitly specified names.

    For example::

      column_names = "name owner * date"

    specifies that `name` and `owner` come first, followed by inserted
    columns and finally by `date`.

    If ``'*'`` is not present in the string only explicitly named
    columns will be available. 

    See also :meth:`setup_column` and :meth:`get_column_names`.

    """

    start_at_bottom = False
    """Set this to `True` if you want your table to *start at the
    bottom*.  Unlike reverse ordering, the rows remain in their
    natural order, but when we open a grid on this table, we want it
    to start on the last page.
    
    First use case are :class:`ml.sales.InvoicesByJournal` and
    :class:`ml.ledger.InvoicesByJournal`.
    But the result is not yet satisfying.

{'fraction_non_alphanumeric': 0.09424290220820189, 'fraction_numerical': 0.004416403785488959, 'mean_word_length': 2.174217772215269, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4076935', 'n_tokens_mistral': 3741, 'n_tokens_neox': 3542, 'n_words': 1084}