'Constructor - extracts all unique records, generates a
pivot table from them with the given dimensions and
computes the aggregated values for each cell.
@param resource: the S3Resource
@param rows: field selector for the rows dimension
@param cols: field selector for the columns dimension
@param facts: list of S3PivotTableFacts to compute
@param strict: filter out dimension values which don\'t match
the resource filter'

def __init__(self, resource, rows, cols, facts, strict=True):

    if ((not rows) and (not cols)):
        raise SyntaxError('No    rows    or    columns    specified    for    pivot    table')
    self.resource = resource
    self.lfields = None
    self.dfields = None
    self.rfields = None
    self.rows = rows
    self.cols = cols
    self.facts = facts
    self.records = None
    '    All    records    in    the    pivot    table    as    a    Storage    like:\n                                                                {\n                                                                    <record_id>:    <Row>\n                                                                }\n                                '
    self.empty = False
    '    Empty-flag    (True    if    no    records    could    be    found)    '
    self.numrows = None
    '    The    number    of    rows    in    the    pivot    table    '
    self.numcols = None
    '    The    number    of    columns    in    the    pivot    table    '
    self.cell = None
    '    Array    of    pivot    table    cells    in    [rows[columns]]-order,    each\n                                                cell    is    a    Storage    like:\n                                                                {\n                                                                    records:    <list_of_record_ids>,\n                                                                    (<fact>,    <method>):    <aggregated_value>,    ...per    layer\n                                                                }\n                                '
    self.row = None
    '    List    of    row    headers,    each    header    is    a    Storage    like:\n                                                                {\n                                                                    value:    <dimension    value>,\n                                                                    records:    <list_of_record_ids>,\n                                                                    (<fact>,    <method>):    <total    value>,    ...per    layer\n                                                                }\n                                '
    self.col = None
    '    List    of    column    headers,    each    header    is    a    Storage    like:\n                                                                {\n                                                                    value:    <dimension    value>,\n                                                                    records:    <list_of_record_ids>,\n                                                                    (<fact>,    <method>):    <total    value>,    ...per    layer\n                                                                }\n                                '
    self.totals = Storage()
    '    The    grand    total    values    for    each    layer,    as    a    Storage    like:\n                                                                {\n                                                                    (<fact>,    <method):    <total    value>,    ...per    layer\n                                                                }\n                                '
    self.values = {}
    tablename = resource.tablename
    fields = current.s3db.get_config(tablename, 'report_fields', [])
    self._get_fields(fields=fields)
    rows = self.rows
    cols = self.cols
    exclude_empty = current.s3db.get_config(tablename, 'report_exclude_empty')
    if (exclude_empty is True):
        query = ((FS(rows) != None) & (FS(cols) != None))
        resource.add_filter(query)
    elif (type(exclude_empty) is tuple):
        for axis in (cols, rows):
            if (axis in exclude_empty):
                resource.add_filter((FS(axis) != None))
    data = resource.select(self.rfields.keys(), limit=None)
    drows = data['rows']
    if drows:
        key = str(resource.table._id)
        records = Storage([(i[key], i) for i in drows])
        gfields = self.gfields
        pkey_colname = gfields[self.pkey]
        rows_colname = gfields[rows]
        cols_colname = gfields[cols]
        if strict:
            rfields = self.rfields
            axes = (rfield for rfield in (rfields[rows], rfields[cols]) if (rfield != None))
            axisfilter = resource.axisfilter(axes)
        else:
            axisfilter = None
        dataframe = []
        extend = dataframe.extend
        expand = self._expand
        for _id in records:
            row = records[_id]
            item = {key: _id}
            if rows_colname:
                item[rows_colname] = row[rows_colname]
            if cols_colname:
                item[cols_colname] = row[cols_colname]
            extend(expand(item, axisfilter=axisfilter))
        self.records = records
        (matrix, rnames, cnames) = self._pivot(dataframe, pkey_colname, rows_colname, cols_colname)
        if cols:
            self.col = [Storage({'value': v}) for v in cnames]
            self.numcols = len(self.col)
        else:
            self.col = [Storage({'value': None})]
            self.numcols = 1
        if rows:
            self.row = [Storage({'value': v}) for v in rnames]
            self.numrows = len(self.row)
        else:
            self.row = [Storage({'value': None})]
            self.numrows = 1
        add_layer = self._add_layer
        for fact in self.facts:
            add_layer(matrix, fact)
    else:
        self.empty = True
